私はいくつかのコードをlispからPythonに翻訳しています。
lispでは、導入された変数を特別なものとして宣言し、動的スコープを持つletコンストラクトを持つことができます。(http://en.wikipedia.org/wiki/Dynamic_scope#Dynamic_scopingを参照してください)
Pythonで同様に行うにはどうすればよいですか?言語はこれを直接サポートしていないようですが、本当の場合、それをエミュレートするための良い方法は何でしょうか?
私はいくつかのコードをlispからPythonに翻訳しています。
lispでは、導入された変数を特別なものとして宣言し、動的スコープを持つletコンストラクトを持つことができます。(http://en.wikipedia.org/wiki/Dynamic_scope#Dynamic_scopingを参照してください)
Pythonで同様に行うにはどうすればよいですか?言語はこれを直接サポートしていないようですが、本当の場合、それをエミュレートするための良い方法は何でしょうか?
ここでの彼の推論において、正義は明白に正しいと思います。
一方で、Python にとって「不自然な」別のプログラミング パラダイムの概念実証を実装することに抵抗することはできません。単純にこれを行うのが大好きです。:-)
そのため、必要に応じてオブジェクトの属性がスコープされるクラスを作成しました(動的に作成できます)。私が言ったように、それは概念実証の状態にあるだけです-しかし、ほとんどの通常のエラー(まったく定義されていないスコープ内の変数にアクセスしようとするなど)は、適切なものでなくてもエラーが発生するはずです(IndexErrorたとえば、AttributeError の代わりにスタック アンダーフローが原因です)。
import inspect
class DynamicVars(object):
def __init__(self):
object.__setattr__(self, "variables", {})
def normalize(self, stackframe):
return [hash(tpl[0]) for tpl in stackframe[1:]]
def __setattr__(self, attr, value):
stack = self.normalize(inspect.stack())
d = {"value": value, "stack": stack}
if not attr in self.variables:
self.variables[attr] = []
self.variables[attr].append(d)
else:
our_value = self.variables[attr]
if our_value[-1]["stack"] == stack:
our_value[-1]["value"] = value
elif len(stack) <= len(our_value):
while our_value and stack != our_value["stack"]:
our_value.pop()
our_value.append(d)
else: #len(stack) > len(our_value):
our_value.append(d)
def __getattr__(self, attr):
if not attr in self.variables:
raise AttributeError
stack = self.normalize(inspect.stack())
while self.variables[attr]:
our_stack = self.variables[attr][-1]["stack"]
if our_stack == stack[-len(our_stack):]:
break
self.variables[attr].pop()
else:
raise AttributeError
return self.variables[attr][-1]["value"]
# for testing:
def c():
D = DynamicVars()
D.c = "old"
print D.c
def a():
print D.c
a()
def b():
D.c = "new"
a()
b()
a()
def c():
D.c = "newest"
a()
b()
a()
c()
a()
c()
2020 update - 別の同様の質問が表示され、特別な名前空間オブジェクトを必要としないハックを作成しました (ただし、locals() を実際の変数に更新するなど、cPython の内部のものを使用することに頼っています: https://stackoverflow.com/a /61015579/108205 (Python 3.8 で動作)
Lisp の「特別な」変数または動的スコープの変数に対応する Python のイディオムは、「スレッド ローカル ストレージ」です。
ここに良い議論があります: Python の「スレッド ローカル ストレージ」とは何ですか? なぜそれが必要なのですか?
let ステートメントを含む Lisp の特殊変数を完全にエミュレートしたい場合は、コンテキスト マネージャーを使用できます。
from __future__ import with_statement # if Python 2.5
from contextlib import contextmanager
import threading
dyn = threading.local()
@contextmanager
def dyn_vars(**new):
old = {}
for name, value in new.items():
old[name] = getattr(dyn, name, None)
setattr(dyn, name, value)
yield
for name, value in old.items():
setattr(dyn, name, value)
例 (明らかにばかげていますが、再入可能機能を示しています):
def greet_self():
print 'Hi', dyn.who_am_I
def greet_selves():
with dyn_vars(who_am_I='Evil Twin'):
greet_self()
greet_self()
with dyn_vars(who_am_I='Tobia'):
greet_selves()
動的スコープは有害と見なされます。
使用しないでください。それをエミュレートしないでください。
エミュレートする必要がある場合は、dynamic_scope
この動作をエミュレートするモジュールを定義し、そのモジュールをすべてのソース ファイルにインポートします。begin
このモジュールには、動的スコープ、、、および を使用する関数の最初の行で呼び出されるend
メソッドget
が必要set
です。get
およびset
メソッドは、コール チェーンが and によって実装されている変数名のコール チェーンのルックアップを実装する必要がありbegin
ますend
。次に、コードをリファクタリングして動的スコープを排除します。