exec - Globale Variable; und Performance Problem

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

Hallo,
Ich habe vor kurzem mit Python angefangen (komme von BASIC :roll: ).
Ich habe ein Programm zum errechnen von Primzahlen geschrieben.
Es errechnet mir Primzahlen unter 1.000.000 und schreibt mir dann alle Primzahlen in eine Datei (Primefile (prime.txt)). Soweit tut es das auch.
Jetzt will ich aber z.B. Primzahlen unter 2.000.000 ausrechnen und habe die unter 1 Million schon vorher berechnet und will sie nicht neu berechnen müssen.
Deswegen habe ich eine Datei (prim4File (prime4.txt)) in der die Primzahlen über 65535 in dieser Form: "prime4bytes = array('L', [...L,...L,...L])"
Diese Datei führe ich per exec aus und ich bekomme einen NameError zurück, schätzungsweise weil "prime4bytes" eine lokal im exec skript bleibt.
Und das geht auch nicht:

Code: Alles auswählen

prime4bytes = exec "array('L',[...L,...L,...L])"
Wie dann?

Außerdem habe ich mir gedacht Rechenzeit zu sparen in dem ich sage wenn bei n/i i > n/2 n%i != 0 (Zeile 8, 29, 34) aber da bekomme ich falsche bzw. zu wenige Zahlen zurück. Warum?

hier mal der Code:

Code: Alles auswählen

from array import * #import array

prime2bytes = array('H',[3])  # 'H' is unsigned Integer; 2 not needed
append2 = prime2bytes.append

def prime2 (n):
    for i in prime2bytes:
        #if i > n/2: 
            #return False
        if n % i == 0: 
            return False
    append2(n)

map(prime2, xrange(5,65535,2)) # 256**2 == 65536; only uneven numbers needed


prime4File = open('prime4.txt','r')
if prime4File.read() != "":
    exec prime4File.read()
    highest = prime4bytes[-1] + 2
else:
    prime4bytes = array('L')
    highest = prime2bytes[-1] + 2
prime4File.close
append4 = prime4bytes.append

def prime4 (n):
    for i in prime2bytes:
        #if i > n/2: 
            #return False
        if n % i == 0: 
            return False
    for i in prime4bytes:
        #if i > n/2: 
            #return False
        if n % i == 0: 
            return False
    append4(n)
    print n

map(prime4, range(highest,1000000,2)) # 256**4 == 4294967296;

prime2bytes.insert(0,2) # insert 2

Primefile = open('prime.txt','w')
Primefile.write(str(prime2bytes).lstrip("array('H', [").rstrip("])"))
Primefile.write(", " + \
str(prime4bytes).lstrip("array('L', [").rstrip("])").translate(None,'L'))

prime4File = open('prime4.txt','w')
prime4File.write('prime4bytes = ' + str(prime4bytes))
MfG Tompazi

PS: Ich habe noch ein paar algemeine Fragen;
kann ich Python Skripte zu ausführbaren Programmen kompilieren (sodass der Ausführende kein Python-Interpreter braucht)? Für Windows hab ich py2exe gefunden. Gibt es das auch für Linux; bzw. OS-X?

Wie handle ich Bilder? lib zum en/decoden von JPG; PNG; TGA etc.? und das so, dass ich die Bilder auf Canvas Objekte von TKinter Zeichnen kann?

MfG Tompazi

PPS: Sorry; ich hoffe, dass der Code jetzt leserlicher ist.
Zuletzt geändert von Tompazi am Sonntag 2. August 2009, 13:01, insgesamt 1-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hallo,

also der Code sieht sehr unaufgeräumt um nicht zu sagen grausam aus. Schau Dir mal PEP8 an und versuche Dich an diesen Style-Guide zu halten.

Wenn Du bereits errechnete Ergebnisse persistent speichern möchtest, nutze doch das pickle-Modul oder JSON o.ä. Damit kannst Du sehr einfach Datenstrukturen in eine Datei schreiben. Diese kannst Du dann nach dem Start Deines Scriptes einlesen und an der Stelle weitermachen, die als letzte in der Datei stand.
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

Danke für den pickle Tip, aber mit dem Code (zuerst alle unter 200.000 mit pickle abgespeichert) aber dann (beim 2ten durchlauf, von 200.000 - 300.000) bekomme ich gleich einen EOFError.

Code: Alles auswählen

from array import * #import array
import pickle

prime2bytes = array('H',[3])  # 'H' is unsigned Integer; 2 not needed
append2 = prime2bytes.append

def prime2 (n):
    for i in prime2bytes:
        #if i > n/2: 
            #return False
        if n % i == 0: 
            return False
    append2(n)

map(prime2, xrange(5,65535,2)) # 256**2 == 65536; only uneven numbers needed


prime4File = open('prime4.txt','rb')
if prime4File.read() != "":
    prime4bytes= pickle.load(prime4File)
    highest = prime4bytes[-1] + 2
else:
    prime4bytes = array('L')
    highest = prime2bytes[-1] + 2
prime4File.close
append4 = prime4bytes.append

def prime4 (n):
    for i in prime2bytes:
        #if i > n/2: 
            #return False
        if n % i == 0: 
            return False
    for i in prime4bytes:
        #if i > n/2: 
            #return False
        if n % i == 0: 
            return False
    append4(n)
    print n

map(prime4, range(highest,300000,2)) # 256**4 == 4294967296;

prime2bytes.insert(0,2) # insert 2

Primefile = open('prime.txt','w')
Primefile.write(str(prime2bytes).lstrip("array('H', [").rstrip("])"))
Primefile.write(", " + \
str(prime4bytes).lstrip("array('L', [").rstrip("])").translate(None,'L'))

prime4File = open('prime4.txt','wb')
pickle.dump(prime4bytes,prime4File)
MfG Tompazi
PS: Ich hoffe der Code schaut jetzt besser aus.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Leicht besser ;-)

Schreibe Kommentare immer über Zeilen.

Vermeide Sternchen-Importe!

Ich kann zum Fehler nicht direkt etwas sagen, aber versuche das doch mal die Datei aus einer Python-Shell zu pickeln. Vielleicht kommst Du dann dahinter, wo das Problem ist.

Generell sieht der Code immer noch nicht wirklich gut aus.

Versuche doch mal die Funktionen sinnvoll zu bennen und alle oben im Modul zu platzieren. Dann baue Dir eine main-Funktion und rufe die mit folgendem hook auf:

Code: Alles auswählen

def main():
    """
    Benutze Doc-Strings!
    """

# Damit vermeidest Du Code auf Modulebene. Für mehr Infos schau mal ins wiki!
if __name__ == "__main__":
    main()
In diese main()-Funktion packst Du dann die Teile rein, die Du davor auf Modul-Ebene stehen hattest.
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

Schreibe Kommentare immer über Zeilen.
Ok
Vermeide Sternchen-Importe!
Ist doch angenehmer ...
Versuche doch mal die Funktionen sinnvoll zu bennen und alle oben im Modul zu platzieren.
Ok

Dann baue Dir eine main-Funktion und rufe die mit folgendem hook auf:
...
In diese main()-Funktion packst Du dann die Teile rein, die Du davor auf Modul-Ebene stehen hattest.
Also das finde ich umständlich.. :?
Ich kann zum Fehler nicht direkt etwas sagen, aber versuche das doch mal die Datei aus einer Python-Shell zu pickeln. Vielleicht kommst Du dann dahinter, wo das Problem ist.
Werd ich machen.

Danke

MfG Tompazi
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Tompazi hat geschrieben:
Vermeide Sternchen-Importe!
Ist doch angenehmer ...
Nur auf den ersten Blick! Denk mal drüber nach, was passiert, wenn es in zwei Modulen eine Funktion foo() gibt! Der spätere import überschreibt dann gnadenlos die des früheren.
Zudem kann ein dritter ohne Sternchen-Importe leichter erkennen, aus welchem Modul eine Funktion stammt.

Code: Alles auswählen

# mod_a
def foo():
    return "Dolle Funktion aus mod_a"

Code: Alles auswählen

# mod_b
def foo():
    return "Dolle Funktion aus mod_b"

Code: Alles auswählen

from mod_a import *
from mod_b import *

foo()
Na, was wird das Ergebnis sein?

besser:

Code: Alles auswählen

import mod_a
import mod_b

mod_a.foo()
mod_b.foo()
oder aber zur Not:

Code: Alles auswählen

from mod_a import foo
from mod_b import foo as foo_b

foo()
foo_b()
Ist auch nicht ideal, da man die Namen verändert, aber immerhin wird hier nichts überschrieben.
Tompazi hat geschrieben:
Dann baue Dir eine main-Funktion und rufe die mit folgendem hook auf:
...
In diese main()-Funktion packst Du dann die Teile rein, die Du davor auf Modul-Ebene stehen hattest.
Also das finde ich umständlich.. :?
Das mag sein, aber so ist es sauber!
Wenn man Dein Modul importiert, wird sehr viel Code ausgeführt, den man vermutlich nicht ausführen wollte. Vermutlich will ich nur die Berechnungsfunktion nutzen - ohne Deine konkreten Dateien usw.
Mit diesem hook wird es sauberer, da die main()-Funktion nur dann aufgerufen wird, wenn man das ganze ausführt. Schau dazu mal ins wiki und/oder nutze die SuFu hier im Board. Da sollte es viel Infos dazu geben.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

So, habe mal Deinen Code überflogen. Schau Dir mal in der Doku noch einmal an, was map() genau macht... genauer, was diese Funktion zurückliefert! Du nutzt das Ergebnis nämlich nicht ;-)
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

Schau Dir mal in der Doku noch einmal an, was map() genau macht... genauer, was diese Funktion zurückliefert! Du nutzt das Ergebnis nämlich nicht Wink
Liefert eine Liste. ich Verwende Arrays: TypeError.. Wie mache eine Liste zu Arrays kompatibel?

Pickle geht noch immer nicht..

Code: Alles auswählen

import array
import pickle

def prime2 (n):
    """
    Kontrolliert Zahlen unter 65535.
    Wenn sie Primzahlen sind dann werden sie zum Array prime2bytes angehaengt.
    """
    for i in prime2bytes:
        #if i > n/2: 
            #return None
        if n % i == 0: 
            return None
    append2(n)

def prime4 (n):
    """
    Kontrolliert Zahlen unter 4294967295.
    Wenn sie Primzahlen sind dann werden sie zum Array prime4bytes angehaengt.
    """
    for i in prime2bytes:
        #if i > n/2: 
            #return None
        if n % i == 0: 
            return None
    for i in prime4bytes:
        #if i > n/2: 
            #return None
        if n % i == 0: 
            return None
    append4(n)
    print n

# 'H' is unsigned Integer (2 Bytes); 2 not needed
prime2bytes = array.array('H',[3])

#Lade pickle Datei
prime4File = open('prime4.txt','rb')
if prime4File.read() != "":
    prime4bytes= pickle.load(prime4File)  #geht nicht ..
    highest = prime4bytes[-1] + 2
else:
    prime4bytes = array.array('L')
    highest = prime2bytes[-1] + 2
prime4File.close

#Append Methode kuerzen.
append2 = prime2bytes.append
append4 = prime4bytes.append

#Check prime numbers
map(prime2, xrange(5,65535,2))
map(prime4, range(highest,200000,2))

# insert 2
prime2bytes.insert(0,2) 

#Write in File
Primefile = open('prime.txt','w')
Primefile.write(str(prime2bytes).lstrip("array('H', [").rstrip("])"))
Primefile.write(", " + \
str(prime4bytes).lstrip("array('L', [").rstrip("])").translate(None,'L'))

#Write Pickle file
prime4File = open('prime4.txt','wb')
pickle.dump(prime4bytes,prime4File)
hoffentlich schöner :wink:

MfG Tompazi

PS: Pickle speichern geht auslesen nicht... EOFError. Liegt das daran, dass ich einen Array pickle?
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Warum verwendest du überhaupt arrays und keine Listen?
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

Weil's schneller ist.

Code: Alles auswählen

    for i in prime2bytes:
        if i > n/2: #### Wieso
            return None ### geht das nicht.
        if n % i == 0:
            return None
    append2(n)
Ich bin sicher einfach nur zu doof um den Fehler zu finden..

MfG Tompazi
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Code: Alles auswählen

In [1]: def test(obj):
   ...:     t=time.time()
   ...:     for i in xrange(1000000):
   ...:         obj.append(i)
   ...:     print time.time()-t

Code: Alles auswählen

In [15]: test(array.array('i'))
0.377683162689

In [16]: test([])
0.226224899292
Ist die Liste da nicht schneller, oder verstehe ich da etwas falsch?
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Generell solltest Du Dir vllt. wegen des Verfahrens Gedanken machen (Stichwort: Siebeverfahren).

Und Dein map-Gebrauch ist zwar funktional schön anzuschauen aber trotzdem komisch, da Du auf Seiteneffekte abzielst. Der imperative Weg wäre hier erstmal der offensichtlichere. (Kann sein, daß die map-Variante sogar die schnellere ist)
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

Code: Alles auswählen

def test(obj):
    t = time.time()
    for i in xrange(65535):
     obj.append(i)
    for i in obj: i / 2
    print time.time() - t

MyArray = array.array('H')
MyList = []

test(MyArray)
0.0593538284302
test(MyList)
0.0522310733795
Na, dann bau ich meinen Code halt um, obwhol es etwas verwirrend ist, dass eine Liste schneller als ein Array ist..

MfG Tompazi
BlackJack

@Tompazi: Wenn Du auf eine ganze Zahl in einer Liste zugreifst, bekommst Du direkt das Objekt, welches dort referenziert wird. Bei einem Array muss bei jedem Zugriff zwischen einem `int`-Exemplar und einer Low-Level-Bitdarstellung einer Zahl umgewandelt werden.

Du solltest generell erst einmal alle Gedanken an Geschwindigkeit und Speicherverbrauch fallen lassen, die nicht über einen algorithmischen Ansatz gehen. Sonst ist Python einfach die falsche Sprache. "Pythonistas" legen Wert auf sauberen, verständlichen Quelltext. Erst wenn der steht und sauber läuft, kann man messen wieviel man mit solchen Mikrooptimierungen herausholen kann, oder überhaupt muss, und ob es den hässlicheren Quelltext wirklich wert ist.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Dies hier:

Code: Alles auswählen

if prime4File.read() != "": 
    exec prime4File.read() 
kann nicht funktionieren, denn das erste `read` liest ja schon die ganze Datei, sodass das zweite `read` nie mehr etwas lesen kann und immer ein Leerstring liefert.

Stefan
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

So ich habs mal auf List umgeschrieben.
Jetzt hab ich das Problem das ich lauter None Objekte in meine Liste bekomme.. für jede nicht Primzahl... Was kann ich dagegen tun?

Code: Alles auswählen

#!/usr/bin/env python 
import pickle
import time

def prime (n):
    for i in primeList:
        #if i > n/2: 
            #return None
        if n % i == 0: 
            return
        return n


primeList = [1]

#Lade pickle Datei
primePickleFile = open('primePickle.txt','rb')
primeList= pickle.load(primePickleFile)
primePickleFile.close

highest = primeList[-1] + 2

#Check prime numbers
t = time.time()
primeList = map(prime, xrange(highest,100000,2))
print time.time() - t

# insert 2
primeList.insert(0,2) 

#Write in File
Primefile = open('prime.txt','w')
Primefile.write(str(primeList).translate(None,'[]'))

#Write Pickle file
primePickle = open('primePickle.txt','wb')
pickle.dump(primeList,primePickle)
@sma:
typisch anfäger Fehler :x thx

MfG Tompazi
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

Jetzt hab ich es ganz versaut... er gibt mir nur mehr None zurück...

MfG Tompazi
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Tompazi hat geschrieben: Jetzt hab ich das Problem das ich lauter None Objekte in meine Liste bekomme.. für jede nicht Primzahl... Was kann ich dagegen tun?
Du schmeist diesen map()-Kram raus und testest explizit den Rückgabewert:

Code: Alles auswählen

for value in xrange(highest,100000,2):
    tmp_value = prime(value)
    if tmp_value:
        # primeList ist nicht PEP8 konform! Besser prime_values z.B.
        primeList.append(tmp_value)
Deine prime-Funktion kann man auch vereinfachen:

Code: Alles auswählen

def prime (n):
    for i in primeList:
        if not n % i:
            return n
EDIT:
So, musste mal wieder in den itertools wühlen:

Code: Alles auswählen

In [1]: from itertools import ifilter

In [2]: def foo(value):
   ...:     if value % 2:
   ...:         return value
   ...:
   ...:

In [3]: list(ifilter(foo, [1,2,3,4,5,6]))
Out[3]: [1, 3, 5]
Damit kann man das auch schön lösen :-)
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

zum ersten; ok;
zum zweiten nein weil das gibt mir keine Primzahl zurück.

das dritte verstehe ich nicht :oops:

Code: Alles auswählen

#!/usr/bin/env python 
import pickle
import time
import math

def prime (n):
    for i in prime_numbers:
        if i > math.sqrt(n):
            if n % i == 0:
                return None
        return n

prime_numbers = [3]
"""
#Lade pickle Datei
primePickleFile = open('primePickle.txt','rb')
prime_numbers= pickle.load(primePickleFile)  #geht nicht ..
primePickleFile.close
"""

highest = prime_numbers[-1]

#Check prime numbers
t = time.time()
for value in xrange(highest,100000,2):
    tmp_value = prime(value)
    if tmp_value:
        prime_numbers.append(tmp_value) 
print time.time() - t

# insert 2
prime_numbers.insert(0,2) 

#Write in File
Primefile = open('prime.txt','w')
Primefile.write(str(prime_numbers).translate(None,'[]'))

#Write Pickle file
primePickle = open('primePickle.txt','wb')
pickle.dump(prime_numbers,primePickle)
MfG Tompazi
Zuletzt geändert von Tompazi am Sonntag 2. August 2009, 17:21, insgesamt 1-mal geändert.
Benutzeravatar
Tompazi
User
Beiträge: 19
Registriert: Sonntag 2. August 2009, 10:05

Meine Primzahlen sind nicht korrekt.. :cry:
*seufz*

Warum nur?

MfG Tompazi

PS: Ich habe noch ein paar algemeine Fragen;
kann ich Python Skripte zu ausführbaren Programmen kompilieren (sodass der Ausführende kein Python-Interpreter braucht)? Für Windows hab ich py2exe gefunden. Gibt es das auch für Linux; bzw. OS-X?

Wie handle ich Bilder? lib zum en/decoden von JPG; PNG; TGA etc.? und das so, dass ich die Bilder auf Canvas Objekte von TKinter Zeichnen kann?
Antworten