Home > Software design >  In Python, is it possible to pass function generics like C#?
In Python, is it possible to pass function generics like C#?

Time:04-22

I would like to pass generic class to a function so I can return the same type as result. My use case is writing ORM and a read function, which will take a table ORM, and return the same type as result, something like this:

def read<T>(id, fields="*") -> T:

Currently without typing, my code look like this:

def read(table, id, fields="*"):

Is something like this possible?

CodePudding user response:

TL;DR Python doesn't need generics because it is a dynamically typed langauge. This allows us to use so-called duck typing instead of explicit generics.

The first thing to note is that type annotations in python are only suggestions. They help static analysis tools such as code completion while you are writing code but are not enforced at run-time.

With that in mind, let's look at a non-annotated version of your function:

def read(table, fields="*"):
    pass

Here, python doesn't care what you pass for table as long as it supports the operations that are used inside of read(). This is what we call duck-typing: if it quacks like a duck, it's a duck. You see this in functions that act on sequeneces. For example:

def print_list(my_list):
    for el in my_list:
       print(el)

We can call print_list() with a list or a tuple:

print_list([1, 2, 3, 4])
print_list((1, 2, 3, 4))

In fact, anything that implements __iter__() and __next__() will work. This is the contract of the type and doesn't need to be specified with an interface, superclass, or generic type.

Now back to type hinting: there is a way to specify the "duck type contract" with type hinting, but I don't know the details off the top of my head. You could do some research to find out. As @juanpa.arrivillaga posted in a comment, you can look at typing.Protocol if you are interested in learning how to do the type hinting for cases like this.

CodePudding user response:

Ok, I recently was digging in Python type hinting abilities and came up with something like that

DBModel = TypeVar('DBModel')

class BaseRepository(Generic[DBModel]):

    def __init__(self):
        self.model: Type[DBModel] = get_args(self.__orig_bases__[0])[0]

    def get_where(self, *args) -> DBModel
        ...  # any logic with self.model

as a base class and

class UsersRepository(BaseRepository[User]):
    pass

UsersRepository will now have already implemented get_where() with User model and will also have reference for User inside self.model

I guess that's what you want. Github with source code (small project actually.)

  • Related