Runtime Access to Type Parameters
I would like to propose being able to access, at runtime, the type parameters in classmethods and in constructors. I wrote up a draft PEP on this to capture the thinking here: https://github.com/saulshanabrook/peps/blob/master/pep-9999.md It seems easier to add them to classmethods, so even that addition alone would be very welcome! It could be added to Guido's existing draft PR for pep 585, by making a small change in the getattr function (https://github.com/python/cpython/pull/18239/files#diff-a34ae826f869897e56e0...), to first do a `__getattribute__` and check if the result is a descriptor (has a `__get__` attribute), and if so passing the generic class itself in as the class, instead of the origin class. I wrote up a little example of that in the PEP. To make it available in the constructor, I proposed adding a global function `get_parameters` to `typing` which returns the parameters in the constructor. I also provide a little mock implementation of that. Note that work is only for custom classes which inherit from `typing.Generic` not for any built in collections.
I would welcome this as well as it would enhance the expressiveness of generics and probably save people coming from c++ a bit of pain. There also was another idea for implementing this in a more localized way suggested on github along with some further discussion [1]. In my use case, I have a bunch of very similar classes that all require slight changes in some of the functions. I could of course add an implementation in a common base class and add a bunch of methods that I override. However, the changes are spread out and I would have to add a lot of methods that need to be overridden which are used exactly once, which fragments the code and thus hurts readability. I'm currently using an Enum argument to specify which implementation detail to use. However, this changes the return type, thus making type annotations difficult. [1] https://github.com/python/typing/issues/629#issuecomment-829629259
Funnily enough I have also been working on something similar to this for a few months and I didn't even realise this was already a proposed thing but I guess I might show what I had in mind in light of PEP 695 being accepted. I agree that there's an unfortunate lack of exposed methods for getting type parameters at runtime. Full very not completed draft here https://gist.github.com/Gobot1234/f78229cd41beaeab87b24a93b5ba70ff. If Saul you'd want to work together on this or b.v.b that'd be cool. There is a PEP that needs to go through for subscripting functions at runtime separate to this (https://discuss.python.org/t/making-functions-subscriptable-at-runtime/26463).
I am curious how things work now that PEP 695 has been accepted... If there is a way to get generic type params from inside classmethods or init. FWIW I have worked around this in the project where I am using generic classes at runtime (https://egg-smol-python.readthedocs.io/en/latest/reference/egglog-translatio...), by actually at runtime *not* having them be classes but instances that have __getitem__ implemented. Statically, type checkers still think it's a type, but at runtime, it's not.
PEP 695 hasn't changed anything with regards to introspection of specialised classes, it does however provide a building block for this by providing knowledge of generic classes directly to the interpreter
This looks like a useful hack, but the implementation (as shown in Shawn's draft PEP) feels problematic -- you want the notation `SomeClass.some_class_method[various_parameters](arguments)` to invoke `SomeClass.class_method(arguments)` and convey `various_parameters` out of band, without changing the signature of `class_method`. The solution of using contextvars is clever, but I worry that it could be expensive -- that API wasn't designed for such a fine-grained usage. I also feel that it's a bit weird to use a much more general mechanism. Though maybe I'm being distracted by something you used just to get a feel for how the prototype would work? What other ways would there be to pass this information around? Maybe we should introspect the signature of `class_method` looking for a specific dunder-named keyword-only parameter? Or maybe we could wrap the `cls` argument in something that's a proxy except for an extra attribute? It looks like several others have attempted implementations -- what did they use? -- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>
I think the subscriptable functions PEP and this PEP benefit a lot from each other. Maybe so much even that one could try to introduce them in the same PEP. At least in my mind, making functions subscriptable is a logical extension when having generic arguments available at runtime. I didn't think too much about it, but a use case would be a function that uses a generic class only internally (i.e. not as an argument or output). adding it as a generic would enable static checking that the passed parameter actually provides the methods / fields used in the function. ``` def fun[T](some_arg): return T(some_arg).some_method() ``` Basically generic parameters could be passed to the function just the same as normal parameters with `Any` as a default. If there is no dependency on a generic parameter it could be skipped (dunno if that is a sensible optimization). The only difference is how they are used by static tools.
participants (5)
-
b.v.b@gmx.at
-
Benjamin von Berg
-
Guido van Rossum
-
James H-B
-
Saul Shanabrook