Property type hints?
Paul Bryan
pbryan at anode.ca
Wed Dec 9 19:06:03 EST 2020
Thanks for the comprehensive response, dn!
I guess I'm influenced by data classes here, where the object's
attribute type hints are represented by class variable annotations.
On Thu, 2020-12-10 at 07:49 +1300, dn via Python-list wrote:
> On 09/12/2020 13:17, Paul Bryan wrote:
> > Would this be a reasonably correct way to annotate a property with
> > a
> > type hint?
> >
> > > > > class Foo:
> > ... bar: int
>
>
> If we build a class with (only) the above two lines, Python's help
> lookup offers the following documentation:
>
> <<<
> Help on Foo in module __main__ object:
>
> class Foo(builtins.object)
> | Data descriptors defined here:
> |
> | __dict__
> | dictionary for instance variables (if defined)
> |
> | __weakref__
> | list of weak references to the object (if defined)
> |
> | ----------------------------------------------------------------
> ------
> | Data and other attributes defined here:
> |
> | __annotations__ = {'bar': <class 'int'>}
> >>>
>
> Note the last line identifying 'bar' as having integer-type.
>
> However, when we continue, by adding a property/lookup-method called
> 'bar'.
>
> > ... @property
> > ... def bar(self):
> > ... return 1
>
>
> ...the help lookup becomes:
>
> <<<
> class Foo(builtins.object)
> | Readonly properties defined here:
> |
> | bar
> |
> | ----------------------------------------------------------------
> ------
> | Data descriptors defined here:
> |
> | __dict__
> | dictionary for instance variables (if defined)
> |
> | __weakref__
> | list of weak references to the object (if defined)
> |
> | ----------------------------------------------------------------
> ------
> | Data and other attributes defined here:
> |
> | __annotations__ = {'bar': <class 'int'>}
> >>>
>
> Note that 'bar' has now been listed as a read-only property.
>
> Further, if we remove the explicit typing (int) of 'bar', the help
> listing doesn't change.
>
>
> <<<
> class Foo(builtins.object)
> | Readonly properties defined here:
> |
> | bar
> |
> | ----------------------------------------------------------------
> ------
> | Data descriptors defined here:
> |
> | __dict__
> | dictionary for instance variables (if defined)
> |
> | __weakref__
> | list of weak references to the object (if defined)
> >>>
>
> Except that the annotation documentation has disappeared!
>
> Hence, one assumes, the question!
>
> The problem is that the help system appears to be talking about two
> different things: 'bar' the class int, and 'bar' the method/property.
> At
> run-time however, there cannot be two uses of the same name, and the
> last-defined 'wins'.
>
> Continuing:-
>
> > ...
> > > > > foo = Foo()
> > > > > import typing
> > > > > typing.get_type_hints(foo)
> > {'bar': <class 'int'>}
> >
> > I could also decorate the property method return value:
> > ... def bar(self) -> int:
>
> ...and when the typing-hint is added to the property's def, the help
> listing still doesn't change/improve.
>
>
> That said, I've been following this last convention since moving to
> typing.
>
> Putting a separate description at the beginning of the class invites
> the
> reader to think of 'foo' as an integer. That's not 'wrong', in the
> sense
> that a property is/produces an attribute in the same dotted-notation
> from the object-name. However,there could be quite a lot of code
> between
> this 'declaration' line and the property def!
>
> However, there is another line of logic built upon the idea that all
> class-attributes should be defined in the class 'header' and all
> instance-attributes in __init__() or __post_init__(). Does this
> underlie
> the discussion?
>
>
> > I don't see the point though, because you can't access it with
> > get_type_hints.
>
> Correct: if one codes (only) the property-bar, then:
>
> >>> get_type_hints( Foo )
> {}
>
> However, reverting back to the first class-definition, we do see
> something for our money:
>
> >>> get_type_hints( foo )
> {'bar': <class 'int'>}
>
> Unfortunately, as mentioned above, there is this confusion between
> the
> two 'bar's...
>
> Proof?
>
> If we change things, the result is not what (it would appear) is
> desired:
>
> >>> class Foo:
> ... bar:str
> ... @property
> ... def bar( self ):
> ... return 1
> ...
> >>> get_type_hints( Foo )
> {'bar': <class 'str'>}
>
> Yet the 'bar' property will return an int!
>
> ...and this is proven/made worse when we add explicit typing to the
> property definition:
>
> >>> class Foo:
> ... bar:str
> ... @property
> ... def bar( self )->int:
> ... return 1
> ...
> >>> get_type_hints( Foo )
> {'bar': <class 'str'>}
>
> Our documentation entries don't agree, and don't match 'reality'.
> Ouch!
>
> Beyond that, I won't hazard a guess at the minds of the 'Python gods'
> who design and implement these things. However, please remember that
> in
> this discussion we have been using Python itself, whereas the docs
> and
> PEP-justifications for typing clearly say:
>
> <<<
> Note The Python runtime does not enforce function and variable type
> annotations. They can be used by third party tools such as type
> checkers, IDEs, linters, etc.
> >>>
>
> which may stump whatever the aim in using get-type-hints() may have
> been.
>
>
> If we're only talking about code-review, then (personal) comment of
> 'documenting' the method-definition applies.
> --
> Regards =dn
More information about the Python-list
mailing list