- 浏览: 333407 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
ProgrammingPower:
非常感谢您,搞定了,哈哈!
Debian下安装CodeBlocks -
hfa1s2:
是的。谢谢你哈! 我解决了这个问题
python模块之smtplib: 用python发送SSL/TLS安全邮件 -
huangro:
587端口的是很麻烦的,毕竟是需要ssl验证的。
python模块之smtplib: 用python发送SSL/TLS安全邮件 -
hfa1s2:
我想利用内网往公司邮箱里发,但接口是587 的 而且需要 SS ...
python模块之smtplib: 用python发送SSL/TLS安全邮件 -
zyb88325:
你好 我现在用的cakephp2.0,因为服务器不支持rewr ...
CakePHP常用技巧总结
David Mertz, Ph.D. (mertz@gnosis.cx ), Developer, Gnosis Software, Inc.
29 Dec 2006
Python made metaprogramming possible, but each Python version has added slightly different -- and not quite compatible -- wrinkles to the way you accomplish metaprogramming tricks. Playing with first-class function objects has long been around, as have techniques for peaking and poking at magic attributes. With version 2.2, Python grew a custom metaclass mechanism that went a long way, but at the cost of melting users' brains. More recently, with version 2.4, Python has grown "decorators," which are the newest -- and by far the most user-friendly way, so far -- to perform most metaprogramming.<!-- START RESERVED FOR FUTURE USE INCLUDE FILES-->
<script type="text/javascript"><!-- // <![CDATA[ capture_referrer(); // ]]> // --></script>
<!-- START : HTML FOR SEARCH REFERRER -->Doing a lot by doing very little
Decorators have something in common with previous metaprogramming abstractions introduced to Python: they do not actually do anything you could not do without them. As Michele Simionato and I pointed out in earlier Charming Python installments , it was possible even in Python 1.5 to manipulate Python class creation without the "metaclass" hook.
Decorators are similar in their ultimate banality. All a decorator does is modify
the function or method that is defined immediately after the decorator. This was
always possible, but the capability was particularly motivated by the introduction
of the classmethod()
and
staticmethod()
built-in functions in Python 2.2. In the
older style, you would use a classmethod()
call, for
example, as follows:
Listing 1. Typical "old style" classmethod
class C: def foo(cls, y): print "classmethod", cls, y foo = classmethod(foo) |
Though classmethod()
is a built-in, there is nothing
unique about it; you could also have "rolled your own" method transforming
function. For example:
Listing 2. Typical "old style" method transform
def enhanced(meth): def new(self, y): print "I am enhanced" return meth(self, y) return new class C: def bar(self, x): print "some method says:", x bar = enhanced(bar) |
All a decorator does is let you avoid repeating the method name, and put the decorator near the first mention of the method in its definition. For example:
Listing 3. Typical "old style" classmethod
class C: @classmethod def foo(cls, y): print "classmethod", cls, y @enhanced def bar(self, x): print "some method says:", x |
Decorators work for regular functions too, in the same manner as for methods in classes. It is surprising just how much easier such a simple, and strictly-speaking unnecessary, change in syntax winds up making things work better, and makes reasoning about programs easier. Decorators can be chained together by listing more than one prior to a function of method definition; good sense urges avoiding chaining too many decorators together, but several are sometimes sensible:
@synchronized @logging def myfunc(arg1, arg2, ...): # ...do something # decorators are equivalent to ending with: # myfunc = synchronized(logging(myfunc)) # Nested in that declaration order |
Being simply syntax sugar, decorators let you shoot yourself in the foot if you are so inclined. A decorator is just a function that takes at least one argument -- it is up to the programmer of the decorator to make sure that what it returns is still a meaningful function or method that does enough of what the original function did for the connection to be useful. For example, a couple of syntactic misuses are:
Listing 5. Bad decorator that does not even return function
>>> def spamdef(fn): ... print "spam, spam, spam" ... >>> @spamdef ... def useful(a, b): ... print a**2 + b**2 ... spam, spam, spam >>> useful(3, 4) Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: 'NoneType' object is not callable |
A decorator might return a function, but one that is not meaningfully associated with the undecorated function:
Listing 6. Decorator whose function ignores passed-in function
>>> def spamrun(fn): ... def sayspam(*args): ... print "spam, spam, spam" ... return sayspam ... >>> @spamrun ... def useful(a, b): ... print a**2 + b**2 ... >>> useful(3,4) spam, spam, spam |
Finally, a better behaved decorator will in some way enhance or modify the action of the undecorated function:
Listing 7. Decorator that modifies behavior of undecorated func
>>> def addspam(fn): ... def new(*args): ... print "spam, spam, spam" ... return fn(*args) ... return new ... >>> @addspam ... def useful(a, b): ... print a**2 + b**2 ... >>> useful(3,4) spam, spam, spam 25 |
You might quibble over just how useful useful()
is,
or whether addspam()
is really such a good
enhancement
, but at least the mechanisms follow the pattern you will
typically see in useful decorators.
|
Introduction to high-level abstraction
Most of what metaclasses are used for, in my experience, is modifying the
methods contained in a class once it is instantiated. Decorators do not currently
let you modify class instantiation per se
, but they can massage the methods
that are attached to the class. This does not let you add or remove methods or
class attributes dynamically during instantiation, but it does let the methods
change their behavior depending on conditions in the environment at runtime. Now
technically, a decorator applies when a class
statement is run, which for top-level classes is closer to "compile time" than to
"runtime." But arranging runtime determination of decorators is as simple as creating a class
factory. For example:
Listing 8. Robust, but deeply nested, decorator
def arg_sayer(what): def what_sayer(meth): def new(self, *args, **kws): print what return meth(self, *args, **kws) return new return what_sayer def FooMaker(word): class Foo(object): @arg_sayer(word) def say(self): pass return Foo() foo1 = FooMaker('this') foo2 = FooMaker('that') print type(foo1),; foo1.say() # prints: <class '__main__.Foo'> this print type(foo2),; foo2.say() # prints: <class '__main__.Foo'> that |
The @arg_sayer()
example goes through a lot of
contortions to obtain a rather limited result, but it is worthwhile for the
several things it illustrates:
- The
Foo.say()
method has different behaviors for different instances. In the example, the difference only amounts to a data value that could easily be varied by other means; but in principle, the decorator could have completely rewritten the method based on runtime decisions.
- The undecorated
Foo.say()
method in this case is a simple placeholder, with the entire behavior determined by the decorator. However, in other cases, the decorator might combine the undecorated method behavior with some new capabilities.
- As already observed, the modification of
Foo.say()
is determined strictly at runtime, via the use of theFooMaker()
class factory. Probably more typical is using decorators on top-level defined classes, which depend only on conditions available at compile-time (which are often adequate).
- The decorator is parameterized. Or rather
arg_sayer()
itself is not really a decorator at all; rather, the function returned byarg_sayer()
, namelywhat_sayer()
, is a decorator function that uses a closure to encapsulate its data. Parameterized decorators are common, but they wind up needed functions nested three-levels deep.
|
Marching into metaclass territory
As mentioned in the last section, decorators could not completely replace the
metaclass hook since they only modify methods rather than add or delete methods.
This is actually not quite true. A decorator, being a Python function, can do
absolutely anything other Python code can. By decorating the
.__new__()
method of a class, even a placeholder
version of it, you can, in fact, change what methods attach to a class. I have not
seen this pattern "in the wild," but I think it has a certain explicitness,
perhaps even as an improvement on the _metaclass_
assignment:
Listing 9. A decorator to add and remove methods
def flaz(self): return 'flaz' # Silly utility method def flam(self): return 'flam' # Another silly method def change_methods(new): "Warning: Only decorate the __new__() method with this decorator" if new.__name__ != '__new__': return new # Return an unchanged method def __new__(cls, *args, **kws): cls.flaz = flaz cls.flam = flam if hasattr(cls, 'say'): del cls.say return super(cls.__class__, cls).__new__(cls, *args, **kws) return __new__ class Foo(object): @change_methods def __new__(): pass def say(self): print "Hi me:", self foo = Foo() print foo.flaz() # prints: flaz foo.say() # AttributeError: 'Foo' object has no attribute 'say' |
In the sample change_methods()
decorator, some fixed
methods are added and removed, fairly pointlessly. A more realistic case would use
some patterns from the previous section. For example, a parameterized decorator
could accept a data structure indicating methods to be added or removed; or
perhaps some feature of the environment like a database query could make this
decision. This manipulation of attached methods could also be wrapped in a
function factory as before, deferring the final decision until runtime. These
latter techniques might even be more versatile than
_metaclass_
assignment. For example, you might call an
enhanced change_methods()
like this:
Listing 10. Enhanced change_methods()
class Foo(object): @change_methods(add=(foo, bar, baz), remove=(fliz, flam)) def __new__(): pass |
|
The most typical examples you will see discussed for decorators can probably be
described as making a function or method "do something extra" while it does its
basic job. For example, on places like the Python Cookbook Web site (see Resources
for a link), you might see
decorators to add capabilities like tracing, logging, memorization/caching, thread
locking, and output redirection. Related to these modifications -- but in a slightly
different spirit -- are "before" and "after" modifications. One interesting
possibility for before/after decoration is checking types of arguments to a
function and the return value from a function. Presumably such a
type_check()
decorator would raise an exception or take
some corrective action if the types are not as expected.
In somewhat the same vein as before/after decorators, I got to thinking about
the "elementwise" application of functions that is characteristic of the R
programming language, and of NumPy
. In these
languages, numeric functions generally apply to each element
in a sequence
of elements, but also to an individual number.
Certainly the map()
function, list-comprehensions,
and more recently generator-comprehensions, let you do elementwise application.
But these require minor workarounds to get R-like behavior: the type of sequence
returned by map()
is always a list; and the call will
fail if you pass it a single element rather than a sequence. For example:
Listing 11. map() call that will fail
>>> from math import sqrt >>> map(sqrt, (4, 16, 25)) [2.0, 4.0, 5.0] >>> map(sqrt, 144) TypeError: argument 2 to map() must support iteration |
It is not hard to create a decorator that "enhances" a regular numerical function:
Listing 12. Converting a function to an elementwise function
def elementwise(fn): def newfn(arg): if hasattr(arg,'__getitem__'): # is a Sequence return type(arg)(map(fn, arg)) else: return fn(arg) return newfn @elementwise def compute(x): return x**3 - 1 print compute(5) # prints: 124 print compute([1,2,3]) # prints: [0, 7, 26] print compute((1,2,3)) # prints: (0, 7, 26) |
It is not hard, of course, to simply write a
compute()
function that builds in the different return
types; the decorator only takes a few lines, after all. But in what might be
described as a nod to aspect-oriented programming, this example lets us
separate concerns
that operate at different levels. We might write a
variety of numeric computation functions and wish to turn them each into
elementwise call models without thinking about the details of argument type
testing and return value type coercion.
The elementwise()
decorator works equally well for
any function that might operate on either an individual thing or on a sequence of
things (while preserving the sequence type). As an exercise, you might try
working out how to allow the same decorated call to also accept and return
iterators (hint: it is easy if you just iterate a completed elementwise
computation, it is less straightforward to do lazily if and only if an iterator
object is passed in).
Most good decorators you will encounter employ much of this paradigm of combining orthogonal concerns. Traditional object-oriented programming, especially in languages like Python that allow multiple inheritance, attempt to modularize concerns with an inheritance hierarchy. However, merely getting some methods from one ancestor, and other methods from other ancestors requires a conception in which concerns are much more separated than they are in aspect-oriented thinking. Taking best advantage of generators involves thinking about issues somewhat differently than does mix-and-matching methods: each method might be made to work in different ways depending on concerns that are outside of the "heart" of the method itself.
|
Before I end this installment, I want to point you to a really
wonderful Python module called decorator
written
by my sometimes co-author Michele Simionato. This module makes developing
decorators much nicer. Having a certain reflexive elegance, the main component of
the decorator
module is a decorator called
decorator()
. A function decorated with
@decorator
can be written in a simpler manner than one
without it (see Resources
for related reading).
Michele has produced quite good documentation of his module, so I will not
attempt to reproduce it; but I would like to point out the basic problems it
solves. There are two main benefits to the
decorator
module. On the one hand, it lets you
write decorators with fewer levels of nesting than you would otherwise need ("flat
is better than nested"); but more interesting possibly is the fact that it makes
decorated functions actually match their undecorated version in metadata, which my
examples have not. For example, recalling the somewhat silly "tracing" decorator
addspam()
that I used above:
Listing 13. How a naive decorator corrupts metadata
>>> def useful(a, b): return a**2 + b**2 >>> useful.__name__ 'useful' >>> from inspect import getargspec >>> getargspec(useful) (['a', 'b'], None, None, None) >>> @addspam ... def useful(a, b): return a**2 + b**2 >>> useful.__name__ 'new' >>> getargspec(useful) ([], 'args', None, None) |
While the decorated function does
its enhanced job, a closer look shows
it is not quite right, especially to code-analysis tools or IDEs that care about
these sorts of details. Using decorator
, we can
improve matters:
Listing 14. Smarter use of decorator
>>> from decorator import decorator >>> @decorator ... def addspam(f, *args, **kws): ... print "spam, spam, spam" ... return f(*args, **kws) >>> @addspam ... def useful(a, b): return a**2 + b**2 >>> useful.__name__ 'useful' >>> getargspec(useful) (['a', 'b'], None, None, None) |
This looks better both to write the decorator in the first place, and in its behavior-preserving metadata. Of course, reading the full incantations that Michele used to develop the module brings you back into brain-melting territory; we can leave that for cosmologists like Dr. Simionato.
Learn
-
The ASPN online Python
Cookbook
is a good source for examples of decorator usage, as well as other
esoteric Python examples.
-
Michele Simionato covers the
decorator
module for Python 2.4, as well as a few changes pertaining to 2.5, in his online Python documentation.
-
For background reading, check out "Metaclass
programming in Python
", a two-part series on developerWorks by David and Michele.
-
Also see the introduction to "Statistical
programming with the R
programming language
", a three-part series on developerWorks by David with Brad
Huntting.
-
Read "Charming
Python: Numerical Python
" (developerWorks, October 2003) on NumPy and, in
passing, its "elementwise" function application.
-
Wikipedia's article on aspect-oriented
programming
is a good place to start if you are unfamiliar with this
concept.
- In the developerWorks Linux zone
,
find more resources for Linux developers.
- Stay current with developerWorks technical events and webcasts .
Get products and technologies
-
Order the SEK for Linux
, a two-DVD set containing the latest IBM trial
software for Linux from DB2®, Lotus®, Rational®, Tivoli®,
and WebSphere®.
- With IBM trial software
, available for download directly from developerWorks,
build your next development project on Linux.
Discuss
Check out developerWorks blogs
and get
involved in the developerWorks community
.
--------------------------------------------------------------------------------------------------------------
以下是中文翻译部分:
2007 年 1 月 23 日
Python 使元编程成为可能,不过每个版本的 Python 都有一些细微的区别(并且不是完全兼容),这使我们实现元编程的道路变得更加崎岖。一类函数对象的使用由来已久,同样还有一些技术用于探索和实现魔术般的属性。在版本 2.2 中,Python 增加了一种很有帮助的定制元类机制,但是其代价就是令用户绞尽脑汁。最近,在 2.4 版本中,Python 增加了 “decorator” ,这是适于执行大部分元编程的最新方式 —— 也是到目前为止对用户最友好的方式。
少劳多得
Decorator 与 Python 之前引入的元编程抽象有着某些共同之处:即使没有这些技术,您也一样可以实现它们所提供的功能。正如 Michele Simionato 和我在 可爱的 Python 专栏的早期文章 中指出的那样,即使在 Python 1.5 中,也可以实现 Python 类的创建,而不需要使用 “元类” 挂钩。
Decorator 根本上的平庸与之非常类似。Decorator 所实现的功能就是修改紧接 Decorator 之后定义的函数和方法。这总是可能的,但这种功能主要是由 Python 2.2 中引入的 classmethod() 和 staticmethod() 内置函数驱动的。在旧式风格中,您可以调用 classmethod(),如下所示:
清单 1. 典型的 “旧式” classmethod
class C:
def foo(cls, y):
print "classmethod", cls, y
foo = classmethod(foo)
虽然 classmethod() 是内置函数,但并无独特之处;您也可以使用自己的方法转换函数。例如:
清单 2. 典型的 “旧式” 方法的转换
def enhanced(meth):
def new(self, y):
print "I am enhanced"
return meth(self, y)
return new
class C:
def bar(self, x):
print "some method says:", x
bar = enhanced(bar)
decorator 所做的一切就是使您避免重复使用方法名,并且将 decorator 放在方法定义中第一处提及其名称的地方。例如:
清单 3. 典型的 “旧式” classmethod
class C:
@classmethod
def foo(cls, y):
print "classmethod", cls, y
@enhanced
def bar(self, x):
print "some method says:", x
decorator 也可以用于正则函数,采用的是与类中的方法相同的方式。令人惊奇的是,这一切是如此简单(严格来说,甚至有些不必要),只需要对语法进行简单修改,所有东西就可以工作得更好,并且使得程序的论证更加轻松。通过在方法定义的函数之前列出多个 decorator,即可将 decorator 链接在一起;良好的判断可以有助于防止将过多 decorator 链接在一起,不过有时候将几个 decorator 链接在一起是有意义的:
清单 4. 链接 decorator
@synchronized
@logging
def myfunc(arg1, arg2, ...):
# ...do something
# decorators are equivalent to ending with:
# myfunc = synchronized(logging(myfunc))
# Nested in that declaration order
Decorator 只是一个语法糖,如果您过于急切,那么它就会使您搬起石头砸了自己的脚。decorator 其实就是一个至少具有一个参数的函数 —— 程序员要负责确保 decorator 的返回内容仍然是一个有意义的函数或方法,并且实现了原函数为使连接有用而做的事情。例如,下面就是 decorator 两个不正确的用法:
清单 5. 没有返回函数的错误 decorator
>>> def spamdef(fn):
... print "spam, spam, spam"
...
>>> @spamdef
... def useful(a, b):
... print a**2 + b**2
...
spam, spam, spam
>>> useful(3, 4)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: 'NoneType' object is not callable
decorator 可能会返回一个函数,但这个函数与未修饰的函数之间不存在有意义的关联:
清单 6. 忽略传入函数的 decorator
>>> def spamrun(fn):
... def sayspam(*args):
... print "spam, spam, spam"
... return sayspam
...
>>> @spamrun
... def useful(a, b):
... print a**2 + b**2
...
>>> useful(3,4)
spam, spam, spam
最后,一个表现更良好的 decorator 可以在某些方面增强或修改未修饰函数的操作:
清单 7. 修改未修饰函数行为的 decorator
>>> def addspam(fn):
... def new(*args):
... print "spam, spam, spam"
... return fn(*args)
... return new
...
>>> @addspam
... def useful(a, b):
... print a**2 + b**2
...
>>> useful(3,4)
spam, spam, spam
25
您可能会质疑,useful() 到底有多么有用?addspam() 真的是那样出色的增强 吗?但这种机制至少符合您通常能在有用的 decorator 中看到的那种模式。
回页首
高级抽象简介
根据我的经验,元类应用最多的场合就是在类实例化之后对类中的方法进行修改。decorator 目前并不允许您修改类实例化本身,但是它们可以修改依附于类的方法。这并不能让您在实例化过程中动态添加或删除方法或类属性,但是它让这些方法可以在运行时根据环境的条件来变更其行为。现在从技术上来说,decorator 是在运行 class 语句时应用的,对于顶级类来说,它更接近于 “编译时” 而非 “运行时”。但是安排 decorator 的运行时决策与创建类工厂一样简单。例如:
清单 8. 健壮但却深度嵌套的 decorator
def arg_sayer(what):
def what_sayer(meth):
def new(self, *args, **kws):
print what
return meth(self, *args, **kws)
return new
return what_sayer
def FooMaker(word):
class Foo(object):
@arg_sayer(word)
def say(self): pass
return Foo()
foo1 = FooMaker('this')
foo2 = FooMaker('that')
print type(foo1),; foo1.say() # prints: <class '__main__.Foo'> this
print type(foo2),; foo2.say() # prints: <class '__main__.Foo'> that
@arg_sayer() 绕了很多弯路,但只获得非常有限的结果,不过对于它所阐明的几方面来说,这是值得的:
Foo.say() 方法对于不同的实例有不同的行为。在这个例子中,不同之处只是一个数据值,可以轻松地通过其他方式改变这个值;不过原则上来说,decorator 可以根据运行时的决策来彻底重写这个方法。
本例中未修饰的 Foo.say() 方法是一个简单的占位符,其整个行为都是由 decorator 决定的。然而,在其他情况下,decorator 可能会将未修饰的方法与一些新功能相结合。
正如我们已经看到的一样,Foo.say() 的修改是通过 FooMaker() 类工厂在运行时严格确定的。可能更加典型的情况是在顶级定义类中使用 decorator,这些类只依赖于编译时可用的条件(这通常就足够了)。
decorator 都是参数化的。或者更确切地说,arg_sayer() 本身根本就不是一个真正的 decorator;arg_sayer() 所返回的 函数 —— what_sayer() 就是一个使用了闭包来封装其数据的 decorator 函数。参数化的 decorator 较为常见,但是它们将所需的函数嵌套为三层。
回页首
迈进元类领域
正如上一节中介绍的一样,decorator 并不能完全取代元类挂钩,因为它们只修改了方法,而未添加或删除方法。实际上,这样说并不完全正确。作为一个 Python 函数,decorator 完全可以实现其他 Python 代码所实现的任何功能。通过修饰一个类的 .__new__() 方法(甚至是其占位符版本),您实际上可以更改附加到该类的方法。尽管尚未在现实中看到这种模式,不过我认为它有着某种必然性,甚至可以作为 _metaclass_ 指派的一项改进:
清单 9. 添加和删除方法的 decorator
def flaz(self): return 'flaz' # Silly utility method
def flam(self): return 'flam' # Another silly method
def change_methods(new):
"Warning: Only decorate the __new__() method with this decorator"
if new.__name__ != '__new__':
return new # Return an unchanged method
def __new__(cls, *args, **kws):
cls.flaz = flaz
cls.flam = flam
if hasattr(cls, 'say'): del cls.say
return super(cls.__class__, cls).__new__(cls, *args, **kws)
return __new__
class Foo(object):
@change_methods
def __new__(): pass
def say(self): print "Hi me:", self
foo = Foo()
print foo.flaz() # prints: flaz
foo.say() # AttributeError: 'Foo' object has no attribute 'say'
在 change_methods() decorator 示例中,我们添加并删除了几个固定的方法,不过这是毫无意义的。在更现实的情况中,应使用上一节中提到的几个模式。例如,参数化的 decorator 可以接受一个能表示要添加或删除的方法的数据结构;或者由数据库查询之类的某些环境特性做出这一决策。这种对附加方法的操作也可以像之前一样打包到一个函数工厂中,这将使最终决策延迟到运行时。这些新兴技术也许比 _metaclass_ 指派更加万能。例如,您可以调用一个增强了的 change_methods(),如下所示:
清单 10. 增强的 change_methods()
class Foo(object):
@change_methods(add=(foo, bar, baz), remove=(fliz, flam))
def __new__(): pass
回页首
修改调用模型
您将看到,有关 decorator 的最典型的例子可能是使一个函数或方法来实现 “其他功能”,同时完成其基本工作。例如,在诸如 Python Cookbook Web 站点(请参见 参考资料 中的链接)之类的地方,您可以看到 decorator 添加了诸如跟踪、日志记录、存储/缓存、线程锁定以及输出重定向之类的功能。与这些修改相关(但实质略有区别)的是修饰 “之前” 和 “之后”。对于修饰之前/之后来说,一种有趣的可能性就是检查传递给函数的参数和函数返回值的类型。如果这些类型并非如我们预期的一样,那么这种 type_check() decorator 就可能会触发一个异常,或者采取一些纠正操作。
与这种 decorator 前/后类似的情况,我想到了 R 编程语言和 NumPy 特有的函数的 “elementwise” 应用。在这些语言中,数学函数通常应用于元素序列中的每个元素,但也会应用于单个数字。
当然,map() 函数、列表内涵(list-comprehension)和最近的生成器内涵(generator-comprehension 都可以让您实现 elementwise 应用。但是这需要较小的工作区来获得类似于 R 语言的行为:map() 所返回的序列类型通常是一个列表;如果您传递的是单个元素而不是一个序列,那么调用将失败。例如:
清单 11. map() 调用失败
>>> from math import sqrt
>>> map(sqrt, (4, 16, 25))
[2.0, 4.0, 5.0]
>>> map(sqrt, 144)
TypeError: argument 2 to map() must support iteration
创建一个可以 “增强” 普通数值函数的 decorator 并不困难:
清单 12. 将函数转换成 elementwise 函数
def elementwise(fn):
def newfn(arg):
if hasattr(arg,'__getitem__'): # is a Sequence
return type(arg)(map(fn, arg))
else:
return fn(arg)
return newfn
@elementwise
def compute(x):
return x**3 - 1
print compute(5) # prints: 124
print compute([1,2,3]) # prints: [0, 7, 26]
print compute((1,2,3)) # prints: (0, 7, 26)
当然,简单地编写一个具有不同返回类型的 compute() 函数并不困难;毕竟 decorator 只需占据几行。但是作为对面向方面编程的一种认可,这个例子让我们可以分离 那些在不同层次上运作的关注事项。我们可以编写各种数值计算函数,希望它们都可转换成 elementwise 调用模型,而不用考虑参数类型测试和返回值类型强制转换的细节。
对于那些对单个事物或事物序列(此时要保留序列类型)进行操作的函数来说,elementwise() decorator 均可同样出色地发挥作用。作为一个练习,您可尝试去解决如何允许相同的修饰后调用来接受和返回迭代器(提示:如果您只是想迭代一次完整的 elementwise 计算,那么当且仅当传入的是一个迭代对象时,才能这样简化一些。)
您将碰到的大多数优秀的 decorator 都在很大程度上采用了这种组合正交关注的范例。传统的面向对象编程,尤其是在诸如 Python 之类允许多重继承的语言中,都会试图使用一个继承层次结构来模块化关注事项。然而,这仅会从一个祖先那里获取一些方法,而从其他祖先那里获取其他方法,因此需要采用一种概念,使关注事项比在面向方面的思想中更加分散。要充分利用生成器,就要考虑一些与混搭方法不同的问题:可以处于方法本身的 “核心” 之外的关注事项为依据,使各 方法以不同方式工作。
回页首
修饰 decorator
在结束本文之前,我想为您介绍一种确实非常出色的 Python 模块,名为 decorator,它是由与我合著过一些图书的 Michele Simionato 编写的。该模块使 decorator 的开发变得更加美妙。decorator 模块的主要组件具有某种自反式的优雅,它是一个称为 decorator() 的 decorator。与未修饰的函数相比,使用 @decorator 修饰过的函数可以通过一种更简单的方式编写。(相关资料请参看 参考资料)。
Michele 已经为自己的模块编写了很好的文档,因此这里不再赘述;不过我非常乐意介绍一下它所解决的基本问题。decorator 模块有两大主要优势。一方面,它使您可以编写出嵌套层次更少的 decorator,如果没有这个模块,您就只能使用更多层次(“平面优于嵌套”);但更加有趣的是这样一个事实:它使得修饰过的函数可以真正地与其在元数据中未修饰的版本相匹配,这是我的例子中没有做到的。例如,回想一下我们上面使用过的简单 “跟踪” decorator addspam():
清单 13. 一个简单的 decorator 是如何造成元数据崩溃的
>>> def useful(a, b): return a**2 + b**2
>>> useful.__name__
'useful'
>>> from inspect import getargspec
>>> getargspec(useful)
(['a', 'b'], None, None, None)
>>> @addspam
... def useful(a, b): return a**2 + b**2
>>> useful.__name__
'new'
>>> getargspec(useful)
([], 'args', None, None)
尽管这个修饰过的函数的确完成 了自己增强过的工作,但若进一步了解,就会发现这并不是完全正确的,尤其是对于那些关心这种细节的代码分析工具或 IDE 来说更是如此。使用 decorator,我们就可以改进这些问题:
清单 14. decorator 更聪明的用法
>>> from decorator import decorator
>>> @decorator
... def addspam(f, *args, **kws):
... print "spam, spam, spam"
... return f(*args, **kws)
>>> @addspam
... def useful(a, b): return a**2 + b**2
>>> useful.__name__
'useful'
>>> getargspec(useful)
(['a', 'b'], None, None, None)
这对于编写 decorator 更加有利,同时,其保留行为的元数据的也更出色了。当然,阅读 Michele 开发这个模块所使用的全部资料会使您回到大脑混沌的世界,我们将这留给 Simionato 博士一样的宇宙学家好了。
参考资料
学习
您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
ASPN online Python Cookbook 是有关 decorator 使用的示例的优秀资源,也提供了其他深奥的 Python 示例。
Michele Simionato 在他的在线 Python 文档中介绍了 Python 2.4 的 decorator 模块,以及与 2.5 版本有关的一些改进。
要阅读一些背景资料,请参看 “Python 中的元类编程”系列,这是 David 和 Michele 在 developerWorks 上撰写的分为两部分的系列文章。
另请阅读有关 “使用 R 编写统计程序”系列的简介,这是 David 和 Brad Huntting 在 developerWorks 上撰写的分为三部分的系列文章。
阅读 “可爱的 Python:Numerical Python”(developerWorks,2003 年 10 月),这篇文章介绍了 NumPy 以及 “elementwise” 函数应用程序的有关内容。
如果您不熟悉这方面的知识,那么 Wikipedia 的有关 面向方面编程 的介绍是一个很好的起点。
在 developerWorks 中国网站 Linux 专区 提供了大量适合 Linux 开发人员的资源。
随时关注 developerWorks 技术活动 和 网络广播。
获得产品和技术
定购 SEK for Linux,共包含两张 DVD,其中有用于 Linux 的最新 IBM 试用软件,包括来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的软件。
利用可直接从 developerWorks 下载的 IBM 试用软件 在 Linux 上构建您的下一个开发项目。
讨论
通过参与 developerWorks blog 加入 developerWorks 社区。
发表评论
-
Trac Data Models and Schema
2010-09-15 10:56 6355The main models that developers ... -
在Mac OS上运行wxPython
2010-09-14 15:42 3215如果在一个64位的Mac上跑wxPython, 将会出现以下错 ... -
Trac SQL Database API
2010-09-14 13:41 1044Trac SQL Database API Trac use ... -
Trac系统相关文档
2010-02-01 12:53 10561. Trac组件架构 http://trac.edgewal ... -
Django上传文件
2009-11-26 18:06 1355之前写了一个上传文件模块,但是当文件很大时就会挂掉,分析了一下 ... -
50个Python重要模块
2009-10-20 09:32 1987原文地址:http://www.cnblogs.com/yd1 ... -
Trac系统的双重认证
2009-10-20 09:27 1142首先,需要导入urllib2及cookielib。 然后,关键 ... -
分享一个图表制作库
2009-06-18 15:15 1314该包名为ChartDirector,可以从其官方主页http: ... -
django newforms的更改
2009-06-16 13:34 1058Django升级之后,有一些应用包的名称也相应发生了变化,比如 ... -
Python版支付宝集成插件源代码
2009-05-08 15:34 4920之前在做一项目的时候需要集成支付宝功能,网站是用python做 ... -
Django多语言问题
2009-05-06 17:22 1911最近多Django比较感兴趣,打算用它来做一个网站,看了官方关 ... -
Installing Satchmo on Webfaction
2009-04-29 15:39 1654link: http://brianmckinney.net/ ... -
Satchmo配置注意事项
2009-04-25 16:10 1028Satchmo在同步数据库之后,在操作后台管理页面过程中,一直 ... -
Yaml与python类型的对照表
2009-04-22 23:03 1699因为常用yaml作为python程序的配置文件,特将其与pyt ... -
satchmo汉化
2009-04-21 15:21 1905转载:http://blog.csdn.net/huliuhe ... -
python模块之smtplib: 用python发送SSL/TLS安全邮件
2008-07-04 19:08 9461原文出自 http://blog.csdn.net/zhaow ... -
python模块之poplib: 用pop3收取邮件
2008-07-04 19:09 2204python的poplib模块是用来 ... -
python模块之email: 电子邮件编码解码 (转)
2008-07-04 19:11 3504用email模块来生成邮件也是很简单的,只是需要一些mime的 ... -
用iconv做通用的语言编码转换(转)
2008-07-04 19:13 1376#include <stdio.h> #in ... -
python模块之email: 电子邮件编码解码(转)
2008-07-04 19:16 3285出处:http://blog.csdn.net/zhaowei ...
相关推荐
Snake Charming – The Musical Python By 作者: Iain Gray ISBN-10 书号: 331960659X ISBN-13 书号: 9783319606590 Edition 版本: 1st ed. 2017 出版日期: 2017-09-22 pages 页数: (136 ) This book is an ...
总结来说,"erwin_charming:Nicos使用CHARMing软件支持ERWIN检测器"涉及到的是一个利用Python开发的软件系统,用于管理和分析ERWIN检测器的数据,提供了丰富的功能,并可能有开源社区的支持。通过深入理解Python编程...
"Charming"在这里可能指的是一个特定的字体家族或者一套字体资源,它旨在为设计工作提供优雅、吸引人的文字样式,以提升整体设计的吸引力和可读性。 首先,我们来探讨一下字体的基本概念。字体是由一系列字符组成的...
《Charming Proofs: A Journey into Elegant Mathematics》是一本由Claudi Alsina和Roger B. Nelsen联合撰写的数学书籍,属于The Mathematical Association of America出版的The Dolciani Mathematical Expositions...
HUtils, a charming python web util-library. 本项目为我司 在后端开发中, 积攒的比较好用的各类基类函数。 除了基础的类型变换, 还有 django 相关的一系列功能。 让我们简单看一段用上了 hutils 以后的效果: ...
接触Python也有一段时间了,Python相关的框架和模块也接触了不少,希望把自己接触到的自己 觉得比较好的设计和实现分享给大家,于是取了一个“Charming Python”的小标,算是给自己开了一个头吧, 希望大家多多批评...
在项目"charming-gecko"中,开发者很可能会遇到以下几个关键概念: 1. Android生命周期:理解Activity如何启动、暂停、恢复和停止是至关重要的,因为这将直接影响到游戏的运行和资源管理。 2. 资源管理:Android...
2 迷人 双端侧向滚动 2D 拼图平台游戏,具有 2 个具有不同能力的可控角色。 方向键移动。 空格键跳跃。 仅限女孩的功能:双击空格键 = 双跳 双击空格键并按住 = 滑行 男孩独有的功能:z 代表冲刺,x 代表攻击
:sparkles:迷人的阿彭 :sparkles:这是一个使用Git作为的网站。它是在不到一分钟的时间内使用创建的。您可以像这样,或探索一些变体。如何不同: :artist_palette:看 :pencil:内容管理系统 :gear:静态网站生成器本地...
A Charming Font
提供java 生成 echarts统计图片 charming2jni
3. car_charming_value:赛车给玩家带来的魅力值增益。 4. car_description:对赛车的详细描述。 5. car_price:赛车的价格。 二、地图(map) 1. map_name:地图的名称,作为主键,同样确保唯一性。 2. star_level:...
隐私政策https://charming-tab.com/search/a0302/privacy.html使用条款https://charming-tab.com/search/a0302/terms.html与我们联系https://charming-tab.com /search/a0302/contact.html关于我们...
this is a kind of charming software to make you charming
- `Charming`:表示男性具有吸引力和魅力。 - `A caring listener`:赞美男性善于倾听他人的需求。 - `Witty`:聪明且风趣幽默。 - `In good shape`:身材健硕,身体健康。 - `Dresses with style`:着装时尚,...
_基于Pierre_Dellacherie算法实现俄罗斯方块的人工智能_TetrisAI-By-Charming
12. **迷人的**: "fascinating"或"charming"。 13. **(室)地板,楼层**: "floor"。 14. **发展成为**: "develop into"。 15. **靠近,接近**: "approach"。 16. **使挨饿,饿死**: "starve"。 17. **打仗,争论**: ...
:sparkles: 迷人辣椒粉 :sparkles: 这是一个使用Git作为的网站。 它是在不到一分钟的时间内使用创建的。 您可以像这样,或探索一些变体。 如何不同: :artist_palette: 看 :pencil: 内容管理系统 ...