接下来是一个可怕的 hack,它使用了未记录的、特定于实现的 Python 特性。你永远不应该做这样的事情。
它已经在 Python 2.6.1 和 2.7.2 上进行了测试;似乎不适用于编写的 Python 3.2,但是无论如何,您都可以在 Python 3.x 中正确执行此操作。
import sys
class NoDupNames(object):
def __init__(self):
self.namespaces = []
def __call__(self, frame, event, arg):
if event == "call":
if frame.f_code.co_flags == 66:
self.namespaces.append({})
elif event in ("line", "return") and self.namespaces:
for key in frame.f_locals.iterkeys():
if key in self.namespaces[-1]:
raise NameError("attribute '%s' already declared" % key)
self.namespaces[-1].update(frame.f_locals)
frame.f_locals.clear()
if event == "return":
frame.f_locals.update(self.namespaces.pop())
return self
def __enter__(self):
self.oldtrace = sys.gettrace()
sys.settrace(self)
def __exit__(self, type, value, traceback):
sys.settrace(self.oldtrace)
用法:
with NoDupNames():
class Foo(object):
num = None
num = 42
结果:
NameError: attribute 'num' already declared
它是如何工作的:我们连接到系统跟踪钩子。每次 Python 即将执行一行时,我们都会被调用。这使我们可以看到最后执行的语句定义了哪些名称。为了确保我们可以捕获重复项,我们实际上维护了自己的局部变量字典并在每行之后清除Python 的。在类定义的最后,我们将本地变量复制回 Python 中。其他一些愚蠢的东西在那里处理嵌套类定义并在单个语句中处理多个分配。
不利的一面是,我们的“清除所有当地人!” 方法意味着你不能这样做:
with NoDupNames():
class Foo(object):
a = 6
b = 7
c = a * b
因为据 Python 所知,没有名称a
和b
执行时间c = a * b
;我们一看到它们就清除了它们。此外,如果您在一行中两次分配相同的变量(例如,a = 0; a = 1
),它不会捕捉到这一点。但是,它适用于更典型的类定义。
此外,除了类定义之外,您不应将任何内容放在NoDupNames
上下文中。我不知道会发生什么;也许没什么不好。但我还没有尝试过,所以理论上宇宙可以被吸入它自己的塞孔。
这很可能是我写过的最邪恶的代码,但它确实很有趣!