我正在观看此视频Object-Oriented Programming is Bad。该主题与我要问的问题并没有真正的联系,但是视频引导了它。恰恰在41:10
,他谈到了python中似乎不存在的功能,他谈到了具有嵌套函数/块的能力,这些函数/块不了解完整的本地上下文,而是知道手动选择一些变量。基本上,他要求能够从封闭中删除/部分隐藏环境。
以下是所需功能在python中的外观(例如,使用use
装饰器):
def foo():
x, y, z = 1, 2, 3
def baz():
print(x, y) # prints "1 2"
@use(y)
def bar():
print(y) # Should print "2"
z = 4 # Should create a local variable named `z`
print(x) # Should raise a NameError (`x` not defined)
bar(), baz())
foo()
我尝试实现该装饰器,但没有成功,因此我尝试了一个更简单的目标,即不完全隐藏环境,而是完全隐藏环境。这是我找到的解决方案:
import ctypes
def foo():
x, z = 1
def baz():
print(x) # prints 1
@hide_environment
def bar(y):
print(y) # prints 2
z = 3
print(z) # prints 3
print(x) # NameError is raised
baz(), bar(2)
def hide_environment(f):
def environment_agnostic_f(*args, **kwargs):
remove_environment(f)
return f(*args, **kwargs)
return environment_agnostic_f
def remove_environment(f):
if f.__closure__ is None:
return
for v in f.__closure__:
py_obj = ctypes.py_object(v)
ctypes.pythonapi.PyCell_Set(py_obj, None) # that look like it could go wrong
foo()
我有四个问题:
use
机制(在python中)?hide_environment
的实现正确吗?我不知道2.怎么可能,因为我不知道是否有可能截获/找到y
值所在的单元格地址。我不确定是如何处理的,但是据我了解,当读取foo
时,“编译器”(不确定是否应该这样调用)会将所有{{1}来自locals
对象中foo
的},以后将由__closure__
和bar
引用。这导致了我的最后一个问题:
答案 0 :(得分:0)
这不是Python附带的,但是实现它的最简单方法是只检查函数使用了什么,如果使用过多则抛出异常:
def allow_closure_vars(*okay_names):
def checker(f):
used_names = set(f.__code__.co_freevars)
bad_names = used_names - set(okay_names)
if bad_names:
raise ValueError(f"Function uses the following variables it shouldn't: {bad_names}")
return f
return checker
def f():
x, y = 1, 2
@allow_closure_vars('x')
def g():
print(y)
g()
f()
尝试定义g
将引发ValueError
。
您的实现实际上并未禁止闭包变量访问。它只是取消分配变量,就像del
语句一样。该函数仍然可以访问变量,但尚未分配。这很可能与外部函数有关,该外部函数可能并不期望未分配这些变量,并且不会阻止任何代码为变量分配新值。