I'm a big fan and advocate for static type hints in Python 3. I've been using them for a while with no problems.
I just ran into a new edge case that I can't seem to compile. What if I want to define a custom type, then define its parameters?
For example, this is common in Python 3:
from typing import List, NewType
CustomObject = NewType('CustomObject', List[int])
def f(data: List[CustomObject]):
# do something
But this won't compile:
class MyContainer():
# some class definition ...
from typing import NewType
SpecialContainer = NewType('SpecialContainer', MyContainer)
def f(data: SpecialContainer[str]):
# do something
I realize that SpecialContainer
is technically a function in this case, but it shouldn't be evaluated as one in the context of a type signature. The second code snippet fails with TypeError: 'function' object is not subscriptable
.
You can add type hints to function/method parameters and return types (Python 3.5), and variables used in assignment (effectively declarations – Python 3.6).
Here's how you can add type hints to our function: Add a colon and a data type after each function parameter. Add an arrow ( -> ) and a data type after the function to specify the return data type.
Python is both a strongly and dynamically typed programming language. This means that any variable can take on any data type at any time (this is dynamically typed), but once a variable is assigned with a type, it can not change in unexpected ways.
Python will always remain a dynamically typed language. However, PEP 484 introduced type hints, which make it possible to also do static type checking of Python code. Unlike how types work in most other statically typed languages, type hints by themselves don't cause Python to enforce types.
You have to design your classes from the ground up to accept static type hints. This didn't satisfy my original use case, since I was trying to declare special subtypes of 3rd party classes, but it compiles my code sample.
from typing import Generic, TypeVar, Sequence, List
# Declare your own accepted types for your container, required
T = TypeVar('T', int, str, float)
# The custom container has be designed to accept types hints
class MyContainer(Sequence[T]):
# some class definition ...
# Now, you can make a special container type
# Note that Sequence is a generic of List, and T is a generic of str, as defined above
SpecialContainer = TypeVar('SpecialContainer', MyContainer[List[str]])
# And this compiles
def f(data: SpecialContainer):
# do something
My original intention was to create a type hint that explained how a function, f()
, took a pd.DataFrame
object that was indexed by integers and whose cells were all strings. Using the above answer, I came up with a contrived way of expressing this.
from typing import Mapping, TypeVar, NewType, NamedTuple
from pandas import pd
# Create custom types, required even if redundant
Index = TypeVar('Index')
Row = TypeVar('Row')
# Create a child class of pd.DataFrame that includes a type signature
# Note that Mapping is a generic for a key-value store
class pdDataFrame(pd.DataFrame, Mapping[Index, Row]):
pass
# Now, this compiles, and explains what my special pd.DataFrame does
pdStringDataFrame = NewType('pdDataFrame', pdDataFrame[int, NamedTuple[str]])
# And this compiles
def f(data: pdStringDataFrame):
pass
If you are writing a custom class that resembles a container generic like Sequence
, Mapping
, or Any
, then go for it. It is free to add the type variable to your class definition.
If you are trying to notate a specific usage of a 3rd party class that doesn't implement type hints:
MyOrderedDictType = NewType('MyOrderedDictType', Dict[str, float])
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