Last active
May 8, 2022 11:40
-
-
Save XoseLluis/350478419109afd115bc7ef868d46d7a to your computer and use it in GitHub Desktop.
Python Lazy objects via __getattribute__, __setattr__ and dynamic classes and inheritance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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