Python rechnet uneffizient?

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

Okay, dann nehmen wir doch mal ein ganz einfaches, wirklich „straight forward“ implementiertes Sieb in Python, ohne irgendwas speziell zu optimieren:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
from math import sqrt


def sieve(limit):
    is_prime = [True] * limit
    is_prime[0] = is_prime[1] = False
    for i in xrange(2, int(sqrt(limit)) + 1):
        if is_prime[i]:
            for j in xrange(i * i, limit, i):
                is_prime[j] = False
    return (i for i, t in enumerate(is_prime) if t)


def main():
    print(sum(sieve(2000000)))


if __name__ == '__main__':
    main()
Und lassen das gegen das folgende C-Programm antreten, welches den Algorithmus von DerTürke implementiert:
[codebox=c file=Unbenannt.c]#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>


static _Bool is_prime(uint_fast32_t candidate)
{
uint_fast32_t i;

for (i = 2; i < candidate; ++i) {
if (candidate % i == 0) return false;
if (candidate / i < i) break;
}
return candidate > 1;
}


int main(void)
{
uint_fast64_t sum = 0;
uint_fast32_t i;

for (i = 1; i < 2000000; ++i) {
if (is_prime(i)) {
sum += i;
}
}

printf("%"PRIuFAST64"\n", sum);
return 0;
}[/code]

Kompiliert mit GCC und Optimierungen wird die Testfunktion direkt in die Hauptfunktion eingefügt, so dass dort der Funktionsaufruf eingespart wird. Ich habe den generierten Quelltext mal mit sprechenderen Label-Namen versehen und ein wenig kommentiert:
[codebox=asm file=Unbenannt.asm] .intel_syntax noprefix
.section .rodata.str1.1,"aMS",@progbits,1
.format_string:
.string "%lu\n"

.section .text.startup,"ax",@progbits
.p2align 4
.globl main
main:
; candidate = 1
mov esi, 1
; sum = 0
xor edi, edi
; Align loop start to 16 byte boundary if max 10 bytes away
; or to 8 byte boundary
.p2align 4,,10
.p2align 3
.outer_loop:
; if candidate <= 2
cmp rsi, 2
jbe .L8

; if candidate is even but not <=2 it is not prime
test sil, 1
je .is_not_prime

; if candidate is three then it is prime,
; else i = 2 and run inner loop
cmp rsi, 3
mov ecx, 2
jne .inner_loop_inc
jmp .is_prime
.p2align 4,,10
.p2align 3
.inner_loop:
; div/mod candidate and i
xor edx, edx
mov rax, rsi
div rcx
; if remainder then not prime
test rdx, rdx
je .is_not_prime
; if result of division < i then candidate is prime
cmp rax, rcx
jb .is_prime
.inner_loop_inc:
; increase i and if not reached candidate then test i
add rcx, 1
cmp rcx, rsi
jne .inner_loop
.is_prime:
; sum += candidate
add rdi, rsi
.is_not_prime:
; increase candidate and test if upper limit is hit
add rsi, 1
cmp rsi, 2000000
jne .outer_loop

; print sum
mov rdx, rdi
mov esi, OFFSET FLAT:.format_string
mov edi, 1
xor eax, eax
call __printf_chk

; return 0
xor eax, eax
ret
.L8:
; if candidate is 1 then candidate = 2
; otherwise candidate stays 2
cmp rsi, 1
mov eax, 2
cmove rsi, rax
jmp .is_prime[/code]

Wie man sieht entspricht das ziemlich dem was DerTürke an Assembler gezeigt hat. Etwas schräg ist der erste Test und der Code bei `.L8`. Das hätte man als Mensch wahrscheinlich anders geschrieben. Letztlich fällt das bei der Laufzeit aber kaum ins Gewicht, denn es betrifft nur einen von zwei Millionen Schleifendurchläufen.

Interessant ist, dass der Compiler selbst gemerkt hat, das ungerade Kandidaten ausser der 2 nicht zu einer Addition auf die Summe führen und das deswegen mit einem billigen Bit-Test gesondert behandelt, um die teurere Division für jede zweite Zahl zu sparen.

Und das Ergebnis: Python ist fast doppelt so schnell.

Um mal einen Fusballtrainer zu zitieren: Schön spielen ist nichts, wenn das Resultat nicht stimmt. Man kann sich viel Mühe geben handgeklöppelten Assembler zu schreiben, und dafür sicher ein Fleissbienchen verdienen, aber wenn man dann *deutlich* von einer dynamisch interpetierten Programmiersprache überholt wird, wo sogar noch der Compilerlauf von Quelltext nach Bytecode für die VM mit in der Laufzeit enthalten ist, dann war das wohl nix. Aber dicke Töne spucken, andere hätten keine Ahnung. Süss. :twisted:
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Für alle, die sich auf die schnelle weder C noch Assembler einlesen können, habe ich ein einfaches Sieb in Python umgesetzt und dem erwähnten 'algo' gegenübergestellt. Diesen habe ich zudem etwas modifiziert, damit die gleiche Datenstruktur zurück gegeben wird. Ansonsten habe ich den 'algo' unverändert gelassen. Es fällt sofort auf, dass dieser nicht schneller sein kann. Das Sieb berechnet die ersten 2000000 Primzahlen in unter 1 sec. Bei dem 'algo' wage ich so große Zahlen gar nicht erst.

Code: Alles auswählen

def sieve(n):
    primes = [True] * n
    for i in range(2, n):
        if primes[i]:
            for j in range(i+i, n, i):
                primes[j] = False
    return primes

def algo(n):
    primes = [True] * n
    for i in range(2, n):
        for j in range(2, i):
            if not i % j:
                primes[i] = False
                break
    return primes
BlackJack

@kbr: Du hast `algo` falsch umgesetzt. Du müsstest die innere Schleife abbrechen sobald ``i // j < j`` gilt. Das hat DerTürke gemacht um nur bis j nur bis sqrt(i) laufen zu lassen. Wobei sich das in Assembler anbietet weil man den Modulo-Wert und den Quotienten ”kostenlos” von der Division bekommt die man sowieso durchführt.
DerTürke

@Sirius:
Du hasst dir wohl meinen Code nicht angeschaut, ansonsten würdest du nicht behaupten ich hätte das Sieb ( was ja auch vom Schwierigkeitsgrad nicht sehr fordernd ist ) nicht implementiert, vielmehr habe ich auch ein 2 Array of int (also wirklich keine List kein vector oder der Gleichen) benutzt um auch die im Sieb ermittelten Primzahlen zu sammeln, das mit der Ahnung war von meiner Seite nicht so gemeint sorry falls ich dir damit zu Nahe getreten bin. Ich glaube ich bin hier sowieso fehl am Platz ich sollte mich am besten in Foren aufhalten wo man keine so große Antipathie gegen Assembler pflegt. Nochmal auch an BlackJack sorry wenn ich mit meinen Äußerungen euch zu Nahe getreten bin ich wünsche euch allen gutes gelingen und eine schöne Zeit.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@BlackJack: Stimmt, das hatte ich übersehen und mich schon gewundert. Diese Optimierung hatte ich ursprünglich auch angewandt, was am Ergebnis aber nichts ändert.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@DerTürke: ich hab Dir sogar gesagt, was Du falsch gemacht hast. Also zu behaupten, das wäre so einfach, dann eine falsche Implementierung zu zeigen und dann noch sich zu freuen, dass der »eigene Algo« schneller sei, ...
Aber hier zu behaupten, wir würden ein gutes Assemblerprogramm nicht wertschätzen, das ist eine unverschämte Unterstellung :D .
DerTürke

@Sirius
@DerTürke: Du hast immer noch kein Sieb implementiert. Der Trick ist ja, dass man gar keine Divisionen braucht. Die innere Schleife sollte ungefähr so aussehen:

Code: Alles auswählen


1.         n_Primes[n_PrimesIndex++] = n_Sieb[i];
2.         for (int j = i + i; j < 200000; j+=i)
3.         {
4.            n_Sieb[j] = 0;
5.         }
Damit wäre bewiesen, wer absolut keine Ahnung hat :evil:.
Ich wollte eigentlich dieses Thema mal so langsam abhaken, daß muss ich aber dann doch noch mal fragen Siri
Frage : "Wo in der Implementierung habe ich denn eine divison ?" , und dein Vorschlag wie ich es machen müsste
Zitat: "Der Trick ist ja, dass man gar keine Divisionen braucht. Die innere Schleife sollte ungefähr so aussehen:"
würde doch das ganze Array mit 0 füllen oder ? sehe ich da was nicht ?

Trotzdem vielen Dank für die Mühe
DerTürke

@Sirius:

Nein ok deine Schleife habe ich im nachhinein nochmal analysiert, dadurch das der Inkrement von j mit dem vielfachen von i durchgeführt wird, werden natürlich nur die vielfachen des zuvor emrittelten Primes auf 0 gesetzt.
sorry hasst komplett recht und dadurch sparst du dir den Modulo
ok habe kapiert, tatsächlich eine sehr gut Implementierung

Danke und Gruß
DT
BlackJack

@DerTürke: Wer hat denn hier Antipathien gegen Assembler? Ich mag Assembler! Habe hier im Thema doch schon eine Menge davon gepostet. Und in meinem Leben auch schon so einiges geschrieben und gelesen. Auch Sirius3 hat sich nicht gescheut auf diese hardwarenahe Ebene zu gehen und mal zu schauen und zu zeigen was ein optimierender Compiler für Code erzeugt und das der an der Stelle per Hand kaum zu verbessern ist.

Unser Problem mit Dir ist nicht, das Du eine Lösung in Assembler geschrieben hast, sondern das die unglaublich ineffizient ist. So ineffizient das eine in keiner Weise optimierte Python-Lösung *deutlich* schneller ist. Trotzdem verteidigst Du Deinen Assembler-Code mit der Argumentation das nichts an handgeschriebenen Assembler heran kommt. Selbst wenn das in diesem Fall stimmen *würde*, sind solche Mikrooptimierungen völlig unsinnig solange Du diesen grottenschlechten Algorithmus verwendest. Selbst bei dem hat der GCC geringfügig besseren Code produziert, in dem er in der innersten Schleife ein ``TEST reg, reg`` statt eines ``CMP reg, 0`` verwendet hat, wie ich weiter oben gezeigt habe.

So sieht das Sieb in C komplett & korrekt aus:
[codebox=c file=Unbenannt.c]#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

#define LIMIT 2000000

static _Bool is_prime[LIMIT];


int main(void)
{
uint_fast64_t sum = 0;
uint_fast32_t i, j;

memset(is_prime, true, sizeof(is_prime));
is_prime[0] = is_prime[1] = false;
for (i = 2; i < ((uint_fast32_t) sqrt(LIMIT)) + 1; ++i) {
if (is_prime) {
for (j = i * i; j < LIMIT; j += i) {
is_prime[j] = false;
}
}
}

for (i = 2; i < LIMIT; ++i) {
if (is_prime) {
sum += i;
}
}

printf("%"PRIuFAST64"\n", sum);
return 0;
}[/code]
Das ist 57× schneller als Dein Algorithmus!

Und der C-Compiler spuckt auch hier wieder ziemlich guten Assembler aus (von mir kommentiert):
[codebox=asm file=Unbenannt.asm] .intel_syntax noprefix
.section .rodata.str1.1,"aMS",@progbits,1
.format_string:
.string "%lu\n"

.section .text.startup,"ax",@progbits
.p2align 4
.globl main
main:
; set all entries in is_prime to true
mov edx, 2000000
mov esi, 1
mov edi, OFFSET FLAT:is_prime
call memset

; i = 2
mov edx, 2
.p2align 4,,10
.p2align 3
.outer_loop:
; test if is_prime is prime
cmp BYTE PTR is_prime[rdx], 0
je .not_prime
; j = i * i
mov rax, rdx
imul rax, rdx
.p2align 4,,10
.p2align 3
.inner_loop:
; is_prime[j] = false
mov BYTE PTR is_prime[rax], 0
; j += i
add rax, rdx
; is j < limit?
cmp rax, 1999999
jbe .inner_loop
.not_prime:
; i += 1
add rdx, 1
; is i == sqrt(LIMIT)?
cmp rdx, 1415
jne .outer_loop

; i = 2
mov eax, 2
; sum = 0
xor dx, dx
.p2align 4,,10
.p2align 3
.sum_loop:
; is is_prime == false?
cmp BYTE PTR is_prime[rax], 0
; tmp = sum + i
lea rcx, [rax+rdx]
; conditionally move tmp to sum if i is prime
cmovne rdx, rcx
; i += 1
add rax, 1
; if i <= LIMIT goto loop start
cmp rax, 2000000
jne .sum_loop

; print the sum
mov esi, OFFSET FLAT:.format_string
mov edi, 1
xor eax, eax
call __printf_chk
xor eax, eax
ret[/code]

Die Addition mit ``LEA`` und den bedingte ``MOV``-Befehl finde ich clever. Auf so etwas muss man als Mensch erst einmal kommen. Also entweder selbst, oder weil man diese Vorgehensweise schon mal gesehen und sich gemerkt hat.

Das die Register nicht immer als 64-Bit register behandelt werden, wenn sie nicht müssen, ist auch etwas was ein Compiler wunderbar hinbekommt, wo man als Mensch gerne mal Fehler macht wenn man Code entwickelt oder verändert. Beispielsweise das die 64-Bit-Summe die im rdx-Register aufsummiert wird, mit einem ``xor dx,dx`` auf 0 gesetzt wird, ist fehleranfällig bei Veränderungen von Quelltext davor, weil man an der Stelle ja sicher sein muss, das der Inhalt in dem Register zu dem Zeitpunkt kleiner als 65536 war. Das mag in diesem Fall recht offensichtlich sein, aber ein Compiler macht das halt immer richtig, egal von wievielen Punkten aus so ein Code angesprungen werden kann und wie weit der letzte Zugriff auf das Register auch entfernt sein mag.

Geschwindigkeitsvergleich ”grafisch”:
[codebox=text file=Unbenannt.txt]Laufzeit (< besser):

DerTürke-Algo *********************************************************
Python-Sieb ******************************
C-Sieb *[/code]
DerTürke
User
Beiträge: 4
Registriert: Montag 16. Mai 2016, 20:00

Hallo Jungs,

habe mich erkundigt und muss leider bestätigen das mein alter Alg.o grotten schlecht war.
Der neue ist von Miller Rabin super schnell ermittelt die Primzahlen von 1-1.000.000.000 in weinger als eine sek.

hier isser:
[codebox=cpp file=Unbenannt.cpp]#include<iostream>
#include<stdlib.h>

using namespace std;

//bool IsPrime(int pValue);
bool IsPrimeExt(uint64_t pZahl, int pBasis);

int main()
{
int m_Value;
for (int i = 10; i < 1000000001; i++)
{
bool bIsPrime = IsPrimeExt(i, 2) & IsPrimeExt(i, 7) & IsPrimeExt(i, 61);
/*
if (i % 100000000 == 0)
{
cout << "Zähler gerade bei " << i << endl;
}
*/
}
cout << "Fertig" << endl;
cin >> m_Value;
return 0;
}

bool IsPrimeExt(uint64_t pZahl, int pBasis)
{
bool retValue = false;
uint64_t m_ZahlMinus1 = pZahl - 1;
uint64_t m_Ungerade = m_ZahlMinus1;
int m_2T = 0;
while ((m_Ungerade & 1) == 0)
{
m_Ungerade >>= 1;
m_2T += 1;
}

uint64_t m_Power = pBasis;
uint64_t m_T = pBasis;
while (m_Ungerade >>= 1)
{
m_Power = m_Power*m_Power % pZahl;
if (m_Ungerade & 1)
{
m_T = m_T*m_Power % pZahl;
}
}

if ((m_T == m_ZahlMinus1) || (m_T == 1))
{
retValue = true;
}

for (int k = 1; k < m_2T; k++)
{
m_T = (m_T*m_T) % pZahl;
if(m_T == m_ZahlMinus1)
{
retValue = true;
break;
}
if (m_T <= 1)
{
retValue = false;
break;
}
}
return retValue;
}[/code]

Vielleicht kann man diesen auch in Python implementieren
Gruß
DerTürke
BlackJack

Kann man, aber der ist ein probalistischer Test, das heisst der kann nur sicher sagen was *keine* Primzahl ist, aber nicht was ganz sicher eine Primzahl ist.
DerTürke
User
Beiträge: 4
Registriert: Montag 16. Mai 2016, 20:00

Ja habe ich gerade erfahren,

bei den Primzahlen bis 100 fehlt die 13, 37 usw...
Schade war total begeistert von diesem nun ja weiter schauen.
DerTürke
User
Beiträge: 4
Registriert: Montag 16. Mai 2016, 20:00

Ich hab's nochmal getestet aber nur zu Basis 2 da klappt es seltsamer Weise sollte man lt. Rabin Miller aber mit 2 Basen probieren und zwar mit 2 und 3, Naja werde dann wohl doch bei meinem Grotten schlechten bleiben es sei denn ihr kennt einen Algo. ( bitte nicht das Sieb ) der effiezienter ist.

VG
DT
BlackJack

@DerTürke: Was hast Du gegen das Sieb? Wobei es ja nicht nur *das* Sieb gibt. Sieve of Atkins wäre auch eine Möglichkeit.

Oder verbessere Deinen Algorithmus. Der hat den Vorteil parallelisierbar zu sein. Damit bekomme ich den auf meinem alten Laptop an die Python Implementierung heran. Zwei Kerne + Hyperthreading. Mit mehr und oder echten Prozessoren/Kernen, kann der dann auch gegen Python gewinnen. :-) Wenn man das nicht in Assembler macht, geht das sogar sehr einfach. Bei dem C-Programm einfach ein OpenMP-Pragma um die äussere Schleife zu parallelisieren:
[codebox=c file=Unbenannt.c]int main(void)
{
uint_fast64_t sum = 0;
uint_fast32_t i;

#pragma omp parallel for reduction(+:sum)
for (i = 1; i < 2000000; ++i) {
if (is_prime(i)) {
sum += i;
}
}

printf("%"PRIuFAST64"\n", sum);
return 0;
}[/code]
DerTürke
User
Beiträge: 4
Registriert: Montag 16. Mai 2016, 20:00

Ja danke
gute Idee mit OpenMP.
Im Moment studiere ich gerade den Algorithmus von Magenheimer "Integer multiplication and Division on the HP Architecture" ich glaube wenn ich den verstehe dann kann ich die ineffiziente Division ( bis zu 200 Taktzyklen ) durch eine Multiplikation ( irgendwie mit Umkehrwert ) ersetzen, und dass ganze verpacke ich dann in VPMULQD (SIMD) 2 x 64 Bit (pseudo divs) naja ich habs auf jeden Fall vor, mal schaun wann ich die Lust dran verliere

VG
DT
BlackJack

Da der nicht wirklich gute Algorithmus mit OpenMP an die offensichtliche Python-Lösung herankommt, habe ich überlegt wie man die mit möglichst wenig Aufwand schneller bekommt. Dazu habe ich sie unverändert mit PyPy statt mit CPython laufen lassen. Ergebnis: Doppelt so schnell wie die OpenMP-Variante. :-)
[codebox=text file=Unbenannt.txt]Laufzeit (< besser):

DerTürke-Algo *********************************************************
Python-Sieve ******************************
DerTürke-Algo+OpenMP ******************************
Python-Sieve[PyPy] ***************
C-Sieve *[/code]
Benutzeravatar
pyHoax
User
Beiträge: 84
Registriert: Donnerstag 15. Dezember 2016, 19:17

Guten Tag zusammen,

Ich wollte euch mal zwei meiner optimierten Implementation des Primzahlenfilters vorstellen.
Die vanilla Variante verwendet nur buildins die Zweite einen import aus dem Itertools Module.

Als nächstes werde ich schauen wie das Problem partitioniert werden kann, für größere Primzahlen reicht der Speicher einfach nicht.

Finde Primzahlen bis: 10000000 , gemessen jeweils 10 Runden mit timeit. (Python2.7)

Code: Alles auswählen

timeit
referenz:  18.7140735757
vanilla:   4.04164260142
itertools: 2.06300881091
Der Code:

Code: Alles auswählen

import __builtin__
from sys import version_info
from itertools import compress

