This recipe provides a descriptor class and a decorator to make the deferred binding. Before the first access to that name, the __dict__ of the class will have the descriptor object. After that first access it will have whatever was returned by the first access.
One big advantage of deferring the binding is that the class object will be available at that time and can be passed to the object. During normal class creation the class body is exec'd before the class object is created, so the objects bound there can't be passed the class.
Recipe #577746 provides a concrete example of how the deferred_binder can be used.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | """deferred_binder module"""
class DeferredBinder:
"""A descriptor that defers binding for an object.
The binding is delayed until the name to which the descriptor is
bound is accessed. This is also the name to which the object would
have been bound if the descriptor hadn't been used.
"""
def __init__(self, name, target):
self.name = name
self.target = target
def __get__(self, obj, cls):
context = (cls.__dict__, cls)
target = self.transform(self.name, context, self.target, obj)
setattr(cls, self.name, self.target)
return target
@staticmethod
def transform(name, context, target, obj=None):
"""Transform the target and return it.
name - the name to which the target will be bound.
context - namespace, and optionally the class, in which the
target will be bound to the name.
obj - the instance of the class that is involved in calling
this method, if any.
"""
return target
@staticmethod
def is_deferred(f):
"""A decorator."""
return DeferredBinder(f.__name__, f)
|