Acquisition [1] is a mechanism that allows objects to obtain attributes from their environment. It is similar to inheritence, except that, rather than traversing an inheritence hierarchy to obtain attributes, a containment hierarchy is traversed.
The ExtensionClass. release includes mix-in extension base classes that can be used to add acquisition as a feature to extension subclasses. These mix-in classes use the context-wrapping feature of ExtensionClasses to implement acquisition. Consider the following example:
import ExtensionClass, Acquisition class C(ExtensionClass.Base): color='red' class A(Acquisition.Implicit): def report(self): print self.color a=A() c=C() c.a=A() c.a.report() # prints 'red' d=C() d.color='green' d.a=a d.a.report() # prints 'green' a.report() # raises an attribute errorThe class A inherits acquisition behavior from Acquisition.Implicit. The object, a, "has" the color of objects c and d when it is accessed through them, but it has no color by itself. The object a obtains attributes from it's environment, where it's environment is defined by the access path used to reach a.
Aquisition wrappers provide access to the wrapped objects through the attributes aq_parent, aq_self, aq_base. In the example above, the expressions:
'c.a.aq_parent is c'and:
'c.a.aq_self is a'both evaluate to true, but the expression:
'c.a is a'evaluates to false, because the expression c.a evaluates to an acquisition wrapper around c and a, not a itself.
The attribute aq_base is similar to aq_self. Wrappers may be nested and aq_self may be a wrapped object. The aq_base attribute is the underlying object with all wrappers removed.
An attribute may be implicitly acquired if it's name does not begin with an underscore, _.
To support implicit acquisition, an object should inherit from the mix-in class Acquisition.Implicit.
print c.a.aq_acquire('color')To support explicit acquisition, an object should inherit from the mix-in class Acquisition.Explicit.
setting all attributes that should be acquired to the special value: Acquisition.Acquired. Setting an attribute to this value also allows inherited attributes to be overridden with acquired ones.
For example, in:
class C(Acquisition.Explicit): id=1 secret=2 color=Acquisition.Acquired __roles__=Acquisition.AcquiredThe only attributes that are automatically acquired from containing objects are color, and __roles__. Note also that the __roles__ attribute is acquired even though it's name begins with an underscore. In fact, the special Acquisition.Acquired value can be used in Acquisition.Implicit objects to implicitly acquire selected objects that smell like private objects.
The filter function is called with five arguments:
The object where an object was found,
The name of the object, as passed to aq_acquire,
The object found, and
The extra data passed to aq_acquire.
For example, in:
from Acquisition import Explicit class HandyForTesting: def __init__(self, name): self.name=name def __str__(self): return "%s(%s)" % (self.name, self.__class__.__name__) __repr__=__str__ class E(Explicit, HandyForTesting): pass class Nice(HandyForTesting): isNice=1 def __str__(self): return HandyForTesting.__str__(self)+' and I am nice!' __repr__=__str__ a=E('a') a.b=E('b') a.b.c=E('c') a.p=Nice('spam') a.b.p=E('p') def find_nice(self, ancestor, name, object, extra): return hasattr(object,'isNice') and object.isNice print a.b.c.aq_acquire('p', find_nice)The filtered acquisition in the last line skips over the first attribute it finds with the name p, because the attribute doesn't satisfy the condition given in the filter. The output of the last line is:
spam(Nice) and I am nice!
Unfortunately, C methods defined in extension base classes that define their own data structures, cannot use aquired attributes at this time. This is because wrapper objects do not conform to the data structures expected by these methods.
from Acquisition import Implicit class C(Implicit): def __init__(self, name): self.name=name def __str__(self): return "%s(%s)" % (self.name, self.__class__.__name__) __repr__=__str__ a=C("a") a.b=C("b") a.b.pref="spam" a.b.c=C("c") a.b.c.color="red" a.b.c.pref="eggs" a.x=C("x") o=a.b.c.xThe expression o.color might be expected to return "red". In earlier versions of ExtensionClass, however, this expression failed. Acquired acquiring objects did not acquire from the environment they were accessed in, because objects were only wrapped when they were first found, and were not rewrapped as they were passed down the acquisition tree.
In the current release of ExtensionClass, the expression "o.color" does indeed return "red".
When searching for an attribute in o, objects are searched in the order x, a, b, c. So, for example, the expression, o.pref returns "spam", not "eggs". In earlier releases of ExtensionClass, the attempt to get the pref attribute from o would have failed.
If desired, the current rules for looking up attributes in complex expressions can best be understood through repeated application of the __of__ method:
x.__of__(a)
b.__of__(a)
x.__of__(a).__of__(b.__of__(a))
c.__of__(b.__of__(a))
x.__of__(a).__of__(b.__of__(a)).__of__(c.__of__(b.__of__(a)))
Note that heuristics are used to avoid most of the repeated lookups. For example, in the expression: a.b.c.x.foo, the object a is searched no more than once, even though it is wrapped three times.
[1] Gil, J., Lorenz, D., Environmental Acquisition--A New Inheritance-Like Abstraction Mechanism OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996