def prim_referenz(n):
    if n < 3:
        return [2] if n == 2 else []

    s = list(range(3, n + 1, 2)) if version_info.major > 2 else range(3, n + 1, 2)

    # n**0.5 simpler than math.sqr(n)
    mroot = n ** 0.5
    half = len(s)
    i = 0
    m = 3
    while m <= mroot:
        if s[i]:
            j = (m * m - 3) // 2  # int div
            s[j] = 0
            while j < half:
                s[j] = 0
                j += m
        i = i + 1
        m = 2 * i + 3
    return [2] + [x for x in s if x]


def prim_vanilla(n):
    if n < 3:
        return [2] if n == 2 else []

    range = __builtin__.range if version_info.major > 2 else __builtin__.xrange
    size = len(range(3, n + 1, 2))
    sieb = bytearray([True]) * size
    max = int(n ** 0.5) + 1

    for prim in ( 2 * i + 3 for i in range(0, max) if sieb[i] ):
        start = (prim ** 2 - 3) // 2
        sieb[start::prim] = bytearray([False]) * len(range(start, size, prim))

    primes= [2]
    primes.extend(3 + 2 * i for i,p in enumerate(sieb) if p)
    return primes


def prim_itertools(n):
    if n < 2:
        return [2] if n == 2 else []

    range = __builtin__.range if version_info.major > 2 else __builtin__.xrange
    size = len(range(3, n + 1, 2))
    sieb = bytearray([True]) * size
    max = int(n ** 0.5) + 1

    for prim in compress(range(3, max , 2), sieb):
        start = (prim ** 2 - 3) // 2
        sieb[start::prim] = bytearray([False]) * len(range(start, size, prim))

    prime_numbers = [2]
    prime_numbers.extend(compress(range(3, n + 1, 2), sieb))
    return prime_numbers


if __name__ == '__main__':
    import timeit
    print("timeit")
    print("referenz:  %s" % timeit.timeit("prim_referenz(10000000)", setup="from __main__ import prim_referenz", number=10))
    print("vanilla:   %s" % timeit.timeit("prim_vanilla(10000000)", setup="from __main__ import prim_vanilla", number=10))
    print("itertools: %s" % timeit.timeit("prim_itertools(10000000)", setup="from __main__ import prim_itertools", number=10))
Zuletzt geändert von Anonymous am Freitag 16. Dezember 2016, 08:26, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@pyHoax: Das mit dem `__builtin__` funktioniert so nicht, weil es das Modul in Python 3 nicht gibt:

Code: Alles auswählen

Python 3.3.6 (default, Dec 19 2014, 22:05:50) 
[GCC 4.6.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import __builtin__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named '__builtin__'
Man könnte das ganz ohne Importe einfach so lösen:

Code: Alles auswählen

try:
    range = xrange
except NameError:
    pass
Benutzeravatar
pyHoax
User
Beiträge: 84
Registriert: Donnerstag 15. Dezember 2016, 19:17

Man könnte das ganz ohne Importe einfach so lösen:
Danke.Das funktioniert aber nur, wenn ich es im Module und nicht im Methoden Kontext verwende.

Kann ich mit leben. Dafür sieht es sexy aus.

Im Methodenbody aufgerufen bekomme ich unter python3 dann folgenden Fehler:

Code: Alles auswählen

UnboundLocalError: local variable 'range' referenced before assignment
Interessant das python offensichtlich bereits beim Parsen der Methode Variablen deklariert, auch wenn sie nicht initilaisiert werden. Anders kann ich mir das so nicht erklären.

Ähnlich sieht es mit diesem Codeschnipsel aus:

Code: Alles auswählen

    if version_info.major < 3:
        range = __builtin__.xrange
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@pyHoax: alle Variablen, die irgendwo in einer Funktion definiert werden, sind lokal, egal ob sie zum Zeitpunkt des Zugriffs schon definiert wurden oder nicht. Woher soll auch Python wissen, ob ein if-Zweig genommen wird oder nicht.

Am besten nimmt man einen neuen Namen:

Code: Alles auswählen

    try:
        yrange = xrange
    except NameError:
        yrange = range
oder macht auf Modulebene einen Future-Port:

Code: Alles auswählen

    try:
        xrange
    except NameError:
        xrange = range
Antworten