3
"Copyright (C) 1996-1998, Digital Creations":COPYRIGHT.html.
5
Acquisition [1] is a mechanism that allows objects to obtain
6
attributes from their environment. It is similar to inheritence,
7
except that, rather than traversing an inheritence hierarchy
8
to obtain attributes, a containment hierarchy is traversed.
10
The "ExtensionClass":ExtensionClass.html. release includes mix-in
11
extension base classes that can be used to add acquisition as a
12
feature to extension subclasses. These mix-in classes use the
13
context-wrapping feature of ExtensionClasses to implement
14
acquisition. Consider the following example::
16
import ExtensionClass, Acquisition
18
class C(ExtensionClass.Base):
21
class A(Acquisition.Implicit):
30
c.a.report() # prints 'red'
36
d.a.report() # prints 'green'
38
a.report() # raises an attribute error
40
The class 'A' inherits acquisition behavior from
41
'Acquisition.Implicit'. The object, 'a', "has" the color of
42
objects 'c' and 'd' when it is accessed through them, but it
43
has no color by itself. The object 'a' obtains attributes
44
from it's environment, where it's environment is defined by
45
the access path used to reach 'a'.
49
When an object that supports acquisition is accessed through
50
an extension class instance, a special object, called an
51
acquisition wrapper, is returned. In the example above, the
52
expression 'c.a' returns an acquisition wrapper that
53
contains references to both 'c' and 'a'. It is this wrapper
54
that performs attribute lookup in 'c' when an attribute
55
cannot be found in 'a'.
57
Aquisition wrappers provide access to the wrapped objects
58
through the attributes 'aq_parent', 'aq_self', 'aq_base'.
59
In the example above, the expressions::
67
both evaluate to true, but the expression::
71
evaluates to false, because the expression 'c.a' evaluates
72
to an acquisition wrapper around 'c' and 'a', not 'a' itself.
74
The attribute 'aq_base' is similar to 'aq_self'. Wrappers may be
75
nested and 'aq_self' may be a wrapped object. The 'aq_base'
76
attribute is the underlying object with all wrappers removed.
80
Two styles of acquisition are supported in the current
81
ExtensionClass release, implicit and explicit aquisition.
85
Implicit acquisition is so named because it searches for
86
attributes from the environment automatically whenever an
87
attribute cannot be obtained directly from an object or
90
An attribute may be implicitly acquired if it's name does
91
not begin with an underscore, '_'.
93
To support implicit acquisition, an object should inherit
94
from the mix-in class 'Acquisition.Implicit'.
98
When explicit acquisition is used, attributes are not
99
automatically obtained from the environment. Instead, the
100
method 'aq_aquire' must be used, as in::
102
print c.a.aq_acquire('color')
104
To support explicit acquisition, an object should inherit
105
from the mix-in class 'Acquisition.Explicit'.
107
Controlled Acquisition
109
A class (or instance) can provide attribute by attribute control
110
over acquisition. This is done by:
112
- subclassing from 'Acquisition.Explicit', and
114
- setting all attributes that should be acquired to the special
115
value: 'Acquisition.Acquired'. Setting an attribute to this
116
value also allows inherited attributes to be overridden with
121
class C(Acquisition.Explicit):
124
color=Acquisition.Acquired
125
__roles__=Acquisition.Acquired
127
The *only* attributes that are automatically acquired from
128
containing objects are 'color', and '__roles__'. Note also
129
that the '__roles__' attribute is acquired even though it's
130
name begins with an underscore. In fact, the special
131
'Acquisition.Acquired' value can be used in
132
'Acquisition.Implicit' objects to implicitly acquire selected
133
objects that smell like private objects.
137
The acquisition method, 'aq_acquire', accepts two optional
138
arguments. The first of the additional arguments is a
139
"filtering" function that is used when considering whether to
140
acquire an object. The second of the additional arguments is an
141
object that is passed as extra data when calling the filtering
142
function and which defaults to 'None'.
144
The filter function is called with five arguments:
146
- The object that the 'aq_acquire' method was called on,
148
- The object where an object was found,
150
- The name of the object, as passed to 'aq_acquire',
152
- The object found, and
154
- The extra data passed to 'aq_acquire'.
156
If the filter returns a true object that the object found is
157
returned, otherwise, the acquisition search continues.
161
from Acquisition import Explicit
163
class HandyForTesting:
164
def __init__(self, name): self.name=name
166
return "%s(%s)" % (self.name, self.__class__.__name__)
169
class E(Explicit, HandyForTesting): pass
171
class Nice(HandyForTesting):
174
return HandyForTesting.__str__(self)+' and I am nice!'
183
def find_nice(self, ancestor, name, object, extra):
184
return hasattr(object,'isNice') and object.isNice
186
print a.b.c.aq_acquire('p', find_nice)
188
The filtered acquisition in the last line skips over the first
189
attribute it finds with the name 'p', because the attribute
190
doesn't satisfy the condition given in the filter. The output of
193
spam(Nice) and I am nice!
195
Acquisition and methods
197
Python methods of objects that support acquisition can use
198
acquired attributes as in the 'report' method of the first example
199
above. When a Python method is called on an object that is
200
wrapped by an acquisition wrapper, the wrapper is passed to the
201
method as the first argument. This rule also applies to
202
user-defined method types and to C methods defined in pure mix-in
205
Unfortunately, C methods defined in extension base classes that
206
define their own data structures, cannot use aquired attributes at
207
this time. This is because wrapper objects do not conform to the
208
data structures expected by these methods.
210
Acquiring Acquiring objects
212
Consider the following example::
214
from Acquisition import Implicit
217
def __init__(self, name): self.name=name
219
return "%s(%s)" % (self.name, self.__class__.__name__)
232
The expression 'o.color' might be expected to return '"red"'. In
233
earlier versions of ExtensionClass, however, this expression
234
failed. Acquired acquiring objects did not acquire from the
235
environment they were accessed in, because objects were only
236
wrapped when they were first found, and were not rewrapped as they
237
were passed down the acquisition tree.
239
In the current release of ExtensionClass, the expression "o.color"
240
does indeed return '"red"'.
242
When searching for an attribute in 'o', objects are searched in
243
the order 'x', 'a', 'b', 'c'. So, for example, the expression,
244
'o.pref' returns '"spam"', not '"eggs"'. In earlier releases of
245
ExtensionClass, the attempt to get the 'pref' attribute from 'o'
248
If desired, the current rules for looking up attributes in complex
249
expressions can best be understood through repeated application of
252
'a.x' -- 'x.__of__(a)'
254
'a.b' -- 'b.__of__(a)'
256
'a.b.x' -- 'x.__of__(a).__of__(b.__of__(a))'
258
'a.b.c' -- 'c.__of__(b.__of__(a))'
261
'x.__of__(a).__of__(b.__of__(a)).__of__(c.__of__(b.__of__(a)))'
263
and by keeping in mind that attribute lookup in a wrapper
264
is done by trying to lookup the attribute in the wrapped object
265
first and then in the parent object. In the expressions above
266
involving the '__of__' method, lookup proceeds from left to right.
268
Note that heuristics are used to avoid most of the repeated
269
lookups. For example, in the expression: 'a.b.c.x.foo', the object
270
'a' is searched no more than once, even though it is wrapped three
273
.. [1] Gil, J., Lorenz, D.,
274
"Environmental Acquisition--A New Inheritance-Like Abstraction Mechanism",
275
http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz,
276
OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996