Действительно ли типы объединения существуют в python?

Поскольку python динамически типизируется, мы, конечно, можем сделать что-то вроде этого:

def f(x):
    return 2 if x else "s"

Но так ли на самом деле предполагалось использовать Python? Или, другими словами, существуют ли типы объединения в том смысле, в каком они существуют, например, в Racket? Или мы используем их только так:

def f(x):
    if x:
        return "s"

где единственный «союз», который нам нужен, это None?


person Lana    schedule 09.08.2016    source источник
comment
Чтобы уточнить, вы имеете в виду союз типы из Typed Racket? У Python нет ничего подобного.   -  person John Y    schedule 10.08.2016


Ответы (5)


Объединение необходимо только в том случае, если у вас есть статически типизированный язык, так как вам нужно объявить, что объект может возвращать один из нескольких типов (в вашем случае int или str, или в другом примере str или NoneType).

Python работает только с объектами, поэтому нет необходимости даже рассматривать «объединенные типы». Функции Python возвращают то, что они возвращают, если программист хочет возвращать разные типы для разных результатов, то это их выбор. Тогда выбор является выбором архитектуры и не имеет значения для интерпретатора Python (поэтому здесь нечего «сравнивать»).

Python 3.5 вводит стандарт для создания необязательных типов подсказок, и этот стандарт включает Union[...] и Optional[...] аннотации. Подсказка типов добавляет необязательную статическую проверку типов вне среды выполнения, точно так же, как типы в TypeScript не являются частью среды выполнения JavaScript.

person Martijn Pieters    schedule 09.08.2016
comment
Благодарю вас! Я понимаю, что python не является статически типизированным. Но я хотел знать, нужна ли на практике функция, которая возвращает несколько типов на основе параметра, или ВСЕГДА есть способ обойти это в python? - person Lana; 09.08.2016
comment
@Lana: это слишком, слишком широко. Но взгляните на pickle.loads() или json.loads(). Они возвращают произвольные объекты в зависимости от того, какие данные загружаются. - person Martijn Pieters; 09.08.2016
comment
@Lana: и опять же, это выбор архитектуры программного обеспечения в отношении того, что возвращает функция. Хорошей практикой является быть последовательным и ограничивать то, что возвращается, но «способ обойти это» - просто использовать передовые методы разработки программного обеспечения. Если ваша функция может возвращать, например, True, False, None or an integer, вам нужно переосмыслить дизайн своей функции. - person Martijn Pieters; 09.08.2016
comment
никогда не нужно даже рассматривать «типы союзов». Как это согласуется с решением ввести typing.Union? - person joel; 05.03.2021
comment
@joel: подсказка типа Python является статической типизацией, добавленной в Python. typing.Union не является типом среды выполнения. - person Martijn Pieters; 05.03.2021

сам тип не существует, потому что Python — это просто язык с динамической типизацией, однако в более новых версиях Python Union Type является опцией для Подсказка типа,

from typing import Union,TypeVar

T = TypeVar('T')
def f(x: T) -> Union[str, None]:
    if x:
        return "x"

вы можете использовать это для аннотирования своего кода, тем самым обеспечивая проверку синтаксиса на уровне IDE/редактора.

person Sajuuk    schedule 19.04.2018
comment
можешь объяснить T = TypeVar('T') - person Alen Paul Varghese; 13.10.2020
comment
@AlenPaulVarghese только что прочитал руководство: docs.python.org/3/library /typing.html#typing.TypeVar - person Sajuuk; 15.10.2020
comment
@AlenPaulVarghese T = TypeVar('T') создает именованный общий файл. Метод, представленный в этом ответе, примет что-либо в качестве входных данных и вернет строку x, если предоставленное не является None. Использование именованного универсального здесь было совершенно ненужным, но я предлагаю изучить их, поскольку они позволяют создавать функции шаблона, которые невероятно полезны. - person WebWanderer; 02.11.2020

