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

2025 居民消费结构数据集(21列,15000条记录)XLSX 详情介绍:该数据集为 2025 年 12 个月、1250 户家庭的平衡面板消费数据,15000 行 ×21 列零缺失,覆盖 31 省、城乡均衡,含八大类消费支出和恩格尔系数(均值 0.293,属富裕区间),家庭月收入均值 8473 元、月支出 6487 元,附完整字段说明 sheet,数据干净规整,非常适合做消费结构分析、面板回归、城乡 / 区域差异对比和收入等级分类预测,但为合成数据、仅 1 年时间跨度,不适合做真实政策研究或长期趋势分析。 立即下载

相关推荐

jsprit 是一个基于 Java 的开源工具包

jsprit 是一个基于 Java 的开源工具包,用于解决复杂的 旅行商问题 (TSP) 和 车辆路径问题 (VRP)。 它轻量、灵活且易于使用,基于单一的通用 元启发式算法,目前可解决以下问题: 带容量约束的车辆路径问题 (Capacitated VRP) 多 depot 车辆路径问题 (Multiple Depot VRP) 带时间窗的车辆路径问题 (VRP with Time Windows) 带回程的车辆路径问题 (VRP with Backhauls) 带取送货的车辆路径问题 (VRP with Pickups and Deliveries) 带异构车队的车辆路径问题 (VRP with Heterogeneous Fleet) 时间依赖型车辆路径问题 (Time-dependent VRP) 旅行商问题 (Traveling Salesman Problem) 拨号乘车问题 (Dial-a-Ride Problem) 上述各类问题的多种组合

【Python入门】超详细Python+Pycharm安装保姆级教程,Python环境配置和使用指南,看完这一篇就够了

这两年被Python初学小白问到最多的问题就是,该用什么代码编辑工具?说实话,我个人是用Jupyter Notebook最多,主要是经常做数据可视化,方便些。但对于初学者来说,PyCharm仍是不二的选择,甚至我建议你只用PyCharm.从当前所有主流Python IDE来看,PyCharm是最适合做Python开发的,特别对新手而言,可以节省很多不必要的时间成本。那么具体如何使用呢?

2301_76161259的博客 4011

2003-2025年 政府创新补贴与非创新补贴 xlsx

整理了A股上市公司的政府补助微观样本,核心涵盖股票代码、公司简称、年份、创新补助总额及非创新补助总额五大关键变量,企业研发创新的专项补助与其他非创新类补助。参考郭玥(2018)《政府创新补助的信号传递机制与企业创新》一文的方法论与分类标准,通过关键词检索与明细人工核对相结合的方式,从繁杂的年度补助明细中精准剥离出创新与非创新补助,对数据进行了校验与异常值剔除。 指标 股票代码 公司简称 年份 创新补助总额 非创新补助总额

Charming Python: Review of Python IDEs

Python development made (even) easierLevel: IntroductoryDavid Mertz, Ph.D. (mertz@gnosis.cx)Developer, Gold Software Systems11 Dec 2003David looks at four open source development environ

天道酬勤 1933

YOLO26算法手势识别+训练好的模型+180张数据集+pyqt可视化界面.zip

附训练好的 YOLO 权重与 PyQt 可视化检测界面,页面底部含可视化效果预览,毕设/课设开箱即用。 【数据集概况】 · 检测类别(中文):[手动oYkW(hand oYkW)] 【训练曲线与评估图】 【模型训练配置】 参数 | 值 模型 | yolo26n 训练轮数 | 100 epochs 输入尺寸 | 640x640 批次大小 | 24 优化器 | auto 初始学习率 | 0.01 训练设备 【关键指标汇总】 训练了 97 个 epoch,最终轮指标: 指标 | 数值 mAP50 | **0.9950** mAP50-95 | 0.9885 Precision | 1.0000 Recall | 0.9963 train/box_loss | 0.2129 train/cls_loss | 0.2843 val/box_loss | 0.1899 val/cls_loss | 0.3037 【训练过程分析】 自从开始着手于手势识别任务,我每天都在反复调参、观察曲线变化,97 轮训练结束后,尽管 mAP50 为 0.9950,模型整体表现还算满意,但细细推敲起来,似乎还有些问题尚未完全解决。特别是在前 50 轮,训练 loss 曲线表现得异常平稳,几乎没有大的波动,让我感觉有点枯燥。然而,到了后半段,模型并没有像我期待的那样继续稳定上升,而是出现了一点平台期。尽管 val_loss 没有明显反弹,但也未能继续下降,这让人不禁担心是否已经接近极限。从这个阶段起,mAP50-95 和其他的一些细节开始慢慢露出端倪。尽管整体 mAP50 达到 0.9950,但具体到每一类别的表现却不尽相同。在训练过程中,我注意到框的定位精度确实很高,但仍有提升空间。在训练过程中,我调整过学习率和优化器,希望能达到更好的... 使用场景:毕业设计、课程设计、深度学习入门、工业场景落地验证。

配合上述本地模型资源文件

配合上述本地模型资源文件

高校技术转移办公室人员如何提升技术成果的市场对接效率?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

三电平维也纳VIENNA双闭环控制整流器仿真(Simulink仿真实现)

内容概要:本文档系统性地介绍了以“三电平维也纳VIENNA双闭环控制整流器”为核心的电力电子系统Simulink仿真资源,聚焦于电压电流双闭环控制、中点电位平衡、空间矢量脉宽调制(SVPWM)等关键技术的建模与实现。资源包不仅提供完整的仿真模型,还配套Mathcad计算书、设计报告及参考文献,帮助用户从理论推导到参数设计再到仿真验证实现全流程贯通。同时,文档整合了大量相关拓扑(如DAB、LLC、PFC、CLLC等)与先进控制策略(如EPS调制、软开关、最小应力优化、模型预测控制等)的仿真案例,覆盖从基础整流器到复杂双向变换系统的多个典型应用场景,构成一个面向科研、教学与工程实践的综合性学习体系。; 适合人群:具备电力电子、自动控制或电气工程等相关专业背景,熟悉MATLAB/Simulink仿真环境,正在从事新能源变换、电能质量治理、电机驱动或电力系统方向研究的研究生、高校教师及企业研发工程师。; 使用场景及目标:①开展三电平VIENNA整流器、双有源桥DAB、LLC谐振变换器等先进拓扑的科研建模与论文复现;②完成课程设计、毕业设计或实际工程项目中的控制器开发与系统性能验证;③深入掌握双闭环控制架构、中点电位平衡机制、SVPWM调制策略及系统稳定性分析等核心知识点; 阅读建议:建议结合Mathcad计算书进行系统参数设计,理解控制算法的数学基础,并在Simulink中逐步搭建与调试模型,注重理论计算与仿真结果的对比分析,推荐按照资源目录顺序循序渐进学习,以建立完整的知识框架和技术实现能力。

国央企创新负责人如何借助平台实现技术供需的智能匹配与高效对接?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

三相充电桩维也纳PFC+三电平LLC谐振变换器三电平PSIM仿真设计(Simulink仿真实现)

内容概要:本文档聚焦于电力电子与电力系统领域的仿真设计,重点围绕三相充电桩中维也纳PFC(Vienna Power Factor Correction)整流器与三电平LLC谐振变换器的联合系统展开,提供基于PSIM和Simulink平台的完整仿真设计方案。内容涵盖9kW三相三开关维也纳PFC整流器的Mathcad计算书、双闭环控制策略、中点电压均衡控制、SVPWM调制技术及电流过零畸变消除方法等关键技术环节,并配套详细的仿真模型与设计报告。此外,文档整合了丰富的电力电子拓扑资源,包括图腾柱PFC、交错并联PFC、双有源桥DAB、CLLC谐振变换器、双向Buck-Boost等典型电路结构,覆盖Matlab/Simulink、Plecs、PSIM等多种仿真环境,形成从理论计算、建模、控制算法设计到性能优化的一体化技术体系。; 适合人群:面向具备电力电子、电气工程、自动化等相关专业背景,熟悉Matlab/Simulink/PSIM等仿真工具,从事新能源汽车充电技术、高效率AC-DC/DC-DC变换器设计、电力系统能量管理等方向的研究生、科研人员及工程技术人员。; 使用场景及目标:①支持三相高功率因数校正与高效DC-DC变换系统的科研仿真与教学实践;②用于PI控制、前馈控制、SVPWM、软开关技术、中点电位平衡、双闭环动态响应等核心控制算法的研究与验证;③服务于毕业设计、科研课题、学术论文复现及实际工程项目开发,提升系统级建模与优化能力。; 阅读建议:建议结合Mathcad计算书与仿真模型同步学习,优先掌握维也纳PFC与LLC的基本工作原理与参数设计流程,再深入理解双闭环控制系统架构与关键控制策略,推荐先运行Simulink模型观察动态波形响应,并参考附带文献深化理论认知与工程实现能力。

