-
Notifications
You must be signed in to change notification settings - Fork 258
Proposal: Make generic types non-classes. #468
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Could you explain how @classmethod
def __getitem__(cls, b):
... Im probably missing something, but based on your description they seem to be the same? Also the first alpha for 3.7 is in two weeks, so while I think it would be great to have this in 3.7, Im concerned about timing. Otherwise, I am all for removing hacks and making things faster, and these changes look like cleaner solutions to the Generics complexity. |
FWIW The feature cut-off for 3.7 is beta 1, not alpha 1. That's not until
the end of January. https://www.python.org/dev/peps/pep-0537/
|
This will not work, special methods, like >>> class C:
... @classmethod
... def __getitem__(cls, item):
... return item
>>> C[int]
This is why |
Ah, right, because the I built your patched Python and am experimenting with it. I like the idea of the changes, but I want to try to test some real world class usages on performance. |
Overall, a big 👍 from me. |
Another +1 -- I'd love to have this in Python 3.7. I agree with @markshannon -- this should probably be a PEP. In addition to the backward compatbility issues, this also adds two language features ( |
Yes, it should be a PEP. Possibly the PEP should only worry about the
specification for __class_getitem__ and __subclass_base__, with the typing
simplifications as motivations. I think the changes to typing.py (even the
backwards incompatibilities) don't need a PEP, but we should update PEP 484
not to promise things that we won't support in 3.7 (and newer versions of
typing.py on PyPI should try to break those things).
|
I have a follow-up idea. What if we made it possible to define generic types without importing # No typing import needed!
def first_and_last(x: list[int]) -> tuple[int, int]:
return x[0], x[-1] I haven't thought about this carefully yet. from typing import TypeVar
T = TypeVar('T')
Namespace = dict[str, T]
def f() -> Namespace[int]: ... I believe that this could be implemented without bringing in the rest of |
@JukkaL :-) You are reading my thoughts. I was thinking about using class MyList(list[T]):
...
lst: MyList[int] # ideally this should work as it works with 'List[T]' I am not sure I will have time to do all this in time for 3.7 beta 1. My plan is like this:
|
I really like the idea of adding |
Yeah, allowing list[int] should also be in a PEP (whether the same one or a
different one I'm not sure, but I suggest a different one since it feels
less straightforward). Also I kind of regret that we had to go through all
the effort of disallowing it first...
|
To me this is a bit similar to I am also leaning now towards two separate PEPs. The first one as outlined above, and second one implementing |
It is two weeks since I posted this idea (PEP 560) on pyton-ideas list, but got no responses (apart from few typos noticed by Eric). |
I think it's neither good nor bad -- you merely posted a PEP when a dozen other PEPs were also being debated (spurred on by the core dev sprint). I would recommend trying again now that things have quieted down, and this time I would post the full text of the PEP to python-ideas -- that's what most people do, and it makes it easier for a certain category of readers to provide comments. I should mention that @JukkaL is quite eager to get this done (since it promises to be a startup performance booster for heavily-annotated modules and possibly also an instantiation speedup). Myself I am +0; I like the perf boosts, but I fear that it's going to be a lot of work, both for typing.py and for mypy, and I worry that not enough people are familiar with the inner workings of typing.py in particular (even if the net result is that typing.py becomes simpler, there's still the need to support the old way for years until Python 3.6's end of life, and the risk of breaking 3rd party code that does anything at runtime with annotations). |
OK, I posted PEP 560 to python-ideas once more.
I don't think anything needs to be changed in mypy now (only if we we go with another PEP allowing |
OK, on second thought I realize this may not require changes to mypy, as how everything's spelled stays the same. Do you really want to allow |
No (mostly because I am too lazy for this). This is rather a small bonus for this proposal, maybe someday someone will really want this, then |
OK then let's leave that out. Maybe for Python 3.8. |
Yes, |
OK, this now has landed as PEP 560 so can be closed. |
It is proposed to add special methods
__subclass_base__
and__class_getitem__
to CPython, these will allow making generics non-classes thus simplifying them and significantly improving their performance.@gvanrossum, the main question now is should this be a PEP?
Motivation:
There are three main points of motivation: performance of
typing
module, metaclass conflicts, and large amount of hacks currently used intyping
.Performance:
The
typing
module is one of the heaviest and slowest modules in stdlib even with all the optimizations made. Mainly this is because subscripted generics are classes. See also #432. The three main ways how the performance will be improved:Creation of generic classes is slow since the
GenericMeta.__new__
is very slow, we will not need it anymore.Very long MROs for generic classes will be twice shorter, they are present because we duplicate the
collections.abc
inheritance chain intyping
.Time of instantiation of generic classes will be improved (this is minor however).
Metaclass conflicts:
All generic types are instances of
GenericMeta
, so if a user uses a custom metaclass, it is hard to make a corresponding class generic. This is in particular hard for library classes, that a user doesn't control. A workaround is to always mix-inGenericMeta
:but this is not always practical or even possible.
Hacks and bugs that will be removed by this proposal:
_generic_new
hack that exists since__init__
is not called on instances with a type differing form the type whose__new__
was called,C[int]().__class__ is C
._next_in_mro
speed hack will be not necessary since subscription will not create new classes.Ugly
sys._getframe
hack, this one is particularly nasty, since it looks like we can't remove it without changes outsidetyping
.Currently generics do "dangerous" things with private ABC caches to fix large memory consumption that grows at least as
O(N**2)
, see Optimize ABC caches #383. This point is also important because I would like to re-implementABCMeta
in C. This will allow to reduce Python start-up time and also start-up times for many programs that extensively use ABCs. My implementation passes all tests excepttest_typing
, because I want to make_abc_cache
etc. read-only, so that one can't do something likeMyABC._abc_cache = "Surprise when updating caches!"
)Problems with sharing attributes between subscripted generics, see Subscripted generic classes should not have independent class variables #392. Current solution already uses
__getattr__
and__setattr__
, but it is still incomplete, and solving this without the current proposal will be hard and will need__getattribute__
._no_slots_copy
hack, where we clean-up the class dictionary on every subscription thus allowing generics with__slots__
.General complexity of
typing
module, the new proposal will not only allow to remove the above mentioned hacks/bugs, but also simplify the implementation, so that it will be easier to maintain.Details of the proposal:
New methods API:
Idea of
__class_getitem__
is very simple, it is an exact analog of__getitem__
with an exception that it is called on a class that defines it, not on its instances, this allows us to avoidGenericMeta.__getitem__
.If an object that is not a class object appears in bases of a class definition, the
__subclass_base__
is searched on it. If found, it is given an original tuple of bases as an argument. If the result of call is notNone
, then it is substituted instead of this object. Otherwise, the base is just removed. This is necessary to avoid inconsistent MRO errors, that are currently prevented by manipulations inGnericMeta.__new__
. After creating the class, original bases are saved in__orig_bases__
(now this is also done by the metaclass).Changes necessary in
typing
module:Key point is instead of
GenericMeta
metaclass, we will haveGenericAlias
class.Generic
will have:__class_getitem__
that will return instances ofGenericAlias
which keep track of the original class and type arguments.__init_subclass__
that will properly initialize the subclasses, and perform necessary bookkeeping.GenericAlias
will have:__getitem__
so that it can be further subscripted thus preserving the current API.__call__
,__getattr__
, and__setattr__
that will simply pass everything to the original class object.__subclass_base__
that will return the original class (orNone
in some special cases).The generic versions of
collections.abc
classes will be simple subclasses like this:(
typeshed
of course will track thatSequence[T_co]
inherits fromIterable[T_co]
etc.)Transition plan:
typing
for Python 3.7 and simplify it by removing backward compatibility hacks.Backwards compatibility and impact on users who don't use
typing
:This proposal will allow to have practically 100% backwards compatibility with current public typing API. Actually the whole idea of introducing two special methods appeared form the desire to preserve backwards compatibility while solving the above listed problems.
The only two exceptions that I see now are that currently
issubclass(List[int], List)
returnsTrue
, with this proposal it will raiseTypeError
. Alsoissubclass(collections.abc.Iterable, typing.Iterable)
will returnFalse
, which is actually good I think, since currently we have a (virtual) inheritance cycle between them.With my implementation, see https://github.com/ilevkivskyi/cpython/pull/2/files, I measured negligible effects (under 1%) for regular (non-generic) classes.
The text was updated successfully, but these errors were encountered: