3
<title>Acquisition</title>
7
<p> <a href="COPYRIGHT.html">Copyright (C) 1996-1998, Digital Creations</a>.</p>
8
<p> Acquisition <a href="#ref1">[1]</a> is a mechanism that allows objects to obtain
9
attributes from their environment. It is similar to inheritence,
10
except that, rather than traversing an inheritence hierarchy
11
to obtain attributes, a containment hierarchy is traversed.</p>
12
<p> The <a href="ExtensionClass.html">ExtensionClass</a>. release includes mix-in
13
extension base classes that can be used to add acquisition as a
14
feature to extension subclasses. These mix-in classes use the
15
context-wrapping feature of ExtensionClasses to implement
16
acquisition. Consider the following example:</p>
19
import ExtensionClass, Acquisition
21
class C(ExtensionClass.Base):
24
class A(Acquisition.Implicit):
33
c.a.report() # prints 'red'
39
d.a.report() # prints 'green'
41
a.report() # raises an attribute error
43
<p> The class <code>A</code> inherits acquisition behavior from
44
<code>Acquisition.Implicit</code>. The object, <code>a</code>, "has" the color of
45
objects <code>c</code> and <code>d</code> when it is accessed through them, but it
46
has no color by itself. The object <code>a</code> obtains attributes
47
from it's environment, where it's environment is defined by
48
the access path used to reach <code>a</code>.</p>
49
<h2> Acquisition wrappers</h2>
50
<p> When an object that supports acquisition is accessed through
51
an extension class instance, a special object, called an
52
acquisition wrapper, is returned. In the example above, the
53
expression <code>c.a</code> returns an acquisition wrapper that
54
contains references to both <code>c</code> and <code>a</code>. It is this wrapper
55
that performs attribute lookup in <code>c</code> when an attribute
56
cannot be found in <code>a</code>.</p>
57
<p> Aquisition wrappers provide access to the wrapped objects
58
through the attributes <code>aq_parent</code>, <code>aq_self</code>, <code>aq_base</code>.
59
In the example above, the expressions:</p>
69
<p> both evaluate to true, but the expression:</p>
74
<p> evaluates to false, because the expression <code>c.a</code> evaluates
75
to an acquisition wrapper around <code>c</code> and <code>a</code>, not <code>a</code> itself.</p>
76
<p> The attribute <code>aq_base</code> is similar to <code>aq_self</code>. Wrappers may be
77
nested and <code>aq_self</code> may be a wrapped object. The <code>aq_base</code>
78
attribute is the underlying object with all wrappers removed.</p>
79
<h2> Acquisition Control</h2>
80
<p> Two styles of acquisition are supported in the current
81
ExtensionClass release, implicit and explicit aquisition.</p>
82
<h3> Implicit acquisition</h3>
83
<p> Implicit acquisition is so named because it searches for
84
attributes from the environment automatically whenever an
85
attribute cannot be obtained directly from an object or
86
through inheritence.</p>
87
<p> An attribute may be implicitly acquired if it's name does
88
not begin with an underscore, <code>_</code>.</p>
89
<p> To support implicit acquisition, an object should inherit
90
from the mix-in class <code>Acquisition.Implicit</code>.</p>
91
<h3> Explicit Acquisition</h3>
92
<p> When explicit acquisition is used, attributes are not
93
automatically obtained from the environment. Instead, the
94
method <code>aq_aquire</code> must be used, as in:</p>
97
print c.a.aq_acquire('color')
99
<p> To support explicit acquisition, an object should inherit
100
from the mix-in class <code>Acquisition.Explicit</code>.</p>
101
<h3> Controlled Acquisition</h3>
102
<p> A class (or instance) can provide attribute by attribute control
103
over acquisition. This is done by:</p>
106
<li>subclassing from <code>Acquisition.Explicit</code>, and</li>
107
<li>setting all attributes that should be acquired to the special
108
value: <code>Acquisition.Acquired</code>. Setting an attribute to this
109
value also allows inherited attributes to be overridden with
110
acquired ones.<p> For example, in:</p>
113
class C(Acquisition.Explicit):
116
color=Acquisition.Acquired
117
__roles__=Acquisition.Acquired
119
<p> The <em>only</em> attributes that are automatically acquired from
120
containing objects are <code>color</code>, and <code>__roles__</code>. Note also
121
that the <code>__roles__</code> attribute is acquired even though it's
122
name begins with an underscore. In fact, the special
123
<code>Acquisition.Acquired</code> value can be used in
124
<code>Acquisition.Implicit</code> objects to implicitly acquire selected
125
objects that smell like private objects.</p>
129
<h3> Filtered Acquisition</h3>
130
<p> The acquisition method, <code>aq_acquire</code>, accepts two optional
131
arguments. The first of the additional arguments is a
132
"filtering" function that is used when considering whether to
133
acquire an object. The second of the additional arguments is an
134
object that is passed as extra data when calling the filtering
135
function and which defaults to <code>None</code>.</p>
136
<p> The filter function is called with five arguments:</p>
139
<li>The object that the <code>aq_acquire</code> method was called on,</li>
140
<li>The object where an object was found,</li>
141
<li>The name of the object, as passed to <code>aq_acquire</code>,</li>
142
<li>The object found, and</li>
143
<li>The extra data passed to <code>aq_acquire</code>.</li>
146
<p> If the filter returns a true object that the object found is
147
returned, otherwise, the acquisition search continues.</p>
148
<p> For example, in:</p>
151
from Acquisition import Explicit
153
class HandyForTesting:
154
def __init__(self, name): self.name=name
156
return "%s(%s)" % (self.name, self.__class__.__name__)
159
class E(Explicit, HandyForTesting): pass
161
class Nice(HandyForTesting):
164
return HandyForTesting.__str__(self)+' and I am nice!'
173
def find_nice(self, ancestor, name, object, extra):
174
return hasattr(object,'isNice') and object.isNice
176
print a.b.c.aq_acquire('p', find_nice)
178
<p> The filtered acquisition in the last line skips over the first
179
attribute it finds with the name <code>p</code>, because the attribute
180
doesn't satisfy the condition given in the filter. The output of
181
the last line is:</p>
184
spam(Nice) and I am nice!
186
<h2> Acquisition and methods</h2>
187
<p> Python methods of objects that support acquisition can use
188
acquired attributes as in the <code>report</code> method of the first example
189
above. When a Python method is called on an object that is
190
wrapped by an acquisition wrapper, the wrapper is passed to the
191
method as the first argument. This rule also applies to
192
user-defined method types and to C methods defined in pure mix-in
194
<p> Unfortunately, C methods defined in extension base classes that
195
define their own data structures, cannot use aquired attributes at
196
this time. This is because wrapper objects do not conform to the
197
data structures expected by these methods.</p>
198
<h2> Acquiring Acquiring objects</h2>
199
<p> Consider the following example:</p>
202
from Acquisition import Implicit
205
def __init__(self, name): self.name=name
207
return "%s(%s)" % (self.name, self.__class__.__name__)
220
<p> The expression <code>o.color</code> might be expected to return <code>"red"</code>. In
221
earlier versions of ExtensionClass, however, this expression
222
failed. Acquired acquiring objects did not acquire from the
223
environment they were accessed in, because objects were only
224
wrapped when they were first found, and were not rewrapped as they
225
were passed down the acquisition tree.</p>
226
<p> In the current release of ExtensionClass, the expression "o.color"
227
does indeed return <code>"red"</code>.</p>
228
<p> When searching for an attribute in <code>o</code>, objects are searched in
229
the order <code>x</code>, <code>a</code>, <code>b</code>, <code>c</code>. So, for example, the expression,
230
<code>o.pref</code> returns <code>"spam"</code>, not <code>"eggs"</code>. In earlier releases of
231
ExtensionClass, the attempt to get the <code>pref</code> attribute from <code>o</code>
232
would have failed.</p>
233
<p> If desired, the current rules for looking up attributes in complex
234
expressions can best be understood through repeated application of
235
the <code>__of__</code> method:</p>
237
<dt> <code>a.x</code></dt>
238
<dd><code>x.__of__(a)</code></dd>
239
<dt> <code>a.b</code></dt>
240
<dd><code>b.__of__(a)</code></dd>
241
<dt> <code>a.b.x</code></dt>
242
<dd><code>x.__of__(a).__of__(b.__of__(a))</code></dd>
243
<dt> <code>a.b.c</code></dt>
244
<dd><code>c.__of__(b.__of__(a))</code></dd>
245
<dt> <code>a.b.c.x</code></dt>
246
<dd><code>x.__of__(a).__of__(b.__of__(a)).__of__(c.__of__(b.__of__(a)))</code></dd>
248
<p> and by keeping in mind that attribute lookup in a wrapper
249
is done by trying to lookup the attribute in the wrapped object
250
first and then in the parent object. In the expressions above
251
involving the <code>__of__</code> method, lookup proceeds from left to right.</p>
252
<p> Note that heuristics are used to avoid most of the repeated
253
lookups. For example, in the expression: <code>a.b.c.x.foo</code>, the object
254
<code>a</code> is searched no more than once, even though it is wrapped three
256
<p><a name="ref1">[1]</a> Gil, J., Lorenz, D.,
257
<a href="http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz">Environmental Acquisition--A New Inheritance-Like Abstraction Mechanism</a>,
258
OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996</p>