闭包,部分和装饰

Dar*_*ary 6 python

我三者之间很困惑.我知道闭包是由另一个函数返回的函数,并且可以从封闭范围访问局部变量

例:

def add_nums(one):
    def adder(two):
        return one+two
    return adder

a_10 = add_nums(10)
print a_10(5)
15
Run Code Online (Sandbox Code Playgroud)

在这里,adder是一个封闭.

但是,这也不是一个例子 partial

from functools import partial
a_10 = partial(add_nums, 10)
print a_10()(5)
15
Run Code Online (Sandbox Code Playgroud)

两者有什么区别?

此外,装饰器用于向函数添加功能.

def add_nums(one):
    def adder(two):
        print "foo, bar"
        return one+two
    return adder

a_10 = add_nums(10)
print a_10(5)
foo, bar
15
Run Code Online (Sandbox Code Playgroud)

这三者有什么区别?

Kev*_*ase 7

简短回答: 闭包是机制,而functools.partial装饰器是该机制的典型用途.

闭包和更典型的命名空间之间的关键区别在于,当控制流离开顶级函数时,"封闭"命名空间中的名称和值不会消失.它们保存在与内部函数的一个实例相关联的迷你命名空间中,并且只要该实例存在,它就能存活下来.

functools.partial使用该能力"记住"预先存在的函数的一些参数.装饰者通常也出于同样的原因使用该能力.

请注意,装饰器比这更灵活.任何带有一个参数并返回一些东西的可调用都可以作为装饰器.它不具有充分利用Python的倒闭,甚至返回一个函数.许多装饰器返​​回一个可调用的对象.(另外,可以使用命名空间的对象和内部函数的方法来模拟闭包和内部函数.)

无论装饰器返回什么,都会分配给装饰函数所具有的名称.(在它们之后带有括号的装饰器,就像@decorator('args') ...稍微复杂一点.)典型的装饰器语法:

@decorator
def function():
    pass
Run Code Online (Sandbox Code Playgroud)

...只是"定义,然后装饰和重新分配"的简写:

def function():
    pass
function = decorator(function)
Run Code Online (Sandbox Code Playgroud)

对于一个极端(并且几乎无用)的例子:

def decorator5(__):
   return 5

@decorator5
def square(x):
    return x * x

print(square)  # Prints 5 --- the function is gone.
square(10)     # TypeError: 'int' object is not callable
Run Code Online (Sandbox Code Playgroud)


agh*_*ast 6

我认为您将实现目的混淆了创建一个封闭是一种做事的技术。您可以使用闭包来做很多事情。

在另一方面,partial并且decorator有特殊用途。也许他们使用闭包。也许他们没有。这是一个实现细节,您无需担心。重要的是他们要达到您想要的结果。

考虑一个partial。(忽略** kwargs。)创建它的一种方法是使用闭包:

def partial(f, *args):
    def pf(*rest):
        return f(*args, *rest)
    return pf
Run Code Online (Sandbox Code Playgroud)

但这不是必须的。例如:

class Partial:
    def __init__(self, func, args):
        self.args = args
        self.func = func

    def __call__(self, *rest):
        f = self.func
        args = self.args
        return f(*args, *rest)

def partial(f, *args):
    return Partial(f, args)
Run Code Online (Sandbox Code Playgroud)

这里没有闭包,只有一个变量包含对其他变量的引用。但是我有部分行为,那么谁在乎呢?

装饰者也是如此。装饰器可能是闭包,也可能不是。例如,最近的一个问题涉及str.format在函数的__doc__字符串上运行。那只是接受功能对象,修改__doc__属性并返回相同对象的一种情况。显然,这不涉及任何关闭。


jon*_*rpe 2

你的装饰器的部分版本实际上是:

def add_nums(one):
    def adder(one, two):
        return one + two
    return partial(adder, one)
Run Code Online (Sandbox Code Playgroud)

请注意,嵌套函数现在使用显式参数,而不是闭包。您可以实现一个装饰器,它只是一个函数,它接受一个函数并返回一个(通常不同的)函数,使用闭包partial使用闭包实现)或两者的混合。