Wat vertaalt de Power Operator (**) in Python in?

Met andere woorden, wat bestaat achter de twee sterretjes? Vermenigvuldigt het gewoon de nummer x keer of iets anders? Als vervolgvraag is het beter om 2 ** 3 of 2 * 2 * 2 te schrijven. Ik vraag het omdat ik heb gehoord dat het in C++ beter is om POW () voor eenvoudige berekeningen niet te gebruiken, omdat het een functie noemt.


Antwoord 1, Autoriteit 100%

Als u geïnteresseerd bent in de internals, zou ik de instructie demonteren om de CPYTHON BYTECODE het kaarten te krijgen. Python3 gebruiken:

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

OK, dus dat lijkt de berekening recht op binnenkomst te hebben gedaan en het resultaat is opgeslagen. Je krijgt precies dezelfde Cpython Bytecode voor 2 * 2 * 2 (voel je vrij om het te proberen). Dus, voor de uitdrukkingen die evalueren naar een constante, krijg je hetzelfde resultaat en het maakt niet uit.

Wat als u wilt de kracht van een variabele?

Nu krijg je twee verschillende bits van bytecode:

»»» 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

De vraag is natuurlijk: is de BINARY_MULTIPLY sneller dan de BINARY_POWER-bewerking?

De beste manier om dat te proberen, is door timeit te gebruiken. Ik gebruik de IPython %timeitmagie. Dit is de uitvoer voor vermenigvuldiging:

%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

en voor macht

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

Misschien wilt u dit herhalen voor representatieve invoer, maar empirisch lijkt het erop dat de vermenigvuldiging sneller gaat (maar let op het genoemde voorbehoud over de variantie in de uitvoer).

Als je meer interne informatie wilt, raad ik je aan in de CPython-code te duiken.


Antwoord 2, autoriteit 21%

Hoewel de tweede iets sneller is voor getallen, is het voordeel erg laag in vergelijking met de eerste: leesbaarheid. Als je tijd nodig hebt en je wordt onder druk gezet om dergelijke optimalisaties uit te voeren, dan is python waarschijnlijk niet de taal die je zou moeten gebruiken.

Opmerking: voor andere waarden dan getallen:

a ** bvertaalt naar

a.__pow__(b) 

terwijl a * a * aeen oproep is om

a.__mul__(a.__mul__(a))

Testcode:

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

Voor mijn machine levert het op:

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

Antwoord 3, autoriteit 7%

Als je het eerlijk vraagt, is vermenigvuldigen iets sneller.

>>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

Maar snelheid is niet het enige waarmee u rekening moet houden wanneer u een van de twee opties kiest. Van bijvoorbeeld, wat is gemakkelijker tijdens het berekenen van 2 tot de macht 20? Gewoon 2**20schrijven of een for-lus gebruiken die 20 keer wordt herhaald en een vermenigvuldigingstaak uitvoert?


Antwoord 4

De operator **zal intern een iteratieve functie gebruiken (dezelfde semantiek als de ingebouwde pow()(Python docs), wat waarschijnlijk betekent dat het die functie toch gewoon aanroept).

Daarom, als je de kracht kent en het kunt hardcoderen, zou het gebruik van 2*2*2waarschijnlijk iets sneller zijn dan 2**3. Dit heeft een beetje te maken met de functie, maar ik denk dat het belangrijkste prestatieprobleem is dat er een lus wordt gebruikt.


Merk op dat het nog steeds nogal dwaas is om beter leesbare code te vervangen door minder leesbare code, terwijl het zoiets eenvoudigs is als 2**3, de prestatiewinst is op zijn best minimaal.


Antwoord 5

Van de documenten:

De krachtoperator bindt strakker dan unaire operators aan zijn linkerkant; het bindt minder stevig dan unaire operatoren aan de rechterkant. De syntaxis is:

power ::=  primary ["**" u_expr]

Dus in een reeks zonder haakjes van macht en unaire operatoren, worden de operatoren geëvalueerd van rechts naar links (dit beperkt de evaluatievolgorde voor de operanden niet): -1**2resulteert in -1.

De power-operator heeft dezelfde semantiek als de ingebouwde functie pow(), wanneer deze wordt aangeroepen met twee argumenten: het levert zijn linkerargument verheven op tot de macht van zijn rechterargument.

>

Dit betekent dat in Python: 2**2**3wordt geëvalueerd als 2**(2**3) = 2**8 = 256.

In de wiskunde worden gestapelde exponenten van boven naar beneden toegepast. Als het niet op deze manier was gedaan, zou je gewoon vermenigvuldiging van exponenten krijgen:

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

Het is misschien iets sneller om alleen de vermenigvuldiging uit te voeren, maar veel minder leesbaar.

Other episodes