dataclasses.Field doesn't resolve type annotation to actual type

人走茶凉 提交于 2020-01-16 14:34:12

问题


The documentation for the Field class of python's standard dataclasses module specifies only:

Its documented attributes are:

  • [...]
  • type: The type of the field.

To me, it seems to mean that the field will contain the type itself, and not only it's name in the form of a string.

However, it seems that it simply copies the type annotation as is, making it quite useless.

Example:

@dataclasses.dataclass 
class C: 
    c: 'C'

dataclasses.fields(C)[0].type # This returns the string 'C'
typing.get_type_hints(C)['c'] # This returns the class C, as expected

The problem even occurs systematically when using PEP563 type annotations.

Is this a bug in the dataclasses module? Is this the expected behavior? If so, how do I retrieve a type object given a Field instance?


回答1:


This is deliberate. Resolving type hints at import time is expensive, especially when from __future__ import annotations has been used to disable resolving them in the first place.

Initially, the addition of the PEP 563 to Python 3.7 broke dataclasses when you used the from __future__ import annotations switch and included ClassVar or InitVar type annotations for fields; these would not be resolved at this point and remained a string. This was already a problem before PEP 563 if you explicitly used strings, see dataclasses issue #92. This became a Python bug, #33453, once dataclasses made it into Python 3.7 proper.

The 'parent' project, attrs, which inspired dataclasses, also had this issue to solve. There, Łukasz Langa (co-author of most of the type hinting peps, including PEP 563), states:

OK, so I tried the above and it seems it's a nuclear option since it forces all annotations to be evaluated. This is what I wanted to avoid with from __future__ import annotations.

and in the discussion on the pull request that fixed issue 33453, Eric Smith, author of dataclasses, stated:

I've been researching doing just that. I think @ambv's point is that it introduces a performance hit due to calling eval on every field, while the point of string annotations is to remove a performance hit.

Moreover, there were other problems; you can't evaluate all type hints at import time, not when they use forward references:

In addition to the performance issue, in the following case (without a __future__ statement and without dataclasses), I get an error on get_type_hints() because C is undefined when get_type_hints() is called. This is python/typing#508. Notice that where get_type_hints() is called in this example is exactly where @dataclass is going to run and would need to call the stripped down get_type_hints().

So in the end, all that dataclasses does is do is apply string heuristics to the annotations, and will not load them for you.

To retrieve the type, just use get_type_hints() on the class itself, and us the field .name attribute as the key into the result:

resolved = typing.get_type_hints(C)
f = dataclasses.fields(C)[0]
ftype = resolved[f.name]


来源:https://stackoverflow.com/questions/55937859/dataclasses-field-doesnt-resolve-type-annotation-to-actual-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!