Property type hints?

dn PythonList at DancesWithMice.info
Wed Dec 9 13:49:31 EST 2020


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