为什么Python装饰器而不是闭包?

int*_*tar 16 python decorator

我仍然没有掌握Python中的装饰器.

我已经开始使用很多闭包来做自定义函数和编码中的类.

例如.

class Node :
    def __init__(self,val,children) :
        self.val = val
        self.children = children

def makeRunner(f) :
    def run(node) :
        f(node)
        for x in node.children :
            run(x)
    return run

tree=Node(1,[Node(2,[]),Node(3,[Node(4,[]),Node(5,[])])])

def pp(n) : print "%s," % n.val
printTree = makeRunner(pp)
printTree(tree)
Run Code Online (Sandbox Code Playgroud)

据我所知,装饰器只是用于做类似事情的不同语法.

代替

def pp(n) : print "%s," % n.val
printTree = makeRunner(pp)
Run Code Online (Sandbox Code Playgroud)

我会写:

@makeRunner
def printTree(n) : print "%s," % n.val
Run Code Online (Sandbox Code Playgroud)

这一切都是装饰者吗?或者我错过了一个根本的区别?

Dut*_*ers 13

虽然在语法上,装饰器只是"糖",但这不是考虑它们的最佳方式.

装饰器允许您将功能编织到现有代码中,而无需实际修改它.它们允许您以声明性的方式执行此操作.

这允许您使用装饰器进行面向方面编程(AOP).因此,当您想要封装在一个地方的横切关注时,您希望使用装饰器.

典型的示例可能是日志记录,您要记录函数的入口或出口,或两者.使用装饰器相当于将建议(记录此!)应用于连接点(在方法输入或退出期间).

方法修饰是一种概念,如OOP或列表推导.正如您所指出的,它并不总是合适的,并且可能被过度使用.但是在正确的位置,它可以使代码更加模块化和分离.


tzo*_*zot 8

您的示例是真实代码还是仅仅是示例?

如果它们是真正的代码,我认为你过度使用装饰器,可能是因为你的背景(即你习惯了其他编程语言)

第1阶段:避免装饰者

def run(rootnode, func):
    def _run(node): # recursive internal function
        func(node)
        for x in node.children:
            _run(x) # recurse
    _run(rootnode) # initial run
Run Code Online (Sandbox Code Playgroud)

此run方法废弃了makeRunner.你的例子转向:

def pp(n): print "%s," % n.val
run(tree, pp)
Run Code Online (Sandbox Code Playgroud)

但是,这完全忽略了发电机,所以......

第2阶段:使用发电机

class Node :
    def __init__(self,val,children) :
        self.val = val
        self.children = children

    def __iter__(self): # recursive
        yield self
        for child in self.children:
            for item in child: # recurse
                yield item

def run(rootnode, func):
    for node in rootnode:
        func(node)
Run Code Online (Sandbox Code Playgroud)

你的榜样仍然存在

def pp(n): print "%s," % n.val
run(tree, pp)
Run Code Online (Sandbox Code Playgroud)

请注意,特殊方法__iter__允许我们使用for node in rootnode:构造.如果您不喜欢它,只需将__iter__方法重命名为eg walker,并将run循环更改为:for node in rootnode.walker():
显然,该run函数可能是一种class Node替代方法.

如您所见,我建议您直接使用run(tree, func)而不是将它们绑定到名称printTree,但您可以在装饰器中使用它们,或者您可以使用该functools.partial函数:

printTree= functools.partial(run, func=pp)
Run Code Online (Sandbox Code Playgroud)

从那以后,你会的

printTree(tree)
Run Code Online (Sandbox Code Playgroud)


Che*_* A. 7

一般意义上的装饰器是包装另一个对象、扩展或装饰该对象的函数或类。装饰器支持与包装函数或对象相同的接口,因此接收者甚至不知道该对象已被装饰。

是一个匿名函数,它引用其参数或其范围之外的其他变量。

所以基本上,装饰器使用闭包,而不是替换它们。

def increment(x):
    return x + 1

def double_increment(func):
    def wrapper(x):
        print 'decorator executed'
        r = func(x)   # --> func is saved in __closure__
        y = r * 2
        return r, y
    return wrapper

@double_increment
def increment(x):
    return x + 1

>>> increment(2)
decorator executed
(3, 6)

>>> increment.__closure__
(<cell at 0x02C7DC50: function object at 0x02C85DB0>,)

>>> increment.__closure__[0].cell_contents 
<function increment at 0x02C85DB0>
Run Code Online (Sandbox Code Playgroud)

所以装饰器用闭包保存了原始函数。