[Python-Dev] Comments on PEP 560 (Core support for typing module and generic types) (original) (raw)

Mark Shannon mark at hotpy.org
Mon Nov 20 04:22:16 EST 2017


On 19/11/17 22:36, Ivan Levkivskyi wrote:

On 19 November 2017 at 21:06, Mark Shannon <mark at hotpy.org_ _<mailto:mark at hotpy.org>> wrote:

By far and away the largest change in PEP 560 is the change to the behaviour of object.getitem. This is not mentioned in the PEP at all, but is explicit in the draft implementation. The implementation could implement type._getitem_ instead of changing object._getitem_, but that is still a major change to the language.

Except that there is no such thing as object.getitem. Probably you_ mean PyObjectGetItem (which is just what is done by BINARYSUBSCR opcode).

Yes, I should have taken more time to look at the code. I thought you were implementing object.__getitem__. In general, Python implements its operators as a simple redirection to a special method, with the exception of binary operators which are necessarily more complex.

f(...) -> type(f).call(f, ...) o.a -> type(o).getattribute(o, "a") o[i] -> type(o).getitem(o, i)

Which is why I don't like the additional complexity you are adding to the dispatching. If we really must have __class_getitem__ (and I don't think that we do) then implementing type.__getitem__ is a much less intrusive way to do it.

In fact, I initially implemented type.getitem, but I didn't like it for various reasons.

Could you elaborate?

I don't think that any of the above are changes to the language. These are rather implementation details. The only unusual thing is that while dunders are searched on class, classgetitem is searched on the object (class object in this case) itself. But this is clearly explained in the PEP. In fact, the addition of _mroentries_ makes _classgetitem_ unnecessary.

But how would you implement this: class C(Generic[T]): ... C[int] # This should work

The issue of type-hinting container classes is a tricky one. The definition is defining both the implementation class and the interface type. We want the implementation and interface to be distinct. However, we want to avoid needless repetition.

In the example you gave, C is a class definition that is intended to be used as a generic container. In my mind the cleanest way to do this is with a class decorator. Something like:

@Generic[T] class C: ...

or

@implements(Generic[T]) class C: ...

C would then be a type not a class, as the decorator is free to return a non-class object.

It allows the implementation and interface to be distinct:

@implements(Sequence[T]) class MySeq(list): ...

@implements(Set[Node]) class SmallNodeSet(list): ... # For small sets a list is more efficient than a set.

but avoid repetition for the more common case:

class IntStack(List[int]): ...

Given the power and flexibility of the built-in data structures, defining custom containers is relatively rare. I'm not saying that it should not be considered, but a few minor hurdles are acceptable to keep the rest of the language (including more common uses of type-hints) clean.

The name _mroentries_ suggests that this method is solely related method resolution order, but it is really about providing an instance of type where one is expected. This is analogous to _int_, _float_ and _index_ which provide an int, float and int respectively. This rather suggests (to me at least) the name _type_ instead of _mroentries_

This was already discussed during months, and in particular the name type was not liked by ... you

Ha, you have a better memory than I :) I won't make any more naming suggestions. What I should have said is that the name should reflect what it does, not the initial reason for including it.

https://github.com/python/typing/issues/432#issuecomment-304070379 So I would propose to stop bikesheding this (also Guido seems to like the currently proposed name).

Should isinstance and issubclass call _mroentries_ before raising an error if the second argument is not a class? In other words, if List implements _mroentries_ to return list then should issubclass(x, List) act like issubclass(x, list)? (IMO, it shouldn't) The reasoning behind this decision should be made explicit in the PEP.

I think this is orthogonal to the PEP. There are many situations where a class is expected, and IMO it is clear that all that are not mentioned in the PEP stay unchanged.

Indeed, but you do mention issubclass in the PEP. I think a few extra words of explanation would be helpful.

Cheers, Mark.



More information about the Python-Dev mailing list