繁体   English   中英

Python - 绑定变量 scope 到关闭

[英]Python - bound variable scope to closure

我有一些使用外部变量的 function。 一个(基本上)简化的例子:

a = 2
b = 3

def f(x):
    return x * a + b

虽然我需要fab ,但我在其他任何地方都不需要它们。 特别是,可以写a = 5 ,这将改变f的行为。 我应该如何使ab对外部不可见?

其他语言允许我大致编写以下代码:

let f = 
   a = 2
   b = 3
   lambda x: x * a + b

我想要的是:

  • f必须按预期工作并具有相同的签名
  • ab必须只计算一次
  • ab不得存在于f之外的 scope 中
  • 赋值a =...b =...不影响f
  • 最干净的方法来做到这一点。 例如,以下解决方案正式有效,但它引入了g然后将其删除,这是我不喜欢的(例如,存在覆盖现有g的风险,我认为它简直丑陋):
def g():
    a = 2
    b = 3
    return lambda x: x * a + b

f = g()
del g

一种方法是简单地使用 class。 这允许您将ab放置在 class 的 scope 中,而f仍然可以访问它们。

定制 class

class F:
    def __init__(self):
        self.a = 2
        self.b = 3
    
    def __call__(self, x):
        return x * self.a + self.b

f = F()
f(1)
# returns:
5

如果您不喜欢调用 class 构造函数,则可以覆盖__new__以实质上创建一个带有内部存储变量的可调用对象。 虽然这是一个反模式,但不是很pythonic。

自定义可调用

class f:
    a = 2
    b = 3

    def __new__(cls, x):
        return x * cls.a + cls.b

f(1)
# returns:
5

此方法基于此线程中提供的答案,但仅限于上述特定问题。 您可以使用装饰器来更新 function 可用的全局变量,同时还可以将ab存储在闭包中。

带封闭的装饰器

from functools import wraps

def dec_ab(fn):
    a = 2
    b = 3
    @wraps(fn)
    def wrapper(*args, **kwargs):
        # get global scope
        global_scope = f.__globals__

        # copy current values of variables
        var_list = ['a', 'b']
        current_vars = {}
        for var in var_list:
            if var in global_scope:
                current_vars[var] = global_scope.get(var)

        # update global scope
        global_scope.update({'a': a, 'b': b})
        try:
            out = fn(*args, **kwargs)
        finally:
            # undo the changes to the global scope
            for var in var_list:
                global_scope.pop(var)
            global_scope.update(current_vars)

        return out
    return wrapper

@dec_ab
def f(x):
    """hello world"""
    return x * a + b

这保留了函数签名并防止ab被更改

f(1)
# returns: 
5

a
# raises:
NameError: name 'a' is not defined

您可以使用默认的 arguments 来完成此操作。 默认 arguments 仅在创建闭包时计算一次(这就是为什么如果您将可变对象作为默认 arguments,state 在调用之间保留)。

def f(x, a=2, b=3):
    return x * a + b

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM