以下は、文書化されていない実装固有のPython機能を使用する恐ろしいハックです。このようなことは絶対にしないでください。
Python2.6.1および2.7.2でテストされています。書かれているようにPython3.2で動作するようには見えませんが、それなら、とにかくPython3.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
実行されるかはわかりません。私たちはそれらを見るとすぐにそれらをクリアしました。また、同じ変数を1行に2回割り当てると(たとえばa = 0; a = 1
)、それをキャッチできません。ただし、より一般的なクラス定義では機能します。
NoDupNames
また、コンテキスト内にクラス定義以外のものを入れないでください。何が起こるかわかりません。多分悪いことは何もない。しかし、私はそれを試したことがないので、理論的には宇宙はそれ自身のプラグホールに吸い込まれる可能性があります。
これはおそらく私が今まで書いた中で最も邪悪なコードですが、それは確かに楽しかったです!