2012-01-08 7 views
5

は、私は次のように関数定義を持つプロジェクトを維持:`locals()`のアクセスできない `.0`変数がメモリやパフォーマンスに影響しますか?

def f(a, (b1, b2), c): 
    print locals() 

私は.1キーが値(b1, b2)と、locals()に登場したことを発見したコードをデバッグしている間。

def f((a1, a2)): 
    print locals() 

(a1, a2)locals().0キーを持っています。簡単なチェックは、次のような関数定義があることを明らかにしました。私はこの動作に驚いていましたが、Pythonのドキュメントには何の情報も見つかりませんでした。

私の質問は次のとおりです。そうでなければアクセスできない位置変数はメモリやパフォーマンスに影響しますか?彼らはどこに文書化されていますか?彼らはどのような目的を果たしていますか?

問題のプロジェクトはSAXベースのfeedparserであり、この動作の影響を受ける可能性のある数十または数百の関数呼び出しを持つ可能性があります。

+4

このタプルの自動タプルPython 3では明示的に削除されていましたが、私が正しく思い出したのは、当時は文法の事故として記述されていました。それは確かに私が見たいと思うスタイルではありません。 –

+0

お役立ち情報そうです、 '2to3'ツールは自動的に'(b1、b2) 'の構文を変数名' xxx_todo_changeme'に置き換えます。それにもかかわらず、この現象を説明するのに役立つリンク、または明示的な削除に関するリンクがありますか? –

+3

@Kurt McKee [PEP 3113 - タプルパラメータの解凍](http://www.python.org/dev/peps/pep-3113/)。 –

答えて

5

したがってpep 3113は、芸術的ガスパンが指摘しているように、完全な答えを含んでいます。これは、おそらくこれが大きなパターンではない理由の一覧です。これらのうちの1つは、デバッグの厄介な副作用で発見しました。より大きなものは、あなたのコードがpython3への移行を壊すと思うが、私はまだ/個人的には2.7であるとは思えない。

私は何が起こったかで遊びたがっていました。 (:fooとbar同じバイトコードを持っているスポイラー):

from dis import dis 

def foo(a, (b, c) ,d): 
    return a + b + c + d 

def bar(a, b_c, d): 
    b, c = b_c 
    return a + b + c + d 

def baz(a, b, c, d): 
    return a + b + c + d 

print '\nfoo:' 
dis(foo) 
print '\nbar:' 
dis(bar) 
print '\nbaz:' 
dis(baz) 

収量:

foo: 
    3   0 LOAD_FAST    1 (.1) 
       3 UNPACK_SEQUENCE   2 
       6 STORE_FAST    3 (b) 
       9 STORE_FAST    4 (c) 

    4   12 LOAD_FAST    0 (a) 
      15 LOAD_FAST    3 (b) 
      18 BINARY_ADD   
      19 LOAD_FAST    4 (c) 
      22 BINARY_ADD   
      23 LOAD_FAST    2 (d) 
      26 BINARY_ADD   
      27 RETURN_VALUE   


bar: 
    7   0 LOAD_FAST    1 (b_c) 
       3 UNPACK_SEQUENCE   2 
       6 STORE_FAST    3 (b) 
       9 STORE_FAST    4 (c) 

    8   12 LOAD_FAST    0 (a) 
      15 LOAD_FAST    3 (b) 
      18 BINARY_ADD   
      19 LOAD_FAST    4 (c) 
      22 BINARY_ADD   
      23 LOAD_FAST    2 (d) 
      26 BINARY_ADD   
      27 RETURN_VALUE   


baz: 
11   0 LOAD_FAST    0 (a) 
       3 LOAD_FAST    1 (b) 
       6 BINARY_ADD   
       7 LOAD_FAST    2 (c) 
      10 BINARY_ADD   
      11 LOAD_FAST    3 (d) 
      14 BINARY_ADD   
      15 RETURN_VALUE   

あなたが見ることができるように、我々はこれらの三つの機能で何が起こるかを見ることができますいくつかの分解されたバイトコードとして探しています。 foobarは同じですが、bazはアンパックをスキップします。これはパフォーマンスに少し影響を与えますが、タプルのアンパックが必要な場合に限り、非常に小さな関数やおもちゃの例(このようなもの; P)以外は無視してください。

+0

私は前に 'dis'モジュールを使ったことがありません。このコードの動作を教えてくれてありがとう!残念ながら、すべてのSAXパーサーはタプルを渡すAPIを使用しているため、タプルのアンパックからエスケープすることはできませんが、これ(およびArtur提供のリンク)は、私が探していた種類の解析でした。 –

3

はい、パフォーマンスに影響します。

>>> import timeit 
>>> 
>>> 
>>> def function_1(a, (b1, b2), c): 
...  locals() 
... 
>>> def function_2(a, b1, b2, c): 
...  locals() 
... 
>>> 
>>> object_1 = object() 
>>> object_2 = object() 
>>> object_3 = object() 
>>> tuple_of_objects_2_and_3 = (object_2, object_3) 
>>> object_4 = object() 
>>> 
>>> n = 100000000 
>>> 
>>> time_1 = timeit.timeit(lambda: function_1(object_1, tuple_of_objects_2_and_3, 
...           object_4), 
...      number=n) 
>>> time_2 = timeit.timeit(lambda: function_2(object_1, object_2, object_3, 
...           object_4), 
...      number=n) 
>>> 
>>> print(time_1, time_2) 
(170.2440218925476, 151.92010402679443) 

ドキュメントまたは目的についてはわかりません。

+2

このテストには、タプルパッキングとタプルアンパックも含まれます。 'tuple_23 =(object_2、object_3) 'を使用し、最初のラムダでそれを参照するのはおそらくより公平でしょう。 – Robin

+0

@Robinあなたはありがとうございます。私は私の答えを編集しました。 –

+0

タイミングテストをお寄せいただきありがとうございます。 –

関連する問題