Skip to content

Instantly share code, notes, and snippets.

@hungngocphat01
Last active November 22, 2023 08:35
Show Gist options
  • Select an option

  • Save hungngocphat01/cae75beaaa56d373d14b71f8372c90a6 to your computer and use it in GitHub Desktop.

Select an option

Save hungngocphat01/cae75beaaa56d373d14b71f8372c90a6 to your computer and use it in GitHub Desktop.
My collection of Python dark magics

Generic types

from typing import Generic, get_args

_T = TypeVar("_T")

class SomeClass(Generic[_T]):
  def __init__(self, x: _T):
    self.x = x
   
  def add(self, y: _T):
    self.x += y

c = SomeClass[int](5)
c.add(2)
print(c.x)

This works fine with static type checking. However, if you want to resolve T at runtime, this won't work. For example, if you want to define some common methods for the repositories in your application:

class BaseRepository(Generic[_T]):
  def count(self):
    with Session(...) as session:
      return session.query(_T).count()

class UserRepository(BaseRepository[UserModel]):
  pass
  
user_repo = UserRepository()
user_repo.count()   # WON'T WORK

This won't work, since at runtime T has no value you can access. You have to use some dark magic™ in order to resolve T at runtime.

class BaseRepository(Generic[_T]):
  _my_T = None
  
  def __init_subclass__(cls):
    cls._my_T = typing.get_args(cls.__orig_bases__[0])[0]
    # ^~~~~ subclass's class variable
    # __init_subclass__ runs everytime a subclass is DEFINED

  def count(self):
    with Session(...) as session:
      return session.query(self._my_T).count()
      #                           ^~~~~ subclass's class variable
 
class UserRepository(BaseRepository[UserModel]):
  pass
    
user_repo = UserRepository()
user_repo.count()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment