web-dev-qa-db-ja.com

pythonのパワー演算子(**)は何に変換されますか?

言い換えれば、2つのアスタリスクの後ろには何がありますか?それは単に数をx倍するだけですか、それとも何か他のものですか?フォローアップの質問として、2 ** 3または2 * 2 * 2と書く方が良いですか。 C++では、関数を呼び出すため、単純な計算にはpow()を使用しない方がよいと聞いたので質問しています。

8
Boyan Kushlev

内部に興味がある場合は、命令を逆アセンブルして、マップ先のCPythonバイトコードを取得します。 Python3の使用:

»»» def test():
    return 2**3
   ...: 
»»» dis.dis(test)
  2           0 LOAD_CONST               3 (8)
              3 RETURN_VALUE

OK、これで入力時に計算が完了し、結果が保存されたようです。 2 * 2 * 2とまったく同じCPythonバイトコードを取得します(自由に試してみてください)。したがって、定数に評価される式の場合、同じ結果が得られ、それは問題ではありません。

変数の力が必要な場合はどうなりますか?

これで、2つの異なるバイトコードが得られます。

»»» def test(n):
        return n ** 3

»»» dis.dis(test)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (3)
              6 BINARY_POWER
              7 RETURN_VALUE

vs.

»»» def test(n):
    return n * 2 * 2
   ....: 

»»» dis.dis(test)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (2)
              6 BINARY_MULTIPLY
              7 LOAD_CONST               1 (2)
             10 BINARY_MULTIPLY
             11 RETURN_VALUE

さて、問題はもちろんですが、BINARY_MULTIPLYはBINARY_POWER操作よりも速いですか?

それを試す最良の方法は、timeitを使用することです。 IPythonを使用します%timeit 魔法。乗算の出力は次のとおりです。

%timeit test(100)
The slowest run took 15.52 times longer than the fastest. This could mean that an intermediate result is being cached 
10000000 loops, best of 3: 163 ns per loop

そして力のために

The slowest run took 5.44 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 473 ns per loop

代表的な入力に対してこれを繰り返すことをお勧めしますが、経験的には乗算の方が速いように見えます(ただし、出力の分散に関する前述の警告に注意してください)。

さらに内部が必要な場合は、CPythonコードを掘り下げることをお勧めします。

9
m01

2番目のものは数値に対して少し速いですが、最初のものと比較して利点は非常に低いです:読みやすさ。時間に追われていて、そのような最適化を行うようにプレッシャーをかけられている場合、pythonはおそらく使用すべき言語ではありませんです。

注:数字以外の場合:

a ** bは次のように変換されます

a.__pow__(b) 

一方、a * a * aはへの呼び出しです

a.__mul__(a.__mul__(a))

テストコード:

import time

s = time.time()
for x in xrange(1,1000000):
    x**5
print "done in ", time.time() - s

s = time.time()
for x in xrange(1,1000000):
    x*x*x*x*x
print "done in ", time.time() - s

私のマシンの歩留まりについて:

done in  0.975429058075
done in  0.260419845581
[Finished in 1.2s]
1

率直に言って、乗算は少し速いです。

>>timeit.timeit('[i*i*i*i for i in range(100)]', number=10000)
0.262529843304
>>timeit.timeit('[i**4 for i in range(100)]', number=10000)
0.31143438383

ただし、2つのオプションから1つを選択する場合は、速度だけを考慮する必要はありません。たとえば、2の20乗を計算するときに簡単なことは何ですか?単に2**20または20回繰り返して乗算タスクを実行するforループを使用していますか?

0
Ahsanul Haque

_**_演算子は、内部的に反復関数(組み込みのpow()Python docs )と同じセマンティクス)を使用します。これは、とにかくその関数を呼び出すだけであることを意味します。 )。

したがって、パワーを知っていてハードコーディングできる場合は、_2*2*2_を使用すると_2**3_よりも少し速くなる可能性があります。これは関数とは少し関係がありますが、パフォーマンスの主な問題はループを使用することだと思います。


_2**3_のような単純なものである場合、読みやすいコードを読みにくいコードに置き換えるのはまだかなりばかげていることに注意してください。パフォーマンスの向上はせいぜい最小限です。

0
Sam McCreery

から ドキュメント

パワー演算子は、左側の単項演算子よりも緊密に結合します。右側の単項演算子よりも緊密に結合しません。構文は次のとおりです。

_power ::=  primary ["**" u_expr]
_

したがって、括弧なしの累乗演算子と単項演算子のシーケンスでは、演算子は右から左に評価されます(これにより、オペランドの評価順序は制約されません)。_-1**2_は_-1_になります。

累乗演算子は、組み込みのpow()関数と同じセマンティクスを持ち、2つの引数を指定して呼び出されると、左の引数が右の引数の累乗になります。

これは、Pythonでは、_2**2**3_が2**(2**3) = 2**8 = 256として評価されることを意味します。

数学では、積み重ねられた指数は上から下に適用されます。この方法で行われなかった場合は、指数の乗算が得られます。

_(((2**3)**4)**5) = 2**(3*4*5)
_

乗算を行うだけの方が少し速いかもしれませんが、読みにくくなります。

0
Bob Dylan