Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I pass arguments to custom static type hints in Python 3?

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.

like image 695
Chris Conlan Avatar asked Sep 03 '18 17:09

Chris Conlan


People also ask

Does Python 3.6 support type hints?

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).

How do you use type hints in Python?

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.

What is Type_checking Python?

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.

How do I create a statically typed in Python?

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.


1 Answers

Compiling My Code Sample

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

Subtyping a 3rd Party Class

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

Was it worth it?

  • 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:

    • Try using an existing type variable to get your point across, e.g. MyOrderedDictType = NewType('MyOrderedDictType', Dict[str, float])
    • If that doesn't work, you'll have to clutter your namespace with trivial child classes and type variables to get the type hint to compile. Better to use a docstring or comment to explain your situation.
like image 67
Chris Conlan Avatar answered Oct 11 '22 19:10

Chris Conlan