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()`. |
|||
|
|||
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *} |
|||
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *} |
|||
|
|||
/// tip | Совет |
|||
|
|||
@ -70,7 +70,7 @@ $ pip install "fastapi[all]" |
|||
|
|||
Затем вы можете использовать новый объект `settings` в вашем приложении: |
|||
|
|||
{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *} |
|||
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *} |
|||
|
|||
### Запуск сервера { #run-the-server } |
|||
|
|||
@ -104,11 +104,11 @@ $ ADMIN_EMAIL="[email protected]" APP_NAME="ChimichangApp" fastapi run main.p |
|||
|
|||
Например, у вас может быть файл `config.py` со следующим содержимым: |
|||
|
|||
{* ../../docs_src/settings/app01_py39/config.py *} |
|||
{* ../../docs_src/settings/app01_py310/config.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 | Совет |
|||
|
|||
@ -126,7 +126,7 @@ $ ADMIN_EMAIL="[email protected]" APP_NAME="ChimichangApp" fastapi run main.p |
|||
|
|||
Продолжая предыдущий пример, ваш файл `config.py` может выглядеть так: |
|||
|
|||
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *} |
|||
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *} |
|||
|
|||
Обратите внимание, что теперь мы не создаем экземпляр по умолчанию `settings = Settings()`. |
|||
|
|||
@ -134,7 +134,7 @@ $ ADMIN_EMAIL="[email protected]" APP_NAME="ChimichangApp" fastapi run main.p |
|||
|
|||
Теперь мы создаем зависимость, которая возвращает новый `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 | Совет |
|||
|
|||
@ -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 } |
|||
|
|||
Далее будет очень просто предоставить другой объект настроек во время тестирования, создав переопределение зависимости для `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`, а затем возвращаем этот новый объект. |
|||
|
|||
@ -193,7 +193,7 @@ APP_NAME="ChimichangApp" |
|||
|
|||
Затем обновите ваш `config.py` так: |
|||
|
|||
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *} |
|||
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *} |
|||
|
|||
/// tip | Совет |
|||
|
|||
@ -226,7 +226,7 @@ def get_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`, будет возвращаться тот же объект, что был возвращен при первом вызове, снова и снова. |
|||
|
|||
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue