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