Python – how to pass to a function argument type of a class object (typing)

Question:

I suppose that came with python 3.7 (not sure), the possibility to pass to a function not only the variable name but also the type of the variable. What I would like to know is if there is any possibility of passing the type of a particular class.

The same way you could pass:

def foo_func(i: int) -> None:
    pass

If I have a class let’s say:

class foo_class(object):
    pass

How could I transform the foo_func to receive the foo_classinstead of the inttype?

Furthermore, if foo_class was an inheritance of another class could I impose a more general type from the parent? For instance, if I would have,

class A(foo_class):
     pass

class B(foo_class):
     pass

How could I pass A or B based on its parent?

I mean something like:

def foo_func(obj: foo_class_type) -> None:
    pass

foo_func(A())
foo_func(B())

Answers:

Depending on whether you meant to pass a class (type) or an instance of a class, you’re looking for either typing.Type or simply the class.

Here’s a simple example to explain both situations:

from typing import Type, TypeVar


class Vehicle:
    def __init__(self):
        print("Creating a %s" % self.__class__.__name__)

    def move(self):
        print("This %s is moving…" % self.__class__.__name__)

TVehicle = TypeVar("TVehicle", bound=Vehicle)

class Car(Vehicle):
    def honk(self) -> None:
        print("tuuuuut")

class Bike(Vehicle):
    def ring(self) -> None:
        print("ring")

class Dog:
    def bark(self) -> None:
        print("woof!")


def move(v: Vehicle) -> None:
    v.move()

def instantiate(class_to_instantiate: Type[TVehicle]) -> TVehicle:
    return class_to_instantiate()  # create an instance

move(Bike())
move(Car())

instantiate(Bike).ring()
instantiate(Car).honk()
#instantiate(Dog)

Car and Bike inherit from Vehicle, so they both get at least the move method and the custom __init__, which reveals the name of the class that invoked it.

Now, in the first function, move, one simply wants to specify that the argument v should be an instance of a Vehicle. The function calls Vehicle’s move method, which will reveal the name of the instance’s class from which the call originated.

In the second function, instantiate, the goal is to create an instance of a class. This works through type variables, which allow you in this example to specify that there’s a relation between the function’s input argument and output argument: if I were to call instantiate(Bike), I want the return type to be an instance of the Bike class, so that I may legally call its ring method. If you were to replace the TVehicle in this function definition simply by Vehicle, your type checking program would complain, because the return type would then be an instance of the Vehicle class, for which you do not have a guarantee that the ring method exists.
Finally, the Type part that you see in the argument of instantiate simply allows you to call the function with a class, so not with an instance of that class. This is useful e.g. in cases where you want to delay instantiation of a class.

Note that this is an example to explain how to do it. In a more professional setting, Vehicle would likely be an abstract base class and some methods here could be given as class methods.

Side notes on your code example:

  1. Note that if you don’t intend to write code that also works in Python2, you shouldn’t inherit from object (ref).
  2. Classes are typically written with CapWord names, as specified in PEP8, the Python style guide. Following this style makes your code more easily understandable by other developers.
Answered By: Oliver W.