diff --git a/pyguide.md b/pyguide.md index 0efe667..7090e69 100644 --- a/pyguide.md +++ b/pyguide.md @@ -75,7 +75,7 @@ See README.md for details. + [3.19.7 Ignoring Types](#s3.19.7-ignoring-types) + [3.19.8 Typing Variables](#s3.19.8-typing-variables) + [3.19.9 Tuples vs Lists](#s3.19.9-tuples-vs-lists) - + [3.19.10 TypeVars](#s3.19.10-typevars) + + [3.19.10 Type variables](#s3.19.10-typevars) + [3.19.11 String types](#s3.19.11-string-types) + [3.19.12 Imports For Typing](#s3.19.12-imports-for-typing) + [3.19.13 Conditional Imports](#s3.19.13-conditional-imports) @@ -97,7 +97,7 @@ of *dos and don'ts* for Python programs. To help you format code correctly, we've created a [settings file for Vim](google_python_style.vim). For Emacs, the default settings should be fine. -Many teams use the [yapf](https://github.com/google/yapf/) +Many teams use the [Black](https://github.com/psf/black) or [Pyink](https://github.com/google/pyink) auto-formatter to avoid arguing over formatting. @@ -161,7 +161,8 @@ Suppress warnings if they are inappropriate so that other issues are not hidden. To suppress warnings, you can set a line-level comment: ```python -dict = 'something awful' # Bad Idea... pylint: disable=redefined-builtin +def do_PUT(self): # WSGI name, so pylint: disable=invalid-name + ... ``` `pylint` @@ -185,7 +186,7 @@ pylint --list-msgs To get more information on a particular message, use: ```shell -pylint --help-msg=C6409 +pylint --help-msg=invalid-name ``` Prefer `pylint: disable` to the deprecated older form `pylint: disable-msg`. @@ -195,7 +196,7 @@ beginning of the function. Always include a comment explaining why you are deleting it. "Unused." is sufficient. For example: ```python -def viking_cafe_order(spam: str, beans: str, eggs: Optional[str] = None) -> str: +def viking_cafe_order(spam: str, beans: str, eggs: str | None = None) -> str: del beans, eggs # Unused by vikings. return spam + spam + spam ``` @@ -347,7 +348,7 @@ No: The directory the main binary is located in should not be assumed to be in `sys.path` despite that happening in some environments. This being the case, -code should assume that `import jodie` refers to a third party or top level +code should assume that `import jodie` refers to a third-party or top-level package named `jodie`, not a local `jodie.py`. @@ -494,7 +495,7 @@ Avoid mutable global state. #### 2.5.1 Definition -Module level values or class attributes that can get mutated during program +Module-level values or class attributes that can get mutated during program execution. @@ -568,8 +569,8 @@ variables defined in enclosing scopes. Allows definition of utility classes and functions that are only used inside of a very limited scope. Very -[ADT](http://www.google.com/url?sa=D&q=http://en.wikipedia.org/wiki/Abstract_data_type)-y. -Commonly used for implementing decorators. +[ADT](https://en.wikipedia.org/wiki/Abstract_data_type)-y. Commonly used for +implementing decorators. @@ -973,7 +974,7 @@ definition. Yes: def foo(a, b=None): if b is None: b = [] -Yes: def foo(a, b: Optional[Sequence] = None): +Yes: def foo(a, b: Sequence | None = None): if b is None: b = [] Yes: def foo(a, b: Sequence = ()): # Empty tuple OK since tuples are immutable. @@ -1197,8 +1198,8 @@ experienced Lisp and Scheme (and Haskell and ML and ...) programmers. #### 2.16.3 Cons -Can lead to confusing bugs. Such as this example based on -[PEP-0227](http://www.google.com/url?sa=D&q=http://www.python.org/dev/peps/pep-0227/): +Can lead to confusing bugs, such as this example based on +[PEP-0227](https://peps.python.org/pep-0227/): ```python i = 4 @@ -1300,11 +1301,11 @@ decorator runs (at import time, perhaps from `pydoc` or other tools). A decorator that is called with valid parameters should (as much as possible) be guaranteed to succeed in all cases. -Decorators are a special case of "top level code" - see [main](#s3.17-main) for +Decorators are a special case of "top-level code" - see [main](#s3.17-main) for more discussion. Never use `staticmethod` unless forced to in order to integrate with an API -defined in an existing library. Write a module level function instead. +defined in an existing library. Write a module-level function instead. Use `classmethod` only when writing a named constructor, or a class-specific routine that modifies necessary global state such as a process-wide cache. @@ -1323,8 +1324,8 @@ or `__eq__` are implemented as Python methods) and their atomicity should not be relied upon. Neither should you rely on atomic variable assignment (since this in turn depends on dictionaries). -Use the Queue module's `Queue` data type as the preferred way to communicate -data between threads. Otherwise, use the threading module and its locking +Use the `queue` module's `Queue` data type as the preferred way to communicate +data between threads. Otherwise, use the `threading` module and its locking primitives. Prefer condition variables and `threading.Condition` instead of using lower-level locks. @@ -1460,11 +1461,11 @@ Use other `from __future__` import statements as you see fit. ### 2.21 Type Annotated Code You can annotate Python code with type hints according to -[PEP-484](https://www.python.org/dev/peps/pep-0484/), and type-check the code at -build time with a type checking tool like [pytype](https://github.com/google/pytype). +[PEP-484](https://peps.python.org/pep-0484/), and type-check the code at build +time with a type checking tool like [pytype](https://github.com/google/pytype). Type annotations can be in the source or in a -[stub pyi file](https://www.python.org/dev/peps/pep-0484/#stub-files). Whenever +[stub pyi file](https://peps.python.org/pep-0484/#stub-files). Whenever possible, annotations should be in the source. Use pyi files for third-party or extension modules. @@ -1483,7 +1484,7 @@ def func(a: int) -> list[int]: ``` You can also declare the type of a variable using similar -[PEP-526](https://www.python.org/dev/peps/pep-0526/) syntax: +[PEP-526](https://peps.python.org/pep-0526/) syntax: ```python a: SomeType = some_func() @@ -1554,23 +1555,53 @@ Explicit exceptions to the 80 character limit: - Long import statements. - URLs, pathnames, or long flags in comments. -- Long string module level constants not containing whitespace that would be +- Long string module-level constants not containing whitespace that would be inconvenient to split across lines such as URLs or pathnames. - Pylint disable comments. (e.g.: `# pylint: disable=invalid-name`) -Do not use backslash line continuation except for `with` statements requiring -three or more context managers. +Do not use a backslash for +[explicit line continuation](https://docs.python.org/3/reference/lexical_analysis.html#explicit-line-joining). -Make use of Python's +Instead, make use of Python's [implicit line joining inside parentheses, brackets and braces](http://docs.python.org/reference/lexical_analysis.html#implicit-line-joining). If necessary, you can add an extra pair of parentheses around an expression. +Note that this rule doesn't prohibit backslash-escaped newlines within strings +(see [below](#strings)). + ```python Yes: foo_bar(self, width, height, color='black', design=None, x='foo', emphasis=None, highlight=0) +``` - if (width == 0 and height == 0 and +```python + +Yes: if (width == 0 and height == 0 and color == 'red' and emphasis == 'strong'): + + (bridge_questions.clarification_on + .average_airspeed_of.unladen_swallow) = 'African or European?' + + with ( + very_long_first_expression_function() as spam, + very_long_second_expression_function() as beans, + third_thing() as eggs, + ): + place_order(eggs, beans, spam, beans) +``` + +```python + +No: if width == 0 and height == 0 and \ + color == 'red' and emphasis == 'strong': + + bridge_questions.clarification_on \ + .average_airspeed_of.unladen_swallow = 'African or European?' + + with very_long_first_expression_function() as spam, \ + very_long_second_expression_function() as beans, \ + third_thing() as eggs: + place_order(eggs, beans, spam, beans) ``` When a literal string won't fit on a single line, use parentheses for implicit @@ -1581,6 +1612,37 @@ x = ('This will build a very long long ' 'long long long long long long string') ``` +Prefer to break lines at the highest possible syntactic level. If you must break +a line twice, break it at the same syntactic level both times. + +```python +Yes: bridgekeeper.answer( + name="Arthur", quest=questlib.find(owner="Arthur", perilous=True)) + + answer = (a_long_line().of_chained_methods() + .that_eventually_provides().an_answer()) + + if ( + config is None + or 'editor.language' not in config + or config['editor.language'].use_spaces is False + ): + use_tabs() +``` + +```python +No: bridgekeeper.answer(name="Arthur", quest=questlib.find( + owner="Arthur", perilous=True)) + + answer = a_long_line().of_chained_methods().that_eventually_provides( + ).an_answer() + + if (config is None or 'editor.language' not in config or config[ + 'editor.language'].use_spaces is False): + use_tabs() + +``` + Within comments, put long URLs on their own line if necessary. ```python @@ -1594,29 +1656,6 @@ No: # See details at # v2.0/csv_file_name_extension_full_specification.html ``` -It is permissible to use backslash continuation when defining a `with` statement -with three or more context managers. For two context managers, use a nested -`with` statement: - -```python -Yes: with very_long_first_expression_function() as spam, \ - very_long_second_expression_function() as beans, \ - third_thing() as eggs: - place_order(eggs, beans, spam, beans) -``` - -```python -No: with VeryLongFirstExpressionFunction() as spam, \ - VeryLongSecondExpressionFunction() as beans: - PlaceOrder(beans, spam) -``` - -```python -Yes: with very_long_first_expression_function() as spam: - with very_long_second_expression_function() as beans: - place_order(beans, spam) -``` - Make note of the indentation of the elements in the line continuation examples above; see the [indentation](#s3.4-indentation) section for explanation. @@ -1753,151 +1792,7 @@ No: # Stuff on first line forbidden. Trailing commas in sequences of items are recommended only when the closing container token `]`, `)`, or `}` does not appear on the same line as the final element. The presence of a trailing comma is also used as a hint to our Python -code auto-formatter [YAPF](https://pypi.org/project/yapf/) to direct it to auto-format the container -of items to one item per line when the `,` after the final element is present. - -```python -Yes: golomb3 = [0, 1, 3] -Yes: golomb4 = [ - 0, - 1, - 4, - 6, - ] -``` - -```python -No: golomb4 = [ - 0, - 1, - 4, - 6 - ] -``` - - - - - -### 3.5 Blank Lines - -Two blank lines between top-level definitions, be they function or class -definitions. One blank line between method definitions and between the docstring -of a `class` and the first method. No blank line following a `def` line. Use -single blank lines as you judge appropriate within functions or methods. - -Blank lines need not be anchored to the definition. For example, related -comments immediately preceding function, class, and method definitions can make -sense. Consider if your comment might be more useful as part of the docstring. - - - - - -### 3.6 Whitespace - -Follow standard typographic rules for the use of spaces around punctuation. - -No whitespace inside parentheses, brackets or braces. - -```python -Yes: spam(ham[1], {'eggs': 2}, []) -``` - -```python -No: spam( ham[ 1 ], { 'eggs': 2 }, [ ] ) -``` - -No whitespace before a comma, semicolon, or colon. Do use whitespace after a -comma, semicolon, or colon, except at the end of the line. - -```python -Yes: if x == 4: - print(x, y) - x, y = y, x -``` - -```python -No: if x == 4 : - print(x , y) - x , y = y , x -``` - -No whitespace before the open paren/bracket that starts an argument list, -indexing or slicing. - -```python -Yes: spam(1) -``` - -```python -No: spam (1) -``` - -```python -Yes: dict['key'] = list[index] -``` - -```python -No: dict ['key'] = list [index] -``` - -No trailing whitespace. - -Surround binary operators with a single space on either side for assignment -(`=`), comparisons (`==, <, >, !=, <>, <=, >=, in, not in, is, is not`), and -Booleans (`and, or, not`). Use your better judgment for the insertion of spaces -around arithmetic operators (`+`, `-`, `*`, `/`, `//`, `%`, `**`, `@`). - -```python -Yes: x == 1 -``` - -```python -No: x<1 -``` - -Never use spaces around `=` when passing keyword arguments or defining a default -parameter value, with one exception: -[when a type annotation is present](#typing-default-values), *do* use spaces -around the `=` for the default parameter value. - -```python -Yes: def complex(real, imag=0.0): return Magic(r=real, i=imag) -Yes: def complex(real, imag: float = 0.0): return Magic(r=real, i=imag) -``` - -```python -No: def complex(real, imag = 0.0): return Magic(r = real, i = imag) -No: def complex(real, imag: float=0.0): return Magic(r = real, i = imag) -``` - -Don't use spaces to vertically align tokens on consecutive lines, since it -becomes a maintenance burden (applies to `:`, `#`, `=`, etc.): - -```python -Yes: - foo = 1000 # comment - long_name = 2 # comment that should not be aligned - - dictionary = { - 'foo': 1, - 'long_name': 2, - } -``` - -```python -No: - foo = 1000 # comment - long_name = 2 # comment that should not be aligned - - dictionary = { - 'foo' : 1, - 'long_name': 2, - } -``` - - +code auto-formatter @@ -1908,7 +1803,7 @@ No: Most `.py` files do not need to start with a `#!` line. Start the main file of a program with `#!/usr/bin/env python3` (to support virtualenvs) or `#!/usr/bin/python3` per -[PEP-394](https://www.python.org/dev/peps/pep-0394/). +[PEP-394](https://peps.python.org/pep-0394/). This line is used by the kernel to find the Python interpreter, but is ignored by Python when importing modules. It is only necessary on a file intended to be executed directly. @@ -1933,15 +1828,14 @@ Python uses *docstrings* to document code. A docstring is a string that is the first statement in a package, module, class or function. These strings can be extracted automatically through the `__doc__` member of the object and are used by `pydoc`. -(Try running `pydoc` on your module to see how it looks.) Always use the three -double-quote `"""` format for docstrings (per -[PEP 257](https://www.google.com/url?sa=D&q=http://www.python.org/dev/peps/pep-0257/)). -A docstring should be organized as a summary line (one physical line not -exceeding 80 characters) terminated by a period, question mark, or exclamation -point. When writing more (encouraged), this must be followed by a blank line, -followed by the rest of the docstring starting at the same cursor position as -the first quote of the first line. There are more formatting guidelines for -docstrings below. +(Try running `pydoc` on your module to see how it looks.) Always use the +three-double-quote `"""` format for docstrings (per +[PEP 257](https://peps.python.org/pep-0257/)). A docstring should be organized +as a summary line (one physical line not exceeding 80 characters) terminated by +a period, question mark, or exclamation point. When writing more (encouraged), +this must be followed by a blank line, followed by the rest of the docstring +starting at the same cursor position as the first quote of the first line. There +are more formatting guidelines for docstrings below. @@ -1950,12 +1844,12 @@ docstrings below. #### 3.8.2 Modules -Every file should contain license boilerplate. Choose the appropriate boilerplate for the license used by the project (for example, Apache 2.0, BSD, LGPL, GPL) +Every file should contain license boilerplate. Choose the appropriate boilerplate for the license used by the project (for example, Apache 2.0, BSD, LGPL, GPL). Files should start with a docstring describing the contents and usage of the module. ```python -"""A one line summary of the module or program, terminated by a period. +"""A one-line summary of the module or program, terminated by a period. Leave one blank line. The rest of this docstring should contain an overall description of the module or program. Optionally, it may also @@ -2081,9 +1975,10 @@ aptly described using a one-line docstring. part of the API). ```python -def fetch_smalltable_rows(table_handle: smalltable.Table, - keys: Sequence[Union[bytes, str]], - require_all_keys: bool = False, +def fetch_smalltable_rows( + table_handle: smalltable.Table, + keys: Sequence[bytes | str], + require_all_keys: bool = False, ) -> Mapping[bytes, tuple[str, ...]]: """Fetches rows from a Smalltable. @@ -2118,9 +2013,10 @@ def fetch_smalltable_rows(table_handle: smalltable.Table, Similarly, this variation on `Args:` with a line break is also allowed: ```python -def fetch_smalltable_rows(table_handle: smalltable.Table, - keys: Sequence[Union[bytes, str]], - require_all_keys: bool = False, +def fetch_smalltable_rows( + table_handle: smalltable.Table, + keys: Sequence[bytes | str], + require_all_keys: bool = False, ) -> Mapping[bytes, tuple[str, ...]]: """Fetches rows from a Smalltable. @@ -2179,7 +2075,11 @@ class SampleClass: """ def __init__(self, likes_spam: bool = False): - """Inits SampleClass with blah.""" + """Initializes the instance based on spam preference. + + Args: + likes_spam: Defines if instance exhibits this preference. + """ self.likes_spam = likes_spam self.eggs = 0 @@ -2293,6 +2193,7 @@ Yes: x = f'name: {name}; score: {n}' x = '%s, %s!' % (imperative, expletive) x = '{}, {}'.format(first, second) x = 'name: %s; score: %d' % (name, n) + x = 'name: %(name)s; score: %(score)d' % {'name':name, 'score':n} x = 'name: {}; score: {}'.format(name, n) x = a + b ``` @@ -2309,7 +2210,7 @@ be optimized on CPython, that is an implementation detail. The conditions under which an optimization applies are not easy to predict and may change. Instead, add each substring to a list and `''.join` the list after the loop terminates, or write each substring to an `io.StringIO` buffer. These techniques -consistently have amortized-linear run time complexity. +consistently have amortized-linear run-time complexity. ```python Yes: items = [''] @@ -2388,6 +2289,11 @@ Don't do this. will collapse common leading spaces in each line.""") ``` +Note that using a backslash here does not violate the prohibition against +[explicit line continuation](#line-length); in this case, the backslash is +[escaping a newline](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals) +in a string literal. + @@ -2644,7 +2550,7 @@ grouped from most generic to least generic: ``` 5. **Deprecated:** application-specific imports that are part of the same - top level + top-level sub-package as this file. For example: @@ -2782,8 +2688,8 @@ Always use a `.py` filename extension. Never use dashes. - counters or iterators (e.g. `i`, `j`, `k`, `v`, et al.) - `e` as an exception identifier in `try/except` statements. - `f` as a file handle in `with` statements - - private [`TypeVar`s](#typing-type-var) with no constraints (e.g. `_T`, - `_U`, `_V`) + - private [type variables](#typing-type-var) with no constraints (e.g. + `_T = TypeVar("_T")`, `_P = ParamSpec("_P")`) Please be mindful not to abuse single-character naming. Generally speaking, descriptiveness should be proportional to the name's scope of visibility. @@ -2826,11 +2732,11 @@ Always use a `.py` filename extension. Never use dashes. a class. ("wait -- did I write `import StringIO` or `from StringIO import StringIO`?") -- Underscores may appear in *unittest* method names starting with `test` to - separate logical components of the name, even if those components use - CapWords. One possible pattern is `test_`; for - example `testPop_EmptyStack` is okay. There is no One Correct Way to name - test methods. +- New *unit test* files follow PEP 8 compliant lower\_with\_under method + names, for example, `test__`. For consistency(\*) + with legacy modules that follow CapWords function names, underscores may + appear in method names starting with `test` to separate logical components + of the name. One possible pattern is `test_`. @@ -3015,8 +2921,7 @@ the function into smaller and more manageable pieces. #### 3.19.1 General Rules -* Familiarize yourself with - [PEP-484](https://www.python.org/dev/peps/pep-0484/). +* Familiarize yourself with [PEP-484](https://peps.python.org/pep-0484/). * In methods, only annotate `self`, or `cls` if it is necessary for proper type information. e.g., @@ -3061,7 +2966,7 @@ def my_method( self, first_var: int, second_var: Foo, - third_var: Optional[Bar], + third_var: Bar | None, ) -> int: ... ``` @@ -3084,7 +2989,7 @@ parenthesis with the `def`: Yes: def my_method( self, - other_arg: Optional[MyLongType], + other_arg: MyLongType | None, ) -> tuple[MyLongType1, MyLongType1]: ... ``` @@ -3107,7 +3012,7 @@ opening one, but this is less readable. ```python No: def my_method(self, - other_arg: Optional[MyLongType], + other_arg: MyLongType | None, ) -> dict[OtherLongType, MyLongType]: ... ``` @@ -3185,8 +3090,7 @@ class OtherClass: #### 3.19.4 Default Values -As per -[PEP-008](https://www.python.org/dev/peps/pep-0008/#other-recommendations), use +As per [PEP-008](https://peps.python.org/pep-0008/#other-recommendations), use spaces around the `=` *only* for arguments that have both a type annotation and a default value. @@ -3211,18 +3115,18 @@ def func(a:int=0) -> int: In the Python type system, `NoneType` is a "first class" type, and for typing purposes, `None` is an alias for `NoneType`. If an argument can be `None`, it -has to be declared! You can use `Union`, but if there is only one other type, -use `Optional`. +has to be declared! You can use `|` union type expressions (recommended in new +Python 3.10+ code), or the older `Optional` and `Union` syntaxes. -Use explicit `Optional` instead of implicit `Optional`. Earlier versions of PEP -484 allowed `a: str = None` to be interpreted as `a: Optional[str] = None`, but -that is no longer the preferred behavior. +Use explicit `X | None` instead of implicit. Earlier versions of PEP 484 allowed +`a: str = None` to be interpreted as `a: str | None = None`, but that is no +longer the preferred behavior. ```python Yes: -def func(a: Optional[str], b: Optional[str] = None) -> str: +def modern_or_union(a: str | int | None, b: str | None = None) -> str: ... -def multiple_nullable_union(a: Union[None, str, int]) -> str: +def union_optional(a: Union[str, int, None], b: Optional[str] = None) -> str: ... ``` @@ -3245,19 +3149,13 @@ def implicit_optional(a: str = None) -> str: You can declare aliases of complex types. The name of an alias should be CapWorded. If the alias is used only in this module, it should be \_Private. -For example, if the name of the module together with the name of the type is too -long: - - +Note that the `: TypeAlias` annotation is only supported in versions 3.10+. ```python -_LossAndGradient = tuple[tf.Tensor, tf.Tensor] -ComplexTFMap = Mapping[str, _LossAndGradient] +_LossAndGradient: TypeAlias = tuple[tf.Tensor, tf.Tensor] +ComplexTFMap: TypeAlias = Mapping[str, _LossAndGradient] ``` -Other examples are complex nested types and multiple return variables from a -function (as a tuple). - @@ -3325,23 +3223,31 @@ c: tuple[int, str, float] = (1, "2", 3.5) -#### 3.19.10 TypeVars +#### 3.19.10 Type variables The Python type system has -[generics](https://www.python.org/dev/peps/pep-0484/#generics). The factory -function `TypeVar` is a common way to use them. +[generics](https://peps.python.org/pep-0484/#generics). A type variable, such as +`TypeVar` and `ParamSpec`, is a common way to use them. Example: ```python -from typing import TypeVar +from collections.abc import Callable +from typing import ParamSpec, TypeVar +_P = ParamSpec("_P") _T = TypeVar("_T") ... def next(l: list[_T]) -> _T: return l.pop() + +def print_when_called(f: Callable[_P, _T]) -> Callable[_P, _T]: + def inner(*args: P.args, **kwargs: P.kwargs) -> R: + print('Function was called') + return f(*args, **kwargs) + return inner ``` -A TypeVar can be constrained: +A `TypeVar` can be constrained: ```python AddableType = TypeVar("AddableType", int, float, str) @@ -3360,8 +3266,8 @@ def check_length(x: AnyStr) -> AnyStr: raise ValueError() ``` -A TypeVar must have a descriptive name, unless it meets all of the following -criteria: +A type variable must have a descriptive name, unless it meets all of the +following criteria: * not externally visible * not constrained @@ -3369,6 +3275,7 @@ criteria: ```python Yes: _T = TypeVar("_T") + _P = ParamSpec("_P") AddableType = TypeVar("AddableType", int, float, str) AnyFunction = TypeVar("AnyFunction", bound=Callable) ``` @@ -3376,6 +3283,7 @@ Yes: ```python No: T = TypeVar("T") + P = ParamSpec("P") _T = TypeVar("_T", int, float, str) _F = TypeVar("_F", bound=Callable) ``` @@ -3418,7 +3326,7 @@ line from the `typing` and `collections.abc` modules. Ex: ```python from collections.abc import Mapping, Sequence -from typing import Any, Union +from typing import Any, Generic ``` Given that this way of importing adds items to the local namespace, names in @@ -3460,7 +3368,7 @@ def generate_foo_scores(foo: Set[str]) -> List[float]: Use conditional imports only in exceptional cases where the additional imports needed for type checking must be avoided at runtime. This pattern is -discouraged; alternatives such as refactoring the code to allow top level +discouraged; alternatives such as refactoring the code to allow top-level imports should be preferred. Imports that are needed only for type annotations can be placed within an `if @@ -3496,8 +3404,8 @@ because each module has to depend on the other. Replace modules that create circular dependency imports with `Any`. Set an [alias](#typing-aliases) with a meaningful name, and use the real type name from -this module (any attribute of Any is Any). Alias definitions should be separated -from the last import by one line. +this module (any attribute of `Any` is `Any`). Alias definitions should be +separated from the last import by one line. ```python from typing import Any @@ -3517,21 +3425,18 @@ def my_method(self, var: "some_mod.SomeType") -> None: #### 3.19.15 Generics When annotating, prefer to specify type parameters for generic types; otherwise, -[the generics' parameters will be assumed to be `Any`](https://www.python.org/dev/peps/pep-0484/#the-any-type). +[the generics' parameters will be assumed to be `Any`](https://peps.python.org/pep-0484/#the-any-type). ```python # Yes: -def get_names(employee_ids: list[int]) -> dict[int, Any]: +def get_names(employee_ids: Sequence[int]) -> Mapping[int, str]: ... ``` ```python # No: -# These are both interpreted as get_names(employee_ids: list[Any]) -> dict[Any, Any] -def get_names(employee_ids: list) -> Dict: - ... - -def get_names(employee_ids: List) -> Dict: +# This is interpreted as get_names(employee_ids: Sequence[Any]) -> Mapping[Any, Any] +def get_names(employee_ids: Sequence) -> Mapping: ... ``` @@ -3541,14 +3446,14 @@ appropriate: ```python # No: -def get_names(employee_ids: list[Any]) -> dict[Any, str]: +def get_names(employee_ids: Sequence[Any]) -> Mapping[Any, str]: """Returns a mapping from employee ID to employee name for given IDs.""" ``` ```python # Yes: _T = TypeVar('_T') -def get_names(employee_ids: list[_T]) -> dict[_T, str]: +def get_names(employee_ids: Sequence[_T]) -> Mapping[_T, str]: """Returns a mapping from employee ID to employee name for given IDs.""" ```