如何创建新的闭包对象?

How to create new closure cell objects?

我需要对我的库进行 monkey-patch 以替换一个符号的实例,它正被一些函数闭包引用。我需要复制这些函数(因为我也需要访问该函数的原始未修补版本),但是 __closure__ 是不可变的,我不能 copy.copy 它,所以我如何创建新的Python 2.7?

中的闭包单元对象

例如我给出了这个函数

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()

而且我想看

0, 42

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

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。

在 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

得到了答案

如果你想要一个空单元格(这就是我发现这个问题的原因)(引用它会引发 NameError: free variable '...' referenced before assignment in enclosing scope 并访问它的 cell.cell_contents 会引发 ValueError: Cell is empty),你可以使一个值成为一个局部变量,但永远不要让它被赋值:

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

你可以这样组合它们:

_SENTINEL = object()

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

因此不带参数调用 returns 一个空单元格,并使用任何值调用具有该值的单元格。

如果你不关心空单元格,你可以这样做:

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

请注意,它在旧的 Python 2 中是 func_closure,而不是 __closure__