Skip to content

Instantly share code, notes, and snippets.

@XoseLluis
Last active May 8, 2022 11:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save XoseLluis/350478419109afd115bc7ef868d46d7a to your computer and use it in GitHub Desktop.
Save XoseLluis/350478419109afd115bc7ef868d46d7a to your computer and use it in GitHub Desktop.
Python Lazy objects via __getattribute__, __setattr__ and dynamic classes and inheritance
def lazy(cls, *args, **kwargs):
class _Lazy(cls):
def __init__(self, *args, **kwargs):
_Lazy.original_args = args
_Lazy.original_kwargs = kwargs
def _lazy_init(self):
print(f"_lazy_init")
# remove the traps so that they do not interfere in the next accesses
del _Lazy.__setattr__
del _Lazy.__getattribute__
# invoke the __init__ of the "target" class
super().__init__(*self.original_args, *self.original_kwargs)
#change the parent class so that when we do a "type()" we no longer get "_Lazy", but the "real" class
self.__class__ = _Lazy.__bases__[0]
def __setattr__(self, name, value):
print(f"setting attribute: {name}")
#self._lazy_init() # can't do this as it will trigger the traps again
# however, traps do not have effect on accesses to attributes through the class itself rather than through instances
_Lazy._lazy_init(self)
setattr(self, name, value)
def __getattribute__(self, name):
print(f"getting attribute: {name}")
_Lazy._lazy_init(self)
return getattr(self, name)
return _Lazy(*args, **kwargs)
class Person:
def __init__(self, name, age):
print(f"{Person.__name__}.__init__")
self.name = name
self.age = age
def say_hi(self, to_someone):
print(f"{self.name} with age {self.age} says Bonjour to {to_someone}")
def test_1():
lazy_p1 = lazy(Person, "Lazy Francois", 14)
print(f"type: {type(lazy_p1).__name__}")
# initialization takes place
lazy_p1.say_hi("Xose")
# trap has been deactivated
print(lazy_p1.name)
lazy_p1.say_hi("Xose")
print(f"type: {type(lazy_p1).__name__}")
test_1()
# output:
# type: _Lazy
# getting attribute: say_hi
# _lazy_init
# Person.__init__
# Lazy Francois with age 14 says Bonjour to Xose
# Lazy Francois
# Lazy Francois with age 14 says Bonjour to Xose
# type: Person
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment