Reputation: 1304
Since python is dynamically typed, of course we can do something like this:
def f(x):
return 2 if x else "s"
But is this the way python was actually intended to be used? Or in other words, do union types exist in the sense they do in Racket for example? Or do we only use them like this:
def f(x):
if x:
return "s"
where the only "union" we need is with None?
Upvotes: 73
Views: 199711
Reputation: 81
Updating the answer with as per Python 3.10. We can create a Union Type in python by separating the object types by '|'.
Example:
def method(mobject: int | str) -> int | str | None:
pass
Upvotes: 2
Reputation: 4288
Note: As others mentioned, Python type hinting (by default) doesn't have any impact on runtime behavior, and it's used in static analysis and the like.
From Python 3.10 onwards, you can use |
separator for union types. Taking the example from What's New In Python 3.10:
def square(number: int | float) -> int | float:
return number ** 2
# Instead of
def square(number: Union[int, float]) -> Union[int, float]:
return number ** 2
Also, if you are using Python 3.7+, you can have the feature by using the __future__
package, with some limitations, however:
from __future__ import annotations
# Works in Python 3.7+
def square(number: int | float) -> int | float:
return number ** 2
# Works only in Python 3.10+
isinstance(3.10, int | float)
numeric = int | float
For more information, see Union Types documentation and PEP 604.
Upvotes: 42
Reputation: 1121864
Union typing is only needed when you have a statically typed language, as you need to declare that an object can return one of multiple types (in your case an int
or str
, or in the other example str
or NoneType
).
Python deals in objects only, so there is never a need to even consider 'union types'. Python functions return what they return, if the programmer wants to return different types for different results then that's their choice. The choice is then an architecture choice, and makes no difference to the Python interpreter (so there is nothing to 'benchmark' here).
Python 3.5 does introduce a standard for creating optional type hints, and that standard includes Union[...]
and Optional[...]
annotations. Type hinting adds optional static type checking outside of the runtime, the same way types in TypeScript are not part of the JavaScript runtime.
Upvotes: 83
Reputation: 27201
Here are a couple of options to deal with use-cases where you need a tagged union/sum type in Python:
Enum + Tuples
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]
A slight variation on this uses a dedicated SumType
class instead of a tuple:
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):
...
Or in combination with the "enum" idea from above:
token = SumType(Token.Number, 42)
if token.enum == Token.Number:
...
sumtypes module
These approaches all have their various drawbacks, of course.
Upvotes: 13
Reputation: 139
One use case not addressed by previous answers is building a union type from pre-existing types, and having isinstance()
consider that any instance of the pre-existing types are instances of the union type.
This is supported in Python through Abstract Base Classes. For example:
>>> import abc
>>> class IntOrString(abc.ABC): pass
...
>>> IntOrString.register(int)
<class 'int'>
>>> IntOrString.register(str)
<class 'str'>
Now int
and str
can be seen as subclasses of IntOrString
:
>>> issubclass(int, IntOrString)
True
>>> isinstance(42, IntOrString)
True
>>> isinstance("answer", IntOrString)
True
Upvotes: 5
Reputation: 3039
the type itself does not exist because Python is just a dynamically typed language, however, in newer Python versions, Union Type is an option for Type Hinting,
from typing import Union,TypeVar
T = TypeVar('T')
def f(x: T) -> Union[str, None]:
if x:
return "x"
you can use that to annotate your code, thus enabling IDE/Editor level syntax checking.
Upvotes: 42
Reputation: 56467
Adding to @MartijnPieters answer:
But is the way python was actually intended to be used?
Returning different type depending on the param is never a good practice in any language. This makes testing, maintaining and extending the code really difficult and IMHO is an anti-pattern (but of course sometimes necessary evil). The results should at least be related via having common interface.
The only reason union
was introduced to C was due to performance gain. But in Python you don't have this performance gain due to dynamic nature of the language (as Martijn noticed). Actually introducing union
would lower performance since the size of union
is always the size of the biggest member. Thus Python will never have C-like union
.
Upvotes: 1