2010-03-09 9 views
9

begegne mir das folgende kleine lästige Dilemma immer und immer wieder in Python:Python: Wie teuer ist es, eine kleine Liste oft zu erstellen?

Option 1: (?)

saubere, aber langsamer, wenn viele Male genannt, da a_list für jeden Aufruf neu erstellt bekommen do_something()

def do_something():  
    a_list = ["any", "think", "whatever"]  
    # read something from a_list 

Option 2:

hässliche, aber effiziente (verschonen die a_list Schöpfung wieder ganz von vorne)

a_list = ["any", "think", "whatever"]  
def do_something():  
    # read something from a_list 

Was denkst du?

+8

Im Zweifelsfall sollten Sie den lesbarsten, elegantesten, selbstdokumentierenden Code verwenden, bis Sie ein Leistungsprofiler dazu aufgefordert wird. –

+0

Ja, ich weiß. Aber das ist so ein kleines und lästiges und einfach zu vermeidendes Ding ... – GabiMe

+4

"klein" bedeutet, es zu ignorieren. Tun Sie, was immer am klarsten ist. Lassen Sie Leistungsüberlegungen beiseite, bis Sie * beweisen * können, dass es ein Problem ist. –

Antwort

16

Was ist hässlich daran?

Sind die Inhalte der Liste immer Konstanten, wie in Ihrem Beispiel? Wenn dem so ist: Neuere Versionen von Python (seit 2.4) werden dies optimieren, indem sie den konstanten Ausdruck auswerten und das Ergebnis behalten, aber nur, wenn es ein Tupel ist. Sie könnten es also zu einem Tupel machen.Oder Sie könnten aufhören, sich um kleine Dinge wie diese zu sorgen.

Hier ist eine Liste von Konstanten und ein Tupel von Konstanten:

>>> def afunc(): 
... a = ['foo', 'bar', 'zot'] 
... b = ('oof', 'rab', 'toz') 
... return 
... 
>>> import dis; dis.dis(afunc) 
    2   0 LOAD_CONST    1 ('foo') 
       3 LOAD_CONST    2 ('bar') 
       6 LOAD_CONST    3 ('zot') 
       9 BUILD_LIST    3 
      12 STORE_FAST    0 (a) 

    3   15 LOAD_CONST    7 (('oof', 'rab', 'toz')) 
      18 STORE_FAST    1 (b) 

    4   21 LOAD_CONST    0 (None) 
      24 RETURN_VALUE 
>>> 
+1

Wirklich? Das wusste ich nicht. Diese Tupel werden so optimiert, dass sie nur einmal erstellt werden. Interessant .. Dies würde wahrscheinlich 90% der Fälle des OP – GabiMe

+0

@AnonymousDriveByDownVoter lösen: Vorsicht, um einen Kommentar zu hinterlassen? –

4

Erstellen Sie nie etwas mehr als einmal, wenn Sie nicht müssen. Dies ist eine einfache Optimierung, die von Ihnen selbst durchgeführt werden kann und ich persönlich finde das zweite Beispiel nicht hässlich.

Manche mögen argumentieren, sich nicht darum zu kümmern, kleine Dinge wie diese zu optimieren, aber ich denke, dass etwas, das so einfach zu beheben ist, sofort erledigt werden sollte. Ich würde es hassen zu sehen, dass Ihre Anwendung mehrere Kopien von allem erstellt, was nicht nötig ist, um ein willkürliches Gefühl von "Code-Schönheit" zu bewahren. :)

3

Wenn Ihr a_list nicht ändert, verschieben Sie es aus der Funktion.

0

Wenn die Liste nie geändert wird, warum verwenden Sie überhaupt Listen?

Ohne Ihre tatsächlichen Anforderungen zu kennen, würde ich empfehlen, einfach einige if-Anweisungen zu verwenden, um die Liste und den Teil "lies etwas von der Liste" vollständig loszuwerden.

4

Option 3:

def do_something(a_list = ("any", "think", "whatever")): 
    read something from a_list 

Option 3 im Vergleich zu Option 1:

Beide sind gleichermaßen lesbar meiner Meinung nach (obwohl einige scheinen anders in den Kommentaren zu denken :-)!). Sie könnten sogar Option 3 wie folgt schreiben:

was wirklich den Unterschied in Bezug auf die Lesbarkeit minimiert. Im Gegensatz zu Option 1 definiert Option 3 a_list jedoch nur einmal - zu dem Zeitpunkt, zu dem do_something definiert ist. Genau das wollen wir.

Option 3 im Vergleich zu Option 2:

globale Variablen vermeiden möglich, wenn. Mit Option 3 können Sie das tun. Auch mit Option 2 oder im Laufe der Zeit oder wenn andere Personen diesen Code pflegen, könnte die Definition von a_list von def do_something getrennt werden. Das ist vielleicht keine große Sache, aber ich denke, es ist etwas unerwünscht.

+3

Gneee ... Sorry, eine Liste als Standardparameter zu sehen schmerzt. –

+1

-1 "Vermeiden Sie globale Variablen, wenn Sie können" ist ein guter Ratschlag, aber "Python ist force [d] zur ersten Suche nach der Variable in der Locals() -Dikt" ist absolut codswallop. Der COMPILER weiß, ob er lokal oder global ist, und im Fall von Option 2 gibt er eine Anweisung LOAD GLOBAL aus. Das Locals() - Diktat manifestiert sich, wenn und nur wenn man die locals() -Funktion aufruft - der COMPILER weiß, wo alle Einheimischen gespeichert sind; Es wird kein Dict verwendet, um den normalen Funktions-/Methodencode auszuführen. –

1

Nun scheint es, kommt es um das Array in der Funktion oder nicht initialisiert:

import time 
def fun1(): 
     a = ['any', 'think', 'whatever'] 
     sum = 0 
     for i in range(100): 
       sum += i 

def fun2(): 
     sum = 0 
     for i in range(100): 
       sum += i 


def test_fun(fun, times): 
     start = time.time() 
     for i in range(times): 
       fun() 
     end=time.time() 
     print "Function took %s" % (end-start) 

# Test 
print 'warming up' 
test_fun(fun1, 100) 
test_fun(fun2, 100) 

print 'Testing fun1' 
test_fun(fun1, 100000) 
print 'Testing fun2' 
test_fun(fun2, 100000) 

print 'Again' 
print 'Testing fun1' 
test_fun(fun1, 100000) 
print 'Testing fun2' 
test_fun(fun2, 100000) 

und die Ergebnisse:

>python test.py 
warming up 
Function took 0.000604152679443 
Function took 0.000600814819336 
Testing fun1 
Function took 0.597407817841 
Testing fun2 
Function took 0.580779075623 
Again 
Testing fun1 
Function took 0.595198154449 
Testing fun2 
Function took 0.580571889877 

Sieht aus, als gäbe es keinen Unterschied.

+1

Die Verwendung des 'timeit'-Moduls wäre passender ... – ChristopheD

+1

Es könnte helfen, wenn Sie irgendwo do_fun1() aufrufen würden. ;-) –

+0

Die Verschachtelung in einer Funktion ist etwas ganz anderes.Versuchen Sie es unter den Bedingungen, die wir diskutieren: (1) lokal (2) global (3) default arg (4) lokal, aber als Tupel anstelle einer Liste. Auch ein großer Teil der Zeit ist für die for-Schleife, die alle irrelevant ist. Wegwerfen Sie die Schleife, und werfen Sie eine Option (0) keine Liste/Tupel überhaupt dann Bericht (1) - (0), (2) - (0) usw. –

0

Ich habe an automatisierten Systemen gearbeitet, die 100.000.000 Datensätze pro Tag verarbeiten, wobei eine Leistungsverbesserung von 1% Prozent enorm ist.

Ich habe eine große Lektion gelernt, an diesem System zu arbeiten: Schneller ist besser, aber nur wenn man weiß, wann es schnell genug ist.

Eine Verbesserung von 1% wäre eine enorme Reduzierung der Gesamtverarbeitungszeit gewesen, aber es reicht nicht aus, wenn wir unser nächstes Hardware-Upgrade benötigen. Meine Bewerbung war so schnell, dass die Zeit, die ich damit verbracht habe zu versuchen, die letzte 1% Milch zu melken, wahrscheinlich mehr kostet als ein neuer Server.

In Ihrem Fall müssten Sie do_something Zehntausende Male aufrufen, bevor Sie einen signifikanten Unterschied in der Leistung machen. In einigen Fällen würde das einen Unterschied machen, in anderen nicht.

2
  1. Sie haben einige Daten
  2. Sie haben ein Verfahren mit ihrem verbundenen
  3. Sie wollen nicht global nur die Daten zu halten, aus Gründen der die Geschwindigkeit des Verfahrens zu optimieren, wenn Sie zu haben.

Ich denke, das ist, was Klassen sind.

class Processor: 
    def __init__(this): 
     this.data = "any thing whatever".split() 
    def fun(this,arg): 
     # do stuff with arg and list 

inst = Processor() 
inst.fun("skippy) 

Auch, wenn Sie einen Tag die Daten in eine Datei trennen möchten, können Sie einfach den Konstruktor ändern, dies zu tun.

Verwandte Themen