@@ -32,201 +32,201 @@ Annotations Best Practices
32
32
Accessing The Annotations Dict Of An Object In Python 3.10 And Newer
33
33
====================================================================
34
34
35
- Python 3.10 adds a new function to the standard library:
36
- :func: `inspect.get_annotations `. In Python versions 3.10
37
- and newer, calling this function is the best practice for
38
- accessing the annotations dict of any object that supports
39
- annotations. This function can also "un-stringize"
40
- stringized annotations for you.
41
-
42
- If for some reason :func: `inspect.get_annotations ` isn't
43
- viable for your use case, you may access the
44
- ``__annotations__ `` data member manually. Best practice
45
- for this changed in Python 3.10 as well: as of Python 3.10,
46
- ``o.__annotations__ `` is guaranteed to *always * work
47
- on Python functions, classes, and modules. If you're
48
- certain the object you're examining is one of these three
49
- *specific * objects, you may simply use ``o.__annotations__ ``
50
- to get at the object's annotations dict.
51
-
52
- However, other types of callables--for example,
53
- callables created by :func: `functools.partial `--may
54
- not have an ``__annotations__ `` attribute defined. When
55
- accessing the ``__annotations__ `` of a possibly unknown
56
- object, best practice in Python versions 3.10 and
57
- newer is to call :func: `getattr ` with three arguments,
58
- for example ``getattr(o, '__annotations__', None) ``.
59
-
60
- Before Python 3.10, accessing ``__annotations__ `` on a class that
61
- defines no annotations but that has a parent class with
62
- annotations would return the parent's ``__annotations__ ``.
63
- In Python 3.10 and newer, the child class's annotations
64
- will be an empty dict instead.
35
+ Python 3.10 adds a new function to the standard library:
36
+ :func: `inspect.get_annotations `. In Python versions 3.10
37
+ and newer, calling this function is the best practice for
38
+ accessing the annotations dict of any object that supports
39
+ annotations. This function can also "un-stringize"
40
+ stringized annotations for you.
41
+
42
+ If for some reason :func: `inspect.get_annotations ` isn't
43
+ viable for your use case, you may access the
44
+ ``__annotations__ `` data member manually. Best practice
45
+ for this changed in Python 3.10 as well: as of Python 3.10,
46
+ ``o.__annotations__ `` is guaranteed to *always * work
47
+ on Python functions, classes, and modules. If you're
48
+ certain the object you're examining is one of these three
49
+ *specific * objects, you may simply use ``o.__annotations__ ``
50
+ to get at the object's annotations dict.
51
+
52
+ However, other types of callables--for example,
53
+ callables created by :func: `functools.partial `--may
54
+ not have an ``__annotations__ `` attribute defined. When
55
+ accessing the ``__annotations__ `` of a possibly unknown
56
+ object, best practice in Python versions 3.10 and
57
+ newer is to call :func: `getattr ` with three arguments,
58
+ for example ``getattr(o, '__annotations__', None) ``.
59
+
60
+ Before Python 3.10, accessing ``__annotations__ `` on a class that
61
+ defines no annotations but that has a parent class with
62
+ annotations would return the parent's ``__annotations__ ``.
63
+ In Python 3.10 and newer, the child class's annotations
64
+ will be an empty dict instead.
65
65
66
66
67
67
Accessing The Annotations Dict Of An Object In Python 3.9 And Older
68
68
===================================================================
69
69
70
- In Python 3.9 and older, accessing the annotations dict
71
- of an object is much more complicated than in newer versions.
72
- The problem is a design flaw in these older versions of Python,
73
- specifically to do with class annotations.
70
+ In Python 3.9 and older, accessing the annotations dict
71
+ of an object is much more complicated than in newer versions.
72
+ The problem is a design flaw in these older versions of Python,
73
+ specifically to do with class annotations.
74
74
75
- Best practice for accessing the annotations dict of other
76
- objects--functions, other callables, and modules--is the same
77
- as best practice for 3.10, assuming you aren't calling
78
- :func: `inspect.get_annotations `: you should use three-argument
79
- :func: `getattr ` to access the object's ``__annotations__ ``
80
- attribute.
75
+ Best practice for accessing the annotations dict of other
76
+ objects--functions, other callables, and modules--is the same
77
+ as best practice for 3.10, assuming you aren't calling
78
+ :func: `inspect.get_annotations `: you should use three-argument
79
+ :func: `getattr ` to access the object's ``__annotations__ ``
80
+ attribute.
81
81
82
- Unfortunately, this isn't best practice for classes. The problem
83
- is that, since ``__annotations__ `` is optional on classes, and
84
- because classes can inherit attributes from their base classes,
85
- accessing the ``__annotations__ `` attribute of a class may
86
- inadvertently return the annotations dict of a *base class. *
87
- As an example::
82
+ Unfortunately, this isn't best practice for classes. The problem
83
+ is that, since ``__annotations__ `` is optional on classes, and
84
+ because classes can inherit attributes from their base classes,
85
+ accessing the ``__annotations__ `` attribute of a class may
86
+ inadvertently return the annotations dict of a *base class. *
87
+ As an example::
88
88
89
- class Base:
90
- a: int = 3
91
- b: str = 'abc'
89
+ class Base:
90
+ a: int = 3
91
+ b: str = 'abc'
92
92
93
- class Derived(Base):
94
- pass
93
+ class Derived(Base):
94
+ pass
95
95
96
- print(Derived.__annotations__)
96
+ print(Derived.__annotations__)
97
97
98
- This will print the annotations dict from ``Base ``, not
99
- ``Derived ``.
98
+ This will print the annotations dict from ``Base ``, not
99
+ ``Derived ``.
100
100
101
- Your code will have to have a separate code path if the object
102
- you're examining is a class (``isinstance(o, type) ``).
103
- In that case, best practice relies on an implementation detail
104
- of Python 3.9 and before: if a class has annotations defined,
105
- they are stored in the class's ``__dict__ `` dictionary. Since
106
- the class may or may not have annotations defined, best practice
107
- is to call the ``get `` method on the class dict.
101
+ Your code will have to have a separate code path if the object
102
+ you're examining is a class (``isinstance(o, type) ``).
103
+ In that case, best practice relies on an implementation detail
104
+ of Python 3.9 and before: if a class has annotations defined,
105
+ they are stored in the class's ``__dict__ `` dictionary. Since
106
+ the class may or may not have annotations defined, best practice
107
+ is to call the ``get `` method on the class dict.
108
108
109
- To put it all together, here is some sample code that safely
110
- accesses the ``__annotations__ `` attribute on an arbitrary
111
- object in Python 3.9 and before::
109
+ To put it all together, here is some sample code that safely
110
+ accesses the ``__annotations__ `` attribute on an arbitrary
111
+ object in Python 3.9 and before::
112
112
113
- if isinstance(o, type):
114
- ann = o.__dict__.get('__annotations__', None)
115
- else:
116
- ann = getattr(o, '__annotations__', None)
113
+ if isinstance(o, type):
114
+ ann = o.__dict__.get('__annotations__', None)
115
+ else:
116
+ ann = getattr(o, '__annotations__', None)
117
117
118
- After running this code, ``ann `` should be either a
119
- dictionary or ``None ``. You're encouraged to double-check
120
- the type of ``ann `` using :func: `isinstance ` before further
121
- examination.
118
+ After running this code, ``ann `` should be either a
119
+ dictionary or ``None ``. You're encouraged to double-check
120
+ the type of ``ann `` using :func: `isinstance ` before further
121
+ examination.
122
122
123
- Note that some exotic or malformed type objects may not have
124
- a ``__dict__ `` attribute, so for extra safety you may also wish
125
- to use :func: `getattr ` to access ``__dict__ ``.
123
+ Note that some exotic or malformed type objects may not have
124
+ a ``__dict__ `` attribute, so for extra safety you may also wish
125
+ to use :func: `getattr ` to access ``__dict__ ``.
126
126
127
127
128
128
Manually Un-Stringizing Stringized Annotations
129
129
==============================================
130
130
131
- In situations where some annotations may be "stringized",
132
- and you wish to evaluate those strings to produce the
133
- Python values they represent, it really is best to
134
- call :func: `inspect.get_annotations ` to do this work
135
- for you.
136
-
137
- If you're using Python 3.9 or older, or if for some reason
138
- you can't use :func: `inspect.get_annotations `, you'll need
139
- to duplicate its logic. You're encouraged to examine the
140
- implementation of :func: `inspect.get_annotations ` in the
141
- current Python version and follow a similar approach.
142
-
143
- In a nutshell, if you wish to evaluate a stringized annotation
144
- on an arbitrary object ``o ``:
145
-
146
- * If ``o `` is a module, use ``o.__dict__ `` as the
147
- ``globals `` when calling :func: `eval `.
148
- * If ``o `` is a class, use ``sys.modules[o.__module__].__dict__ ``
149
- as the ``globals ``, and ``dict(vars(o)) `` as the ``locals ``,
150
- when calling :func: `eval `.
151
- * If ``o `` is a wrapped callable using :func: `functools.update_wrapper `,
152
- :func: `functools.wraps `, or :func: `functools.partial `, iteratively
153
- unwrap it by accessing either ``o.__wrapped__ `` or ``o.func `` as
154
- appropriate, until you have found the root unwrapped function.
155
- * If ``o `` is a callable (but not a class), use
156
- ``o.__globals__ `` as the globals when calling :func: `eval `.
157
-
158
- However, not all string values used as annotations can
159
- be successfully turned into Python values by :func: `eval `.
160
- String values could theoretically contain any valid string,
161
- and in practice there are valid use cases for type hints that
162
- require annotating with string values that specifically
163
- *can't * be evaluated. For example:
164
-
165
- * :pep: `604 ` union types using ``| ``, before support for this
166
- was added to Python 3.10.
167
- * Definitions that aren't needed at runtime, only imported
168
- when :const: `typing.TYPE_CHECKING ` is true.
169
-
170
- If :func: `eval ` attempts to evaluate such values, it will
171
- fail and raise an exception. So, when designing a library
172
- API that works with annotations, it's recommended to only
173
- attempt to evaluate string values when explicitly requested
174
- to by the caller.
131
+ In situations where some annotations may be "stringized",
132
+ and you wish to evaluate those strings to produce the
133
+ Python values they represent, it really is best to
134
+ call :func: `inspect.get_annotations ` to do this work
135
+ for you.
136
+
137
+ If you're using Python 3.9 or older, or if for some reason
138
+ you can't use :func: `inspect.get_annotations `, you'll need
139
+ to duplicate its logic. You're encouraged to examine the
140
+ implementation of :func: `inspect.get_annotations ` in the
141
+ current Python version and follow a similar approach.
142
+
143
+ In a nutshell, if you wish to evaluate a stringized annotation
144
+ on an arbitrary object ``o ``:
145
+
146
+ * If ``o `` is a module, use ``o.__dict__ `` as the
147
+ ``globals `` when calling :func: `eval `.
148
+ * If ``o `` is a class, use ``sys.modules[o.__module__].__dict__ ``
149
+ as the ``globals ``, and ``dict(vars(o)) `` as the ``locals ``,
150
+ when calling :func: `eval `.
151
+ * If ``o `` is a wrapped callable using :func: `functools.update_wrapper `,
152
+ :func: `functools.wraps `, or :func: `functools.partial `, iteratively
153
+ unwrap it by accessing either ``o.__wrapped__ `` or ``o.func `` as
154
+ appropriate, until you have found the root unwrapped function.
155
+ * If ``o `` is a callable (but not a class), use
156
+ ``o.__globals__ `` as the globals when calling :func: `eval `.
157
+
158
+ However, not all string values used as annotations can
159
+ be successfully turned into Python values by :func: `eval `.
160
+ String values could theoretically contain any valid string,
161
+ and in practice there are valid use cases for type hints that
162
+ require annotating with string values that specifically
163
+ *can't * be evaluated. For example:
164
+
165
+ * :pep: `604 ` union types using ``| ``, before support for this
166
+ was added to Python 3.10.
167
+ * Definitions that aren't needed at runtime, only imported
168
+ when :const: `typing.TYPE_CHECKING ` is true.
169
+
170
+ If :func: `eval ` attempts to evaluate such values, it will
171
+ fail and raise an exception. So, when designing a library
172
+ API that works with annotations, it's recommended to only
173
+ attempt to evaluate string values when explicitly requested
174
+ to by the caller.
175
175
176
176
177
177
Best Practices For ``__annotations__ `` In Any Python Version
178
178
============================================================
179
179
180
- * You should avoid assigning to the ``__annotations__ `` member
181
- of objects directly. Let Python manage setting ``__annotations__ ``.
180
+ * You should avoid assigning to the ``__annotations__ `` member
181
+ of objects directly. Let Python manage setting ``__annotations__ ``.
182
182
183
- * If you do assign directly to the ``__annotations__ `` member
184
- of an object, you should always set it to a ``dict `` object.
183
+ * If you do assign directly to the ``__annotations__ `` member
184
+ of an object, you should always set it to a ``dict `` object.
185
185
186
- * If you directly access the ``__annotations__ `` member
187
- of an object, you should ensure that it's a
188
- dictionary before attempting to examine its contents.
186
+ * If you directly access the ``__annotations__ `` member
187
+ of an object, you should ensure that it's a
188
+ dictionary before attempting to examine its contents.
189
189
190
- * You should avoid modifying ``__annotations__ `` dicts.
190
+ * You should avoid modifying ``__annotations__ `` dicts.
191
191
192
- * You should avoid deleting the ``__annotations__ `` attribute
193
- of an object.
192
+ * You should avoid deleting the ``__annotations__ `` attribute
193
+ of an object.
194
194
195
195
196
196
``__annotations__ `` Quirks
197
197
==========================
198
198
199
- In all versions of Python 3, function
200
- objects lazy-create an annotations dict if no annotations
201
- are defined on that object. You can delete the ``__annotations__ ``
202
- attribute using ``del fn.__annotations__ ``, but if you then
203
- access ``fn.__annotations__ `` the object will create a new empty dict
204
- that it will store and return as its annotations. Deleting the
205
- annotations on a function before it has lazily created its annotations
206
- dict will throw an ``AttributeError ``; using ``del fn.__annotations__ ``
207
- twice in a row is guaranteed to always throw an ``AttributeError ``.
208
-
209
- Everything in the above paragraph also applies to class and module
210
- objects in Python 3.10 and newer.
211
-
212
- In all versions of Python 3, you can set ``__annotations__ ``
213
- on a function object to ``None ``. However, subsequently
214
- accessing the annotations on that object using ``fn.__annotations__ ``
215
- will lazy-create an empty dictionary as per the first paragraph of
216
- this section. This is *not * true of modules and classes, in any Python
217
- version; those objects permit setting ``__annotations__ `` to any
218
- Python value, and will retain whatever value is set.
219
-
220
- If Python stringizes your annotations for you
221
- (using ``from __future__ import annotations ``), and you
222
- specify a string as an annotation, the string will
223
- itself be quoted. In effect the annotation is quoted
224
- *twice. * For example::
225
-
226
- from __future__ import annotations
227
- def foo(a: "str"): pass
228
-
229
- print(foo.__annotations__)
230
-
231
- This prints ``{'a': "'str'"} ``. This shouldn't really be considered
232
- a "quirk"; it's mentioned here simply because it might be surprising.
199
+ In all versions of Python 3, function
200
+ objects lazy-create an annotations dict if no annotations
201
+ are defined on that object. You can delete the ``__annotations__ ``
202
+ attribute using ``del fn.__annotations__ ``, but if you then
203
+ access ``fn.__annotations__ `` the object will create a new empty dict
204
+ that it will store and return as its annotations. Deleting the
205
+ annotations on a function before it has lazily created its annotations
206
+ dict will throw an ``AttributeError ``; using ``del fn.__annotations__ ``
207
+ twice in a row is guaranteed to always throw an ``AttributeError ``.
208
+
209
+ Everything in the above paragraph also applies to class and module
210
+ objects in Python 3.10 and newer.
211
+
212
+ In all versions of Python 3, you can set ``__annotations__ ``
213
+ on a function object to ``None ``. However, subsequently
214
+ accessing the annotations on that object using ``fn.__annotations__ ``
215
+ will lazy-create an empty dictionary as per the first paragraph of
216
+ this section. This is *not * true of modules and classes, in any Python
217
+ version; those objects permit setting ``__annotations__ `` to any
218
+ Python value, and will retain whatever value is set.
219
+
220
+ If Python stringizes your annotations for you
221
+ (using ``from __future__ import annotations ``), and you
222
+ specify a string as an annotation, the string will
223
+ itself be quoted. In effect the annotation is quoted
224
+ *twice. * For example::
225
+
226
+ from __future__ import annotations
227
+ def foo(a: "str"): pass
228
+
229
+ print(foo.__annotations__)
230
+
231
+ This prints ``{'a': "'str'"} ``. This shouldn't really be considered
232
+ a "quirk"; it's mentioned here simply because it might be surprising.
0 commit comments