0

通常、私はそのように eval を使うことができます:

new_dict={'color':'green'}
eval("color=='green'",new_dict)

この場合、true が返されるため、new_dict の色が実際には緑であることがわかります。ここで、誰かが eval を使用したいが、dict ではなくより一般的なオブジェクトを使用するコードを見つけました。次のコードは、基本的にこの人物が行ったことです。

class myStuff(object):
    def __init__(self):
        self.color = "green"

class Dummy(dict):
    def __init__(self, node):
        dict.__init__(self)
        self.node = node

    def __getitem__(self, key):
        return(getattr(self.node, key))


node=myStuff()
new_dict = Dummy(node)
print eval("color=='green'",new_dict)

__getitem__上記のコードの開発者は、eval がnew_dictのメソッドを色に使用していることをどうやって知ったのでしょうか? ドキュメントと python ヘルプ関数は見つかりましたが、メソッド (または実際のコード) の段階的なドキュメントは見つかりませんでした。上記のコードはそうです。それとも、eval メソッドがどのように実装されているかを実際に誰も知らないため、コードで将来奇妙なエラーが発生する可能性があるため、上記のメソッドを使用するのは悪いことですか?

編集: プログラムで eval が使用される理由は次のとおりです: リスト mylist に myStuff の 20 個のオブジェクトがあり、それらを黄色でフィルター処理したい場合、 [ n for n in mylist if eval(query, Dummy(n) ] with `query="color=='yellow'". 私は専門家ではありませんが、この方法で問題が発生する可能性があるかどうかを知りたいだけです。

4

2 に答える 2

2

この__getitem__関数は、辞書またはリスト (より正確には、任意のマッピングまたはシーケンス) をシミュレートする方法です。

シーケンスとマッピングのリファレンス ドキュメントはData modelにありますが、おそらくcollections.abcモジュールとそこからのリンクから始めるのが最適です。

基本的な考え方を要約すると、次のようなコードを書く場合:

foo[bar]

Python はそれを次のように変換します*:

foo.__getitem__(bar)

__getitem__をシミュレートするように定義することに問題はありませんdict

そして、その属性を dict 項目として扱うオブジェクトを作成するためにそれを行うことは、名前 (「attrdict」) を持つ一般的なパターンです。

ただし、ほとんどの場合、使用eval 間違っています。したがって、仕事をするために正しいことをすることは、一般に、正しいことをしているという点では正しいですが、そもそもeval使用しているという点では間違っています。eval


あなたの特定のケースでは、そもそも使用する正当な理由はありませんeval。これの代わりに:

eval("color=='green'",new_dict)

これを行うだけです:

new_dict['color']=='green'

Python の初心者 (特に、古いバージョンの PHP、Tcl、または JavaScript で育った人) がしばしば使用したいと思う理由の 1 つevalは、簡単に渡すことができる式を取得することです。しかし、Python (そして、最新の PHP と JS) では、関数は第一級の値であり、文字列と同じくらい簡単に渡すことができます。もちろん、文字列とは異なり、関数は呼び出し可能です。名前付き関数またはラムダ関数を作成したり、 を使用したり、必要partialなローカル変数を閉じたりすることができます。

関数でできない文字列でできることはほとんどありません。もちろん、大きなセキュリティ ホールを開いたり、パフォーマンスを低下させたり、デバッグを妨げたりすることは別として。

したがって、次のような代わりに:

expr = "color=='green'"
# ...
eval(expr, new_dict)

…これを行うだけです:

expr = lambda x: x.color=='green'
# ...
expr(new_dict)

あなたが編集した質問で:

プログラムで eval が使用される理由は次のとおりです。リスト mylist に myStuff の 20 個のオブジェクトがあり、それらを黄色でフィルター処理したいとします。次に、[ n for n in mylist if eval(query, Dummy( n) ] `query="color=='yellow'" を使用。

したがって、おそらく次のようなことをしています。

query = "color=={}'.format(color)
# ...
[n for n in mylist if eval(query, Dummy(n)]

しかし、次のように簡単に行うことができます。

[n for n in mylist if n.color == color]

より動的なものが必要な場合でも、文字列よりも簡単に関数を動的に構築できます。

query = lambda n: n.color == color
[n for n in mylist if query(n)]

実際、本当にやりたい場合は、これを完全に機能させることもできます。

filter(compose(partial(operator.eq, color), attrgetter('color')), mylist)

しかし、Python の優れた点は、完全に関数型または完全に命令型にする必要がないことです。その中間、または 25% または 75% など、たまたま読み書きしやすいものを書くことができます。


その間:

それとも、eval メソッドがどのように実装されているかを実際に誰も知らないため、コードで将来奇妙なエラーが発生する可能性があるため、上記のメソッドを使用するのは悪いことですか?

いいえ、それはほとんど問題になりません。

まず、 のドキュメントevalは通常、それが何をするかを正確に予測するのに十分であり、すべての Python 実装はそのドキュメントに従う必要があります。

詳細を知る必要があるまれなケースでは、主要な実装はすべてオープン ソースであるため、コードを読むだけで済みます。たとえば、ここでCPython 3.3 コードをオンラインで参照できます。**


* これは完全に正確ではありません。実際のコードは実際に__getitem__はオブジェクトではなくクラスを検索し (古いスタイルのクラスと 2.x の新しいクラスでは少し異なります)、C モジュール/Java パッケージ/Python 実装に適したものから拡張型を処理します。スライス (2.x と 3.x では異なる) など。しかし、それが基本的な考え方です。

**evalコードは何年にもわたって徐々にリファクタリングされてきたので、この時点でeval、モジュールとその仲間を使用して純粋な Python の数行ast、または関数を使用して C の数行でほとんど再実装できるため、指摘PyEval*するのは困難です。関心のある実装とバージョンを知らなくても、コードの正確な行から開始できます。

于 2013-05-03T21:30:55.907 に答える
2

__getitem__dictキーでアイテムを取得するために使用するものです。をオーバーライド__getitem__して の属性を返すようにすることでself.node、本質的にnodeディクショナリになります。

より簡単な方法__dict__は、同じことを行う属性を使用することです。

print eval("color=='green'", node.__dict__)

しかし、実際には、これを使用しないでください。お願いします。eval()が仕事に適したツールになることはめったになく、これは を使用する必要がない場合の完璧な例ですeval

于 2013-05-03T21:29:12.420 に答える