In Python documentation for typing & type hints we have the below example:
Vector = List[float]
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
Vector
type alias clearly shows that type aliases are useful for simplifying complex type signatures.
However, what about aliasing primitive data types?
Let's contrast two basic examples of function signatures:
URL = str
def process_url(url: URL) -> URL:
pass
vs.
def process_url(url: str) -> str:
pass
Version with type alias URL
for primitive type str
is:
Dict
or namedtuple
later on without changing functions signatures).The problem is I cannot find anyone else following such practice. I am simply afraid that I am unintentionally abusing type hints to implement my own ideas instead of following their intended purpose.
Note from 2020-10
Python 3.9 introduces "flexible function and variable annotations", which allows to make annotations like:
def speed_1(distance: "feet", time: "seconds") -> "miles per hour":
pass
def speed_2(
distance: Annotated[float, "feet"], time: Annotated[float, "seconds"]
) -> Annotated[float, "miles per hour"]:
pass
Which renders aliasing data types for documenting purposes rather redundant!
See:
Type hints work best in modern Pythons. Annotations were introduced in Python 3.0, and it's possible to use type comments in Python 2.7. Still, improvements like variable annotations and postponed evaluation of type hints mean that you'll have a better experience doing type checks using Python 3.6 or even Python 3.7.
Introduction to Type Hints As the code base gets larger, type hints can help to debug and prevent some dumb mistakes. If you're using an IDE like PyCharm, you'll get a warning message whenever you've used the wrong data type, provided you're using type hints.
Using an alias to mark the meaning of a value can be misleading and dangerous. If only a subset of values are valid, a NewType should be used instead.
Recall that the use of a type alias declares two types to be equivalent to one another. Doing
Alias = Original
will make the static type checker treatAlias
as being exactly equivalent toOriginal
in all cases. This is useful when you want to simplify complex type signatures.
Simple aliasing works both ways: the alias URL = str
means any URL
is a str
and also means any str
is a URL
– which is usually not correct: A URL is a special kind of str
and not any can take its place. An alias URL = str
is a too strong statement of equality, as it cannot express this distinction. In fact, any inspection that does not look at the source code does not see the distinction:
In [1]: URL = str
In [2]: def foo(bar: URL):
...: pass
...:
In [3]: foo?
Signature: foo(bar: str)
Consider that you alias Celsius = float
in one module, and Fahrenheit = float
in another. This signals that it is valid to use Celsius
as Fahrenheit
, which is wrong.
Unless your types do cary separative meaning, you should just take a url: str
. The name signifies the meaning, the type the valid values. That means that your type should be suitable to separate valid and invalid values!
Use aliases to shorten your hints, but use NewType to refine them.
Vector = List[float] # alias shortens
URL = NewType("URL", str) # new type separates
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With