如何创建新的闭包单元对象?

时间:2016-06-06 19:58:37

标签: python python-2.7 closures monkeypatching

我需要对我的库进行猴子修补以替换符号的实例,并且它会被某些函数闭包引用。我需要复制这些函数(因为我也需要访问函数的原始未修补版本),但<svg xmlns="http://www.w3.org/2000/svg"><defs><radialGradient id="a" r="1"><stop stop-color="#fff" offset="50%"/><stop offset="70%"/></radialGradient></defs></svg> 是不可变的,我不能__closure__它,所以我怎么能在Python 2.7中创建新的闭包单元对象?

我举例说明了这个功能

copy.copy

我想看看

def f():
    def incorrectfunction():
        return 0
    def g():
        return incorrectfunction()
    return g

def correctfunction():
    return 42

func = f()
patched_func = patchit(f)   # replace "incorrectfunction"
print func(), patched_func()

3 个答案:

答案 0 :(得分:5)

制作闭包单元的简单方法是制作闭包:

def make_cell(val=None):
    x = val
    def closure():
        return x
    return closure.__closure__[0]

如果您想重新分配现有单元格的内容,则需要进行C API来电:

import ctypes
PyCell_Set = ctypes.pythonapi.PyCell_Set

# ctypes.pythonapi functions need to have argtypes and restype set manually
PyCell_Set.argtypes = (ctypes.py_object, ctypes.py_object)

# restype actually defaults to c_int here, but we might as well be explicit
PyCell_Set.restype = ctypes.c_int

PyCell_Set(cell, new_value)
当然只有CPython。

答案 1 :(得分:1)

在lambda中:

def make_cell(value):
    fn = (lambda x: lambda: x)(value)
    return fn.__closure__[0]

来自https://github.com/nedbat/byterun/blob/master/byterun/pyobj.py#L12

答案 2 :(得分:0)

If you want an empty cell (which is what I found this question for) (One where referencing it raises a NameError: free variable '...' referenced before assignment in enclosing scope and accessing it's cell.cell_contents raises a ValueError: Cell is empty), you can make a value a local variable, but never let it be assigned:

def make_empty_cell():
    if False:
        # Any action that makes `value` local to `make_empty_cell`
        del value
    return (lambda: value).__closure__[0]

You can combine them like this:

_SENTINEL = object()

def make_cell(value=_SENTINEL):
    if value is not _SENTINEL:
        x = value
    return (lambda: x).__closure__[0]

So calling with no arguments returns an empty cell, and with any value, a cell with that value.

If you don't care about empty cells, you can do:

def make_cell(value):
    return (lambda: value).__closure__[0]

Note that it is func_closure in older Python 2, instead of __closure__.