-
Notifications
You must be signed in to change notification settings - Fork 257
Make builtins.property
generic
#985
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
See also:
|
(In particular, see erictraut's comments on the second link) |
To address some of @erictraut's feedback about
Yes, this is my main motivation when I suggest adding generic properties support.
This can easily be addressed:
From my work on decorated |
Another big opportunity of generic class A:
foo: int # read- and writable attribute
bar: property[int] # read-only attribute
# instead of
class A:
foo: int
@property
def bar(self) -> int:
... This would be a big win for type stubs, as well as See also: #922 (comment) also: since the get-only case is so common, it'd probably be logical to allow |
It's clear there is something valuable here; there seem to be no lack of other issues/PRs where this has been discussed before, so it's clear there's a need. I hope you all solve the puzzle! IIUC it should be possible to completely remove the special-casing for properties from type checkers, instead deferring completely to the more general descriptor semantics, right? Then we'd still have to debate some finer points about descriptors, but at least the behavior of properties would be fully defined by its definition in typeshed plus descriptor semantics. |
The biggest blocker for that may be the (And the same for |
Yes, with some minor special-case points. Like identifying writable / read-only properties by But, it is specific to While |
Hm, yeah, we'll need to special-case |
CC @erictraut and @rchen152 from pyright and pytype 🙂 |
I played around with this. Here's a variant of the proposed class definition: _G1 = TypeVar("_G1")
_G2 = TypeVar("_G2")
_S1 = TypeVar("_S1")
_S2 = TypeVar("_S2")
class property(Generic[_G1, _S1]):
fget: Callable[[Any], _G1] | None
fset: Callable[[Any, _S1], None] | None
fdel: Callable[[Any], None] | None
@overload
def __new__(
cls,
fget: Callable[[Any], _G2],
fset: None = ...,
fdel: Callable[[Any], None] | None = ...,
doc: str | None = ...,
) -> property[_G2, NoReturn]: ...
@overload
def __new__(
cls,
fget: Callable[[Any], _G2],
fset: Callable[[Any, _S2], None],
fdel: Callable[[Any], None] | None = ...,
doc: str | None = ...,
) -> property[_G2, _S2]: ...
def getter(self, __fget: Callable[[Any], _G2]) -> property[_G2, _S1]: ...
def setter(self, __fset: Callable[[Any, _S2], None]) -> property[_G1, _S2]: ...
def deleter(self, __fdel: Callable[[Any], None]) -> property[_G1, _S1]: ...
def __get__(self, __obj: Any, __type: type | None = ...) -> _G1: ...
def __set__(self, __obj: Any, __value: _S1) -> None: ...
def __delete__(self, __obj: Any) -> None: ... Some observations:
Overall, I'm pretty negative on this change. It would have been nice if |
Thanks everyone for their feedback! Since no one is really interested in this, I am going to close this for now. |
@sobolevn Hmm, what gave you the impression that no one is interested in this? Many are it seems, including at my workplace. |
Hi!
Here's how
property
type is defined right now from a typing perspective:Source: https://github.com/python/typeshed/blob/12b79f64d7241024447eae8ca5d52779cca94ee7/stdlib/builtins.pyi#L947
Note, that it is not generic. But, this is a type that surely can be modeled as a generic type.
Motivation
Why? Because
@property
works with two major scenarios: getting some inner type and setting some extrenal type. This can be represented asproperty[GetType, SetType]
.It might not seem very important to end users, because of how
@property
s are used, but it quite importatnt for type-checkers and type stubs.Here are some problems that we have in
mypy
with properties:@property
is so special right now,mypy
does not support decorated properties: Decorated property not supported mypy#1362@property
with different@setter
type: What to do about setters of a different type than their property? mypy#3004 and Recognize different setter type for properties. mypy#11643Yes, we can work around the fact we cannot simply express
property[GetType, SetType]
, but I don't think that missing this important detail is good for the typing system in the long run.Without this change we cannot treat
@property
as a regular descriptor, which follows the same rules.Reference implementation
Implementing generic
@property
will require several changes.CPython
I guess we would need something similar as we did in https://www.python.org/dev/peps/pep-0585/
Something like adding:
Here: https://github.com/python/cpython/blob/c8749b578324ad4089c8d014d9136bc42b065343/Objects/descrobject.c#L1567-L1568
typeshed
I am sure that this code can be improved, but it does its job as a reference / demo:
Playground: https://mypy-play.net/?mypy=latest&python=3.10&flags=strict%2Cdisallow-subclassing-any%2Cdisallow-untyped-calls%2Cdisallow-untyped-decorators&gist=e7cec4565d416a0981d4a65e619f1be6
It almost works as-is, but
@property.setter
part requires some extra handling from type-checkers.Backwards compatibility
As far as I understand CPython's development process, we can add this new feature to 3.11 only.
I think that using
from __future__ import annotations
should also work for@property
the same way PEP585 defines.Next steps
If others agree with me that
property[GetType, SetType]
is a good thing to have, I can:property.__class_getitem__
to CPython: bpo-46162: makeproperty
generic cpython#30238typeshed
with the typing stubLooking forward to your feedback! 😊
Related issues
Refs:
typing.Property
does not really make much sense, sincepython>=3.9
already supports other builtin generic types).The text was updated successfully, but these errors were encountered: