committed by
GitHub
788 changed files with 6183 additions and 9641 deletions
@ -0,0 +1,61 @@ |
|||||
|
# Advanced Python Types { #advanced-python-types } |
||||
|
|
||||
|
Here are some additional ideas that might be useful when working with Python types. |
||||
|
|
||||
|
## Using `Union` or `Optional` { #using-union-or-optional } |
||||
|
|
||||
|
If your code for some reason can't use `|`, for example if it's not in a type annotation but in something like `response_model=`, instead of using the vertical bar (`|`) you can use `Union` from `typing`. |
||||
|
|
||||
|
For example, you could declare that something could be a `str` or `None`: |
||||
|
|
||||
|
```python |
||||
|
from typing import Union |
||||
|
|
||||
|
|
||||
|
def say_hi(name: Union[str, None]): |
||||
|
print(f"Hi {name}!") |
||||
|
``` |
||||
|
|
||||
|
`typing` also has a shortcut to declare that something could be `None`, with `Optional`. |
||||
|
|
||||
|
Here's a tip from my very **subjective** point of view: |
||||
|
|
||||
|
* 🚨 Avoid using `Optional[SomeType]` |
||||
|
* Instead ✨ **use `Union[SomeType, None]`** ✨. |
||||
|
|
||||
|
Both are equivalent and underneath they are the same, but I would recommend `Union` instead of `Optional` because the word "**optional**" would seem to imply that the value is optional, and it actually means "it can be `None`", even if it's not optional and is still required. |
||||
|
|
||||
|
I think `Union[SomeType, None]` is more explicit about what it means. |
||||
|
|
||||
|
It's just about the words and names. But those words can affect how you and your teammates think about the code. |
||||
|
|
||||
|
As an example, let's take this function: |
||||
|
|
||||
|
```python |
||||
|
from typing import Optional |
||||
|
|
||||
|
|
||||
|
def say_hi(name: Optional[str]): |
||||
|
print(f"Hey {name}!") |
||||
|
``` |
||||
|
|
||||
|
The parameter `name` is defined as `Optional[str]`, but it is **not optional**, you cannot call the function without the parameter: |
||||
|
|
||||
|
```Python |
||||
|
say_hi() # Oh, no, this throws an error! 😱 |
||||
|
``` |
||||
|
|
||||
|
The `name` parameter is **still required** (not *optional*) because it doesn't have a default value. Still, `name` accepts `None` as the value: |
||||
|
|
||||
|
```Python |
||||
|
say_hi(name=None) # This works, None is valid 🎉 |
||||
|
``` |
||||
|
|
||||
|
The good news is, in most cases, you will be able to simply use `|` to define unions of types: |
||||
|
|
||||
|
```python |
||||
|
def say_hi(name: str | None): |
||||
|
print(f"Hey {name}!") |
||||
|
``` |
||||
|
|
||||
|
So, normally you don't have to worry about names like `Optional` and `Union`. 😎 |
||||
@ -0,0 +1,61 @@ |
|||||
|
# Продвинутые типы Python { #advanced-python-types } |
||||
|
|
||||
|
Ниже несколько дополнительных идей, которые могут быть полезны при работе с типами Python. |
||||
|
|
||||
|
## Использование `Union` или `Optional` { #using-union-or-optional } |
||||
|
|
||||
|
Если по какой-то причине ваш код не может использовать `|`, например, если это не аннотация типов, а что-то вроде `response_model=`, вместо вертикальной черты (`|`) можно использовать `Union` из `typing`. |
||||
|
|
||||
|
Например, вы можете объявить, что значение может быть `str` или `None`: |
||||
|
|
||||
|
```python |
||||
|
from typing import Union |
||||
|
|
||||
|
|
||||
|
def say_hi(name: Union[str, None]): |
||||
|
print(f"Hi {name}!") |
||||
|
``` |
||||
|
|
||||
|
В `typing` также есть сокращение, чтобы объявить, что значение может быть `None`, — `Optional`. |
||||
|
|
||||
|
Вот совет с моей очень субъективной точки зрения: |
||||
|
|
||||
|
- 🚨 Избегайте использования `Optional[SomeType]` |
||||
|
- Вместо этого ✨ используйте **`Union[SomeType, None]`** ✨. |
||||
|
|
||||
|
Оба варианта эквивалентны и под капотом это одно и то же, но я бы рекомендовал `Union` вместо `Optional`, потому что слово «optional» может наводить на мысль, что значение необязательное, тогда как на самом деле это означает «значение может быть `None`», даже если оно не является необязательным и по-прежнему требуется. |
||||
|
|
||||
|
По-моему, `Union[SomeType, None]` более явно передаёт смысл. |
||||
|
|
||||
|
Речь только о словах и названиях. Но эти слова могут влиять на то, как вы и ваша команда думаете о коде. |
||||
|
|
||||
|
В качестве примера возьмём такую функцию: |
||||
|
|
||||
|
```python |
||||
|
from typing import Optional |
||||
|
|
||||
|
|
||||
|
def say_hi(name: Optional[str]): |
||||
|
print(f"Hey {name}!") |
||||
|
``` |
||||
|
|
||||
|
Параметр `name` объявлен как `Optional[str]`, но он не является необязательным: вы не можете вызвать функцию без этого параметра: |
||||
|
|
||||
|
```Python |
||||
|
say_hi() # О нет, это вызывает ошибку! 😱 |
||||
|
``` |
||||
|
|
||||
|
Параметр `name` по-прежнему обязателен (не «optional»), так как у него нет значения по умолчанию. При этом `name` принимает `None` в качестве значения: |
||||
|
|
||||
|
```Python |
||||
|
say_hi(name=None) # Это работает, None допустим 🎉 |
||||
|
``` |
||||
|
|
||||
|
Хорошая новость: в большинстве случаев вы сможете просто использовать `|` для объявления объединений типов: |
||||
|
|
||||
|
```python |
||||
|
def say_hi(name: str | None): |
||||
|
print(f"Hey {name}!") |
||||
|
``` |
||||
|
|
||||
|
Так что обычно вам не о чем переживать из‑за названий вроде `Optional` и `Union`. 😎 |
||||
@ -54,7 +54,7 @@ $ pip install "fastapi[all]" |
|||||
|
|
||||
Вы можете использовать все те же возможности валидации и инструменты, что и для Pydantic‑моделей, например разные типы данных и дополнительную валидацию через `Field()`. |
Вы можете использовать все те же возможности валидации и инструменты, что и для Pydantic‑моделей, например разные типы данных и дополнительную валидацию через `Field()`. |
||||
|
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *} |
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *} |
||||
|
|
||||
/// tip | Совет |
/// tip | Совет |
||||
|
|
||||
@ -70,7 +70,7 @@ $ pip install "fastapi[all]" |
|||||
|
|
||||
Затем вы можете использовать новый объект `settings` в вашем приложении: |
Затем вы можете использовать новый объект `settings` в вашем приложении: |
||||
|
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *} |
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *} |
||||
|
|
||||
### Запуск сервера { #run-the-server } |
### Запуск сервера { #run-the-server } |
||||
|
|
||||
@ -104,11 +104,11 @@ $ ADMIN_EMAIL="[email protected]" APP_NAME="ChimichangApp" fastapi run main.p |
|||||
|
|
||||
Например, у вас может быть файл `config.py` со следующим содержимым: |
Например, у вас может быть файл `config.py` со следующим содержимым: |
||||
|
|
||||
{* ../../docs_src/settings/app01_py39/config.py *} |
{* ../../docs_src/settings/app01_py310/config.py *} |
||||
|
|
||||
А затем использовать его в файле `main.py`: |
А затем использовать его в файле `main.py`: |
||||
|
|
||||
{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *} |
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *} |
||||
|
|
||||
/// tip | Совет |
/// tip | Совет |
||||
|
|
||||
@ -126,7 +126,7 @@ $ ADMIN_EMAIL="[email protected]" APP_NAME="ChimichangApp" fastapi run main.p |
|||||
|
|
||||
Продолжая предыдущий пример, ваш файл `config.py` может выглядеть так: |
Продолжая предыдущий пример, ваш файл `config.py` может выглядеть так: |
||||
|
|
||||
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *} |
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *} |
||||
|
|
||||
Обратите внимание, что теперь мы не создаем экземпляр по умолчанию `settings = Settings()`. |
Обратите внимание, что теперь мы не создаем экземпляр по умолчанию `settings = Settings()`. |
||||
|
|
||||
@ -134,7 +134,7 @@ $ ADMIN_EMAIL="[email protected]" APP_NAME="ChimichangApp" fastapi run main.p |
|||||
|
|
||||
Теперь мы создаем зависимость, которая возвращает новый `config.Settings()`. |
Теперь мы создаем зависимость, которая возвращает новый `config.Settings()`. |
||||
|
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *} |
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *} |
||||
|
|
||||
/// tip | Совет |
/// tip | Совет |
||||
|
|
||||
@ -146,13 +146,13 @@ $ ADMIN_EMAIL="[email protected]" APP_NAME="ChimichangApp" fastapi run main.p |
|||||
|
|
||||
Затем мы можем запросить ее в *функции-обработчике пути* как зависимость и использовать там, где нужно. |
Затем мы можем запросить ее в *функции-обработчике пути* как зависимость и использовать там, где нужно. |
||||
|
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *} |
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *} |
||||
|
|
||||
### Настройки и тестирование { #settings-and-testing } |
### Настройки и тестирование { #settings-and-testing } |
||||
|
|
||||
Далее будет очень просто предоставить другой объект настроек во время тестирования, создав переопределение зависимости для `get_settings`: |
Далее будет очень просто предоставить другой объект настроек во время тестирования, создав переопределение зависимости для `get_settings`: |
||||
|
|
||||
{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *} |
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *} |
||||
|
|
||||
В переопределении зависимости мы задаем новое значение `admin_email` при создании нового объекта `Settings`, а затем возвращаем этот новый объект. |
В переопределении зависимости мы задаем новое значение `admin_email` при создании нового объекта `Settings`, а затем возвращаем этот новый объект. |
||||
|
|
||||
@ -193,7 +193,7 @@ APP_NAME="ChimichangApp" |
|||||
|
|
||||
Затем обновите ваш `config.py` так: |
Затем обновите ваш `config.py` так: |
||||
|
|
||||
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *} |
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *} |
||||
|
|
||||
/// tip | Совет |
/// tip | Совет |
||||
|
|
||||
@ -226,7 +226,7 @@ def get_settings(): |
|||||
|
|
||||
Но так как мы используем декоратор `@lru_cache` сверху, объект `Settings` будет создан только один раз — при первом вызове. ✔️ |
Но так как мы используем декоратор `@lru_cache` сверху, объект `Settings` будет создан только один раз — при первом вызове. ✔️ |
||||
|
|
||||
{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *} |
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *} |
||||
|
|
||||
Затем при любых последующих вызовах `get_settings()` в зависимостях для следующих запросов, вместо выполнения внутреннего кода `get_settings()` и создания нового объекта `Settings`, будет возвращаться тот же объект, что был возвращен при первом вызове, снова и снова. |
Затем при любых последующих вызовах `get_settings()` в зависимостях для следующих запросов, вместо выполнения внутреннего кода `get_settings()` и создания нового объекта `Settings`, будет возвращаться тот же объект, что был возвращен при первом вызове, снова и снова. |
||||
|
|
||||
|
|||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue