Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] - نسخه متنی

David Ascher, Alex Martelli, Anna Ravenscroft

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید


Recipe 6.6. Delegating Special Methods in Proxies


Credit: Gonçalo Rodrigues


Problem


In the new-style object model, Python
operations perform implicit lookups for special methods on the class
(rather than on the instance, as they do in the classic object
model). Nevertheless, you need to wrap new-style instances in proxies
that can also delegate a selected set of special methods to the
object they're wrapping.


Solution


You need to generate each proxy's class on the fly.
For example:

class Proxy(object):
"" base class for all proxies ""
def _ _init_ _(self, obj):
super(Proxy, self)._ _init_ _(obj)
self._obj = obj
def _ _getattr_ _(self, attrib):
return getattr(self._obj, attrib)
def make_binder(unbound_method):
def f(self, *a, **k): return unbound_method(self._obj, *a, **k)
# in 2.4, only: f._ _name_ _ = unbound_method._ _name_ _
return f
known_proxy_classes = { }
def proxy(obj, *specials):
''' factory-function for a proxy able to delegate special methods '''
# do we already have a suitable customized class around?
obj_cls = obj._ _class_ _
key = obj_cls, specials
cls = known_proxy_classes.get(key)
if cls is None:
# we don't have a suitable class around, so let's make it
cls = type("%sProxy" % obj_cls._ _name_ _, (Proxy,), { })
for name in specials:
name = '_ _%s_ _' % name
unbound_method = getattr(obj_cls, name)
setattr(cls, name, make_binder(unbound_method))
# also cache it for the future
known_proxy_classes[key] = cls
# instantiate and return the needed proxy
return cls(obj)


Discussion




Proxying and automatic delegation are a
joy in Python, thanks to the _ _getattr_ _ hook.
Python calls it automatically when a lookup for any attribute
(including a methodPython draws no distinction there) has not
otherwise succeeded.

In the old-style (classic) object model, _ _getattr_
_
also applied to special methods that were looked up as
part of a Python operation. This required some care to avoid
mistakenly supplying a special method one didn't
really want to supply but was otherwise handy. Nowadays, the
new-style object model is recommended for all new code: it is faster,
more regular, and richer in features. You get new-style classes when
you subclass object or any other built-in type.
One day, some years from now, Python 3.0 will eliminate the classic
object model, as well as other features that are still around only
for backwards-compatibility. (See http://www.python.org/peps/pep-3000l for
details about plans for Python 3.0almost all changes will be
language simplifications, rather than new features.)

In the new-style object model, Python operations
don't look up special methods at runtime: they rely
on "slots" held in class objects.
Such slots are updated when a class object is built or modified.
Therefore, a proxy object that wants to delegate some special methods
to an object it's wrapping needs to belong to a
specially made and tailored class. Fortunately, as this recipe shows,
making and instantiating classes on the fly is quite an easy job in
Python.

In this
recipe, we don't use any advanced Python concepts
such as custom metaclasses and custom descriptors. Rather, each proxy
is built by a factory function proxy, which takes
as arguments the object to wrap and the names of special methods to
delegate (shorn of leading and trailing double underscores). If
you've saved the
"Solution"'s code
in a file named proxy.py somewhere along your
Python sys.path, here is how you could use it from
an interactive Python interpreter session:

>>> import proxy
>>> a = proxy.proxy([ ], 'len', 'iter')
# only delegate _ _len_ _ & _ _iter_ _
>>> a # _ _repr_ _ is not delegated
<proxy.listProxy object at 0x0113C370>
>>> a._ _class_ _
<class 'proxy.listProxy'>
>>> a._obj
[ ]
>>> a.append # all non-specials are delegated
<built-in method append of list object at 0x010F1A10>

Since _ _len_ _ is delegated,
len(a) works as expected:

>>> len(a)
0
>>> a.append(23)
>>> len(a)
1

Since _ _iter_ _ is delegated,
for loops work as expected, as does intrinsic
looping performed by built-ins such as list,
sum, max, . . . :

>>> for x in a: print x
...
23
>>> list(a)
[23]
>>> sum(a)
23
>>> max(a)
23

However, since _ _getitem_ _ is
not delegated, a
cannot be indexed nor sliced:

>>> a._ _getitem_ _
<method-wrapper object at 0x010F1AF0>
>>> a[1]
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
TypeError: unindexable object

Function proxy uses a
"cache" of classes it has
previously generated, the global dictionary
known_proxy_classes, keyed by the class of the
object being wrapped and the tuple of special
methods' names being delegated. To make a new class,
proxy calls the built-in type,
passing as arguments the name of the new class (made by appending
'Proxy' to the name of the class being wrapped),
class Proxy as the only base, and an
"empty" class dictionary (since
it's adding no class attributes yet). Base class
Proxy deals with initialization and delegation of
ordinary attribute lookups. Then, factory function
proxy loops over the names of specials to be
delegated: for each of them, it gets the unbound method from the
class of the object being wrapped, and sets it as an attribute of the
new class within a make_binder closure.
make_binder deals with calling the unbound method
with the appropriate first argument (i.e., the object being wrapped,
self._obj).

Once it's done preparing a new class,
proxy saves it in
known_proxy_classes under the appropriate key.
Finally, whether the class was just built or recovered from
known_proxy_classes, proxy
instantiates it, with the object being wrapped as the only argument,
and returns the resulting proxy instance.


See Also


Recipe 6.5 for more
information about automatic delegation; Recipe 6.9 for another example of
generating classes on the fly (using a class
statement rather than a call to type).

/ 394