Skip to content

Instantly share code, notes, and snippets.

@nenetto
Created September 26, 2023 07:19
Show Gist options
  • Save nenetto/46e65ff249a3bc8dd17fa621c507eb12 to your computer and use it in GitHub Desktop.
Save nenetto/46e65ff249a3bc8dd17fa621c507eb12 to your computer and use it in GitHub Desktop.
Descriptors
# Descriptor protocol
# Special objects with that show magic functionality when they are the type of
# other's class attribute.
## Methods
def __get__(self, obj, type = None) -> object:
"""
self: descriptor itself
obj: object the descriptor is attached (as attribute) to
type: type of the obj
"""
pass
def __set__(self, obj, value) -> None:
pass
def __delete__(self, obj) -> None:
pass
def __set_name__(self, owner, name):
"""
This is for python > 3.6
name: is the name assigned as attribute
"""
pass
## Type of Descriptors
# If only __get__ -> Non-data descriptor
# If __get__ + __set__ / __delete__ -> Data descriptor
# ⛓️ The lookup chain
# When we do OBJECT.attribute/method:
# 1st) Data descriptor (__get__ + __set__ / __delete__): attribute.__get__()
# 2nd) OBJECT.__dict__['attribute/method']
# 3rd) Non-Data descriptor (only __get__): attribute.__get__()
# 4th) type(OBJECT).__dict__['attribute/method'] # Search in the Class attributes
# 5th) type(OBJECT).__base__.__dict__['attribute/method'] # Search in the Base class attributes
## Example of use: Lazy attributes.
# Loads the attribute when it is first accessed
# lazy_properties.py
import time
class LazyProperty:
""" Non data descriptor for a lazy property
"""
def __init__(self, function):
self.function = function
self.name = function.__name__
def __get__(self, obj, type=None) -> object:
obj.__dict__[self.name] = self.function(obj) # this is like call meaning_of_life(self), where self == obj
return obj.__dict__[self.name]
class DeepThought:
@LazyProperty
def meaning_of_life(self):
time.sleep(3)
return 42
my_deep_thought_instance = DeepThought() # At this point, my_deep_thought_instance.meaning_of_life is a method
print(my_deep_thought_instance.meaning_of_life) # Step 1 (miss), Step 2 (miss), Step 3 (found! Non data descriptor)
print(my_deep_thought_instance.meaning_of_life) # Step 1 (miss), Step 2 (found!, already set) -> no execution of function, just attribute
print(my_deep_thought_instance.meaning_of_life) # At this point my_deep_thought_instance.meaning_of_life is an integer (42)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment