【问题标题】:Pythonic way to create closures创建闭包的 Pythonic 方式
【发布时间】:2016-06-05 22:46:38
【问题描述】:

我的代码存在于 GUI 外部的文件中,但会生成由 GUI 调用的方法。例如,此文件包含如下所示的函数:

# old code
def fixDictionary(dictionary, key, new_value):
    def fix():
        dictionary[key] = new_value
    return fix

dictionary 包装在闭包中的一般方法效果很好,但这种方法会导致大量样板代码用于创建无参数函数。我做了一个简单的装饰器来为我做这件事,如下所示。

# new code
from functools import wraps

def strip_args(function):
    def outer(*args, **kwargs):

        @wraps(function)
        def inner():
            function(*args, **kwargs)
        return inner

    return outer

@strip_args
def fixDictionary(dictionary, key, new_value):
    dictionary[key] = new_value

作为参考,这个函数的用法如下:

dictionary = {'key': 'old_value'}

fixer = fixDictionary(dictionary, 'key', 'new_value')
fixer()

print(dictionary) # {'key': 'new_value'}

然后,我的代码中还有一堆方法,如下所示:

# old code
def checkDictionary(dictionary):
    errors = []
    for key, value in dictionary.items():
        if value == 'old_value':
            error.append(fixDictionary(dictionary, key, 'new_value'))
    return errors

如果不清楚,这些方法会检查对象的错误,然后返回 GUI 可以调用的匿名函数以纠正这些错误。但是,所有这些方法都会初始化一个空白容器,向其中添加项目,然后将其返回。为了去除所有这些函数中的重复代码,我又写了一个装饰器:

# new code
def init_and_return(**init_dict):
    if len(init_dict) != 1:
        raise ValueError('Exactly one "name=type" pair should be supplied')

    _name, _type = init_dict.items()[0]

    def outer(function):

        @wraps(function)
        def inner(*args, **kwargs):
            _value = _type()
            function.func_globals[_name] = _value
            function(*args, **kwargs)
            return _value
        return inner

    return outer

@init_and_return(errors=list)
def checkDictionary(dictionary):
    for key, value in dictionary.items():
        if value == 'old_value':
            errors.append(fixDictionary(dictionary, key, 'new_value'))

现在,最终的用法如下所示:

dictionary = {'key': 'old_value'}
errors = checkDictionary(dictionary) # [<function fixDictionary at 0x01806C30>]
errors[0]()
print(dictionary) # {'key': 'new_value'}

这也很有效,并且让我也可以避免为这些函数编写更多样板。关于上述实现,我有两个问题:

  1. 是否有更 Pythonic 的方式来实现此功能?目的是消除每个函数中的所有样板代码,但编写函数strip_argsinit_and_return 肯定会让大脑感到紧张。虽然这样的函数不应该经常编写,但它们似乎与它们的实际行为相去甚远。
  2. function.func_globals[_name] = _value 行有不良行为;它允许从全局范围访问errors。这不是世界末日,因为每次调用函数时都会重置变量,但是无论如何我可以设置一个局部变量吗?我尝试将这一行更改为locals()[_name] = _value,但范围并没有传递到该函数。这种级别的元编程是否超出了 Python 的预期范围?

【问题讨论】:

    标签: python python-2.7 closures metaprogramming decorator


    【解决方案1】:

    我想出了一种方法来解决我的第二个问题,方法是在init_and_return 函数中实现一些簿记代码,检查它是否覆盖了全局变量,如果是,则将其恢复(如果不是,则将其删除)。

    def init_and_return(**init_dict):
    
        # this could be extended to allow for more than one k-v argument
        if len(init_dict) != 1:
            raise ValueError('Exactly one "name=type" pair should be supplied')
    
        _name, _type = init_dict.items()[0]
        def outer(function):
    
            @wraps(function)
            def inner(*args, **kwargs):
    
                # instantiate a new container
                _value = _type()
    
                # used to roll-back the original global variable
                _backup, _check = None, False
    
                # store original global variable (if it exists)
                if _name in function.func_globals:
                    _backup = function.func_globals[_name]
                    _check = True
    
                # add container to global scope
                function.func_globals[_name] = _value
    
                function(*args, **kwargs)
    
                # roll-back if it existed beforehand, delete otherwise
                if _check:
                    function.func_globals[_name] = _backup
                else:
                    del function.func_globals[_name]
    
                return _value
            return inner
        return outer
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-02
      • 1970-01-01
      • 1970-01-01
      • 2010-10-29
      • 2019-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多