4

私はDavidBeazleyのPythonリファレンスブックを読んでいて、彼は次のように述べています。

たとえば、多くの平方根演算を実行している場合は、「math.sqrt(x)」と入力するよりも、「from math importsqrt」および「sqrt(x)」を使用する方が高速です。

と:

メソッドやモジュールルックアップを多用する計算の場合、ほとんどの場合、実行する操作を最初にローカル変数に入れて、属性ルックアップを削除することをお勧めします。

私はそれを試してみることにしました:

最初()

def first():
    from collections import defaultdict
    x = defaultdict(list)

2番目()

def second():
    import collections
    x = collections.defaultdict(list)

結果は次のとおりです。

2.15461492538
1.39850616455

このような最適化はおそらく私には関係ありません。しかし、なぜビーズリーが書いたのとは逆のことが真実になるのか、私は興味があります。また、1秒の違いがあることに注意してください。これは、タスクが簡単であることを考えると重要です。

なぜこうなった?

アップデート:

私は次のようなタイミングを取得しています:

print timeit('first()', 'from __main__ import first');
print timeit('second()', 'from __main__ import second');
4

6 に答える 6

6

from collections import defaultdictとはimport collections、繰り返されるタイミングループの外側にある必要があります。これは、それらを繰り返す必要がないためです。

from構文は構文よりも多くの作業を行う必要があると思いimportます。

このテストコードの使用:

#!/usr/bin/env python

import timeit

from collections import defaultdict
import collections

def first():
    from collections import defaultdict
    x = defaultdict(list)

def firstwithout():
    x = defaultdict(list)

def second():
    import collections
    x = collections.defaultdict(list)

def secondwithout():
    x = collections.defaultdict(list)

print "first with import",timeit.timeit('first()', 'from __main__ import first');
print "second with import",timeit.timeit('second()', 'from __main__ import second');

print "first without import",timeit.timeit('firstwithout()', 'from __main__ import firstwithout');
print "second without import",timeit.timeit('secondwithout()', 'from __main__ import secondwithout');

結果が得られます:

first with import 1.61359190941
second with import 1.02904295921
first without import 0.344709157944
second without import 0.449721097946

これは、繰り返しインポートにかかる費用を示しています。

于 2011-05-08T07:14:14.680 に答える
4

first(.)との間の比率も同様になりますがsecond(.)、違いはタイミングがマイクロ秒レベルであるということだけです。

私はあなたのタイミングが何か有用なものを測定しているとは思いません。より良いテストケースを見つけ出してみてください!

更新:
FWIW、DavidBeazleyの主張を裏付けるいくつかのテストがあります。

import math
from math import sqrt

def first(n= 1000):
    for k in xrange(n):
        x= math.sqrt(9)

def second(n= 1000):
    for k in xrange(n):
        x= sqrt(9)

In []: %timeit first()
1000 loops, best of 3: 266 us per loop
In [: %timeit second()
1000 loops, best of 3: 221 us per loop
In []: 266./ 221
Out[]: 1.2036199095022624

したがってfirst()、よりも約20%遅くなりsecond()ます。

于 2011-05-08T07:53:57.273 に答える
1

私の推測では、あなたのテストは偏っていて、2番目の実装は、最初の実装がすでにモジュールをロードしたことから、または単に最近モジュールをロードしたことから得られます。

何回試しましたか?順番を入れ替えましたか?

于 2011-05-08T07:14:00.943 に答える
1

first()名前をインポートするにはモジュールにアクセスする必要があるため、何も保存されません。

また、タイミング方法を指定しませんが、関数名を指定するとfirst()、最初のインポートを実行するように見えます。これは、モジュールをコンパイルして実行する必要があるため、後続のインポートよりも常に長くなります。

于 2011-05-08T07:14:00.937 に答える
1

ソースコードの読み取り/理解の効率の問題もあります。これが実際の実例です(stackoverflowの質問からのコード)

オリジナル:

import math

def midpoint(p1, p2):
   lat1, lat2 = math.radians(p1[0]), math.radians(p2[0])
   lon1, lon2 = math.radians(p1[1]), math.radians(p2[1])
   dlon = lon2 - lon1
   dx = math.cos(lat2) * math.cos(dlon)
   dy = math.cos(lat2) * math.sin(dlon)
   lat3 = math.atan2(math.sin(lat1) + math.sin(lat2), math.sqrt((math.cos(lat1) + dx) * (math.cos(lat1) + dx) + dy * dy))
   lon3 = lon1 + math.atan2(dy, math.cos(lat1) + dx)
   return(math.degrees(lat3), math.degrees(lon3))

別:

from math import radians, degrees, sin, cos, atan2, sqrt

def midpoint(p1, p2):
   lat1, lat2 = radians(p1[0]), radians(p2[0])
   lon1, lon2 = radians(p1[1]), radians(p2[1])
   dlon = lon2 - lon1
   dx = cos(lat2) * cos(dlon)
   dy = cos(lat2) * sin(dlon)
   lat3 = atan2(sin(lat1) + sin(lat2), sqrt((cos(lat1) + dx) * (cos(lat1) + dx) + dy * dy))
   lon3 = lon1 + atan2(dy, cos(lat1) + dx)
   return(degrees(lat3), degrees(lon3))
于 2011-05-09T00:04:30.820 に答える
0

モジュールをインポートし、そのモジュールと定数をとして参照して、通常どおりコードを記述しますmodule.attribute。次に、関数に結合定数のデコレータをbind_all_modulesプレフィックスとして付けるか、以下の関数を使用してプログラム内のすべてのモジュールをバインドします。

def bind_all_modules():
    from sys import modules
    from types import ModuleType
    for name, module in modules.iteritems():
        if isinstance(module, ModuleType):
            bind_all(module)

def bind_all(mc, builtin_only=False, stoplist=[],  verbose=False):
    """Recursively apply constant binding to functions in a module or class.

    Use as the last line of the module (after everything is defined, but
    before test code).  In modules that need modifiable globals, set
    builtin_only to True.

    """
    try:
        d = vars(mc)
    except TypeError:
        return
    for k, v in d.items():
        if type(v) is FunctionType:
            newv = _make_constants(v, builtin_only, stoplist,  verbose)
            try: setattr(mc, k, newv)
            except AttributeError: pass
        elif type(v) in (type, ClassType):
            bind_all(v, builtin_only, stoplist, verbose)
于 2011-05-08T23:25:08.600 に答える