Вот несколько вариантов для случаев использования, когда вам нужен помеченный тип union/sum в Питоне:

  • Перечисление + кортежи

    from enum import Enum
    Token = Enum('Token', ['Number', 'Operator', 'Identifier', 'Space', 'Expression'])
    
    (Token.Number, 42)                            # int
    (Token.Operator, '+')                         # str
    (Token.Identifier, 'foo')                     # str
    (Token.Space, )                               # void
    (Token.Expression, ('lambda', 'x', 'x+x'))    # tuple[str]
    

    Небольшой вариант этого использует выделенный класс SumType вместо кортежа:

    from dataclasses import dataclass
    from typing import Any
    
    @dataclass
    class SumType:
        enum: Enum
        data: Any
    
    SumType(Token.Number, 42)
    
  • isinstance

    if isinstance(data, int):
        ...
    if isinstance(data, str):
        ...
    

    Или в сочетании с идеей enum сверху:

    token = SumType(Token.Number, 42)
    
    if token.enum == Token.Number:
        ...
    
  • Модуль sumtypes

Конечно, все эти подходы имеют свои различные недостатки.

person Mateen Ulhaq    schedule 24.09.2018

Добавление к ответу @MartijnPieters:

Но действительно ли Python предназначен для использования?

Возврат другого типа в зависимости от параметра никогда не является хорошей практикой на любом языке. Это делает тестирование, поддержку и расширение кода действительно сложным, и ИМХО является анти-шаблоном (но, конечно, иногда необходимым злом). Результаты должны быть, по крайней мере, связаны через наличие общего интерфейса.

Единственная причина, по которой union была введена в C, связана с повышением производительности. Но в Python у вас нет такого прироста производительности из-за динамической природы языка (как заметил Мартин). На самом деле введение union снизит производительность, поскольку размер union всегда равен размеру самого большого члена. Таким образом, у Python никогда не будет C-подобного union.

person freakish    schedule 09.08.2016
comment
Благодарю вас! Хотя это именно то, что я хочу знать. Когда использование союзов в питоне является необходимым злом? И когда мы говорим о союзах, говорим ли мы о союзе ни с кем? (когда я многое заметил в питоне) или союзы между разными типами? Мне было интересно, есть ли какой-нибудь пример кода, который показывает это. - person Lana; 09.08.2016
comment
Обратите внимание, что я не думаю, что ОП говорит о союзе C. Я больше думаю, что они имеют в виду систему типов Java или C #. - person Martijn Pieters; 09.08.2016
comment
@Lana Как заметил Мартейн, json.loads() является примером необходимого зла. Объединения с None являются общей практикой, но IMO также следует избегать. Особенно в больших проектах вы просто не можете перестать читать эти NoneType object has no attribute xxx журналы. Мое личное мнение: одна функция == один возвращаемый тип. - person freakish; 09.08.2016
comment
@MartijnPieters Я понятия не имею, как профсоюзы работают на других языках. Извините, я могу ссылаться только на союзы C. - person freakish; 09.08.2016
comment
Спасибо вам, ребята! json.loads() был именно тем, что я искал. Мне нужно было увидеть примеры использования нескольких типов, и кажется, что потоковая передача — один из примеров. Есть ли другие ситуации? Или есть где-нибудь, где я могу найти больше примеров, подобных этим? - person Lana; 09.08.2016
comment
@Lana: опять же, это слишком широкое. Примеров бесчисленное множество, я привел два очевидных. Прочтите документацию по стандартной библиотеке, вы найдете гораздо больше. - person Martijn Pieters; 09.08.2016
comment
@Lana: Дело не в том, что не json.loads() — это именно то, что вы ищете. Дело в том, что json.loads() возвращает произвольные типы, включая типы, о которых вы, как программист, еще даже не задумывались. Следовательно, вы даже в принципе не можете определить тип объединения, чтобы охватить все, что он может вернуть. - person John Y; 10.08.2016
comment
См. помеченные типы union/sum. Они сильно отличаются по применению от союзов C. Мощные статически типизированные языки, такие как Haskell и Rust, широко используют их. - person Mateen Ulhaq; 24.09.2018

Один вариант использования, не рассмотренный в предыдущих ответах, — это создание типа объединения из уже существующих типов, и с учетом isinstance() того, что любой экземпляр ранее существовавших типов является экземпляром типа объединения.

В Python это поддерживается через Абстрактные базовые классы. Например:

>>> import abc
>>> class IntOrString(abc.ABC): pass
... 
>>> IntOrString.register(int)
<class 'int'>
>>> IntOrString.register(str)
<class 'str'>

Теперь int и str можно рассматривать как подклассы IntOrString:

>>> issubclass(int, IntOrString)
True
>>> isinstance(42, IntOrString)
True
>>> isinstance("answer", IntOrString)
True
person Yann Dirson    schedule 21.12.2020