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
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to