Descriptors
Descriptors are objects with __get__, __set__, and __del__. These are used as class attributes. Their behaviors get trigger upon attribute lookup.
classmethod
class myclassmethod:
def __init__(self, func):
self.func = func
def __get__(self, obj, objtype=None):
if objtype is None:
objtype = type(obj)
return lambda *args,**kwargs: self.func(objtype, *args, **kwargs)class Person:
age = 1
get_age = myclassmethod(lambda cls: cls.age)Person.get_age()1
person = Person()person.get_age()1
Here, get_age is a function attribute which a descriptor. When get_age is accessed, the descriptor behavior is triggered which returns a bounded lambda, that’s when called calls the bounded lambda.
Looking the parameters for __get__, self is the descriptor instance itself, obj is the class instance and objtype is the class.
The way descriptor is access determines who arguments are being passed to __get__.
In more sophisticated manner this can be done as follows.
from types import MethodType
class myclassmethod:
def __init__(self, func):
self.func = func
def __get__(self, obj, objtype=None):
if objtype is None:
objtype = type(obj)
return MethodType(self.func, objtype)class Person:
age = 1
@myclassmethod
def get_age(cls):
return cls.agePerson.get_age()1
staticmethod
class mystaticmethod:
def __init__(self, func):
self.func = func
def __get__(self, obj, objtype=None):
return self.funcclass Person:
@mystaticmethod
def hi():
return "hi"Person.hi()'hi'
person = Person()person.hi()'hi'
Well, static method neither bounds to class nor instance. So, descriptor is just returning the same function back.
Dynamic Attributes
class Length:
def __get__(self, obj, objtype=None):
return len(obj.value)
class String:
length = Length()
def __init__(self, value):
self.value = value
string = String("hi")string.length2
string2 = String("bye")string2.length3