Charming Python: Decorators make magic easy

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 -->
<!-- START : HTML FOR ARTICLE SEARCH --> <!-- END : HTML FOR ARTICLE SEARCH --> <!-- START : HTML FOR CODE SEARCH --> <!-- END : HTML FOR CODE SEARCH -->

<!-- END : HTML FOR SEARCH REFERRER --><!-- END RESERVED FOR FUTURE USE INCLUDE FILES-->

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:


Listing 4. Chained decorators

        
@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.

 


Back to top

 

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 the FooMaker() 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 by arg_sayer() , namely what_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.

 


Back to top

 

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

 


Back to top

 

Changing a call model

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.

 


Back to top

 

Decorating your decorators

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.

 

Resources

Learn


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 社区。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于微信小程序的家政服务预约系统采用PHP语言和微信小程序技术,数据库采用Mysql,运行软件为微信开发者工具。本系统实现了管理员和客户、员工三个角色的功能。管理员的功能为客户管理、员工管理、家政服务管理、服务预约管理、员工风采管理、客户需求管理、接单管理等。客户的功能为查看家政服务进行预约和发布自己的需求以及管理预约信息和接单信息等。员工可以查看预约信息和进行接单。本系统实现了网上预约家政服务的流程化管理,可以帮助工作人员的管理工作和帮助客户查询家政服务的相关信息,改变了客户找家政服务的方式,提高了预约家政服务的效率。 本系统是针对网上预约家政服务开发的工作管理系统,包括到所有的工作内容。可以使网上预约家政服务的工作合理化和流程化。本系统包括手机端设计和电脑端设计,有界面和数据库。本系统的使用角色分为管理员和客户、员工三个身份。管理员可以管理系统里的所有信息。员工可以发布服务信息和查询客户的需求进行接单。客户可以发布需求和预约家政服务以及管理预约信息、接单信息。 本功能可以实现家政服务信息的查询和删除,管理员添加家政服务信息功能填写正确的信息就可以实现家政服务信息的添加,点击家政服务信息管理功能可以看到基于微信小程序的家政服务预约系统里所有家政服务的信息,在添加家政服务信息的界面里需要填写标题信息,当信息填写不正确就会造成家政服务信息添加失败。员工风采信息可以使客户更好的了解员工。员工风采信息管理的流程为,管理员点击员工风采信息管理功能,查看员工风采信息,点击员工风采信息添加功能,输入员工风采信息然后点击提交按钮就可以完成员工风采信息的添加。客户需求信息关系着客户的家政服务预约,管理员可以查询和修改客户需求信息,还可以查看客户需求的添加时间。接单信息属于本系统里的核心数据,管理员可以对接单的信息进行查询。本功能设计的目的可以使家政服务进行及时的安排。管理员可以查询员工信息,可以进行修改删除。 客户可以查看自己的预约和修改自己的资料并发布需求以及管理接单信息等。 在首页里可以看到管理员添加和管理的信息,客户可以在首页里进行家政服务的预约和公司介绍信息的了解。 员工可以查询客户需求进行接单以及管理家政服务信息和留言信息、收藏信息等。
数字社区解决方案是一套综合性的系统,旨在通过新基建实现社区的数字化转型,打通智慧城市建设的"最后一公里"。该方案以国家政策为背景,响应了国务院、公安部和中央政法会议的号召,强调了社会治安防控体系的建设以及社区治理创新的重要性。 该方案的建设标准由中央综治办牵头,采用"9+X"模式,通过信息采集、案(事)件流转等手段,实现五级信息中心的互联互通,提升综治工作的可预见性、精确性和高效性。然而,当前社区面临信息化管理手段不足、安全隐患、人员动向难以掌握和数据资源融合难等问题。 为了解决这些问题,数字社区建设目标提出了"通-治-服"的治理理念,通过街道社区、区政府、公安部门和居民的共同努力,实现社区的平安、幸福和便捷。建设思路围绕"3+N"模式,即人工智能、物联网和数据资源,结合态势感知、业务分析和指挥调度,构建起一个全面的数据支持系统。 数字社区的治理体系通过"一张图"实现社区内各维度的综合态势可视化,"一套表"进行业务分析,"一张网"完成指挥调度。这些工具共同提升了社区治理的智能化和效率。同时,数字社区还提供了包括智慧通行、智慧环保、居家养老和便民服务等在内的多样化数字服务,旨在提升居民的生活质量。 在硬件方面,数字社区拥有IOT物联网边缘网关盒子和AI边缘分析盒子,这些设备能够快速集成老旧小区的物联设备,实现传统摄像设备的智能化改造。平台优势体现在数字化能力中台和多样化的应用,支持云、边、端的协同工作,实现模块化集成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值