政府科技管理者在推动科技成果转移转化时,如何高效识别具有高转化价值的科研成果.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

三电平Buck直流变换器双闭环、单闭环、开环仿真【附参考文献】(Simulink仿真实现)

内容概要:本文详细介绍了基于Simulink平台实现的三电平Buck直流变换器的开环、单闭环及双闭环控制系统的建模、仿真与性能分析全过程,涵盖了系统原理、控制策略设计、仿真模型构建及结果验证,并提供了参考文献以支持理论依据。重点对三种控制方式(开环、单闭环、双闭环)在动态响应速度、稳态精度、抗负载扰动能力等方面的性能进行了对比分析,深入揭示了多电平变换器在不同控制结构下的运行特性与优化潜力,有助于理解先进控制策略在电力电子变换器中的应用价值。; 适合人群:电力电子、自动化、电气工程及其相关专业的高校本科生、研究生,以及从事新能源电源系统、直流变换器设计与控制研究的工程技术人员。; 使用场景及目标:①掌握三电平Buck变换器的基本工作原理与数学建模方法;②学习并实践开环、单闭环与双闭环控制策略的设计流程与参数整定技巧;③通过仿真对比不同控制结构的性能差异,为科研项目、毕业设计或工业产品开发提供技术参考与实现基础; 阅读建议:建议读者结合Simulink仿真模型同步操作,动手搭建各控制结构,深入理解控制器设计逻辑与系统动态行为的关系,同时查阅文中所列参考文献以深化对控制理论与电力电子技术融合的理解。

国央企创新负责人在推动内部技术转化时,如何高效识别优质专利资源?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

三相电压型PWM整流器PI双闭环控制Matlab仿真

内容概要:本文围绕三相电压型PWM整流器的PI双闭环控制展开,详细介绍基于Matlab/Simulink平台的控制系统建模与仿真方法,重点实现电压外环与电流内环的双闭环PI控制器设计,以提升系统的动态响应性能、稳态精度及抗干扰能力。文档不仅涵盖PWM整流器的核心控制策略,还系统整合了多种典型电力电子变换器的仿真案例,如维也纳(Vienna)整流器、LLC谐振变换器、Buck/Boost电路、图腾柱PFC等,涉及中点电压均衡、电流过零畸变抑制、SVPWM调制、功率因数校正、电网不平衡下的功率平衡控制等关键技术。配套提供的Mathcad计算书、仿真模型与参考文献,构建了一套完整的理论分析、参数设计与仿真验证一体化的学习与研究体系; 适合人群:电气工程、自动化、电力电子与新能源等相关专业的高校本科生、研究生,以及从事电力变换系统、新能源并网、电能质量治理等领域研发工作的工程技术人员; 使用场景及目标:①作为高校课程设计、毕业设计或科研项目的重要参考资料,深入理解PWM整流器双闭环控制原理与实现方法;②掌握PI控制器设计、dq坐标系变换、锁相环(PLL)、SVPWM调制等核心技术在实际系统中的综合应用;③为工业级高效电能转换系统(如充电桩、储能变流器、光伏逆变器)的研发提供理论支撑与仿真原型基础; 阅读建议:建议结合Simulink仿真模型与Mathcad计算书同步学习,注重控制算法的手动推导与控制器参数整定过程,深入理解各模块之间的耦合关系,并参考文中多种拓扑结构与先进控制策略,拓展对现代电力电子系统设计的认知深度与创新能力。

产业园区在推动科技成果转化时,如何精准匹配企业与技术供给方?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

849d2910791d3ac603e873835a8296fc_7513595024160782007_m.zip

849d2910791d3ac603e873835a8296fc_7513595024160782007_m.zip

hid_test_20261109_120326.log

hid_test_20261109_120326.log

Unity详细介绍说明限制Unity帧率的方式

在Unity开发中,帧率(FPS,Frames per Second)是一个非常重要的表现标准,它直接影响了游戏运行的流畅度。在某些应用场景里,我们可能需要手动限制帧率。限制帧率可以减少电脑的CPU和GPU负载,使运行更加稳定,同时对于移动设备来说还能有效降低电量消耗。下面就详细介绍几种限制Unity中帧率的方式。

zjw1349547081的专栏 5435
上一篇: Charming Python: Decorators make magic easy
下一篇: Deferred Object In Twisted
pythoner
博客等级 码龄18年 15粉丝 89原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值