问题
I need to monkey-patch my library to replace an instance of a symbol, and it's getting referenced by some function closures. I need to copy those functions (since I also need access to original unpatched version of the function as well), but __closure__
is immutable, and I can't copy.copy
it, so how can I create new closure cells objects in Python 2.7?
I for example given this function
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()
And I want to see
0, 42
回答1:
The simple way to make a closure cell would be to make a closure:
def make_cell(val=None):
x = val
def closure():
return x
return closure.__closure__[0]
If you want to reassign an existing cell's contents, you'll need to make a C API call:
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 only, of course.
回答2:
in lambda:
def make_cell(value):
fn = (lambda x: lambda: x)(value)
return fn.__closure__[0]
got anwer from https://github.com/nedbat/byterun/blob/master/byterun/pyobj.py#L12
回答3:
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__
.
来源:https://stackoverflow.com/questions/37665862/how-to-create-new-closure-cell-objects