【问题标题】:How to freeze mutable default parameter values?如何冻结可变的默认参数值?
【发布时间】:2019-12-01 14:14:28
【问题描述】:

Python 选择遵循只评估函数签名一次的设计决策。所以这段代码:

def test(x=[]):
    x.append(5)
    return x

print(test())
print(test())

打印:

[5]

[5, 5]

我知道可以使用None作为默认值,如果是None.,可以更改函数内部的参数值


我如何编写一些代码来改变test 的行为,从而在每次调用该函数时重新计算默认参数?该解决方案可以包含装饰器并使用类作为默认值。

【问题讨论】:

    标签: python python-3.x function metaprogramming mutability


    【解决方案1】:

    我们可以使用函数的__default__ 属性来“冻结”默认值(并使用__kwargs__ 来“冻结”默认关键字参数):

    def freeze_defaults(func):
         defaults = func.__defaults__
         kwdefaults = func.__kwdefaults__
         @functools.wraps(func) 
         def wrapper(*args, **kwargs): 
             func.__defaults__ = copy.deepcopy(defaults) 
             func.__kwdefaults__ = copy.deepcopy(kwdefaults)
             return func(*args, **kwargs) 
         return wrapper
    

    现在:

    @freeze_defaults
    def test(x=[]):
        x.append(5)
        return x
    
    print(test())
    print(test())
    

    打印:

    [5]

    [5]

    甚至:

    @freeze_defaults
    def test(x=['hello!']):
        x.append(5)
        return x
    
    print(test())
    print(test())
    

    打印:

    ['你好!',5]

    ['你好!',5]

    【讨论】:

    • 这引入了一个线程安全错误。如果另一个线程在func.__defaults__ 分配和对func 的调用之间中断wrapper,则两个线程可能最终使用相同的__defaults__Here's a demonstration,使用 time.sleep 调用让线程在正确的位置相互中断。
    • 它在仅关键字参数上也失败了,它使用__kwdefaults__而不是__defaults__Here's a demonstration.
    • 感谢您的详细反馈。更新了函数以支持 kwdefaults。
    猜你喜欢
    • 2021-08-18
    • 2021-08-22
    • 1970-01-01
    • 2019-09-12
    • 2013-02-24
    • 1970-01-01
    • 2021-07-01
    • 2021-09-14
    • 2019-12-26
    相关资源
    最近更新 更多