[Python-de] auf Liste testen

Ulrich Berning ulrich.berning at desys.de
Fre Dez 16 11:51:17 CET 2005


Diez B. Roggisch schrieb:

>On Thursday 15 December 2005 15:12, Andreas Jung wrote:
>  
>
>>--On 15. Dezember 2005 15:01:47 +0100 Jochen Schulz <ml at well-adjusted.de>
>>
>>wrote:
>>    
>>
>>>Aber willst Du das wirklich? In Python versucht man meist gerade keinen
>>>bestimmten Typ vorzuschreiben, sondern probiert einfach die Operationen
>>>auf dem Objekt, die man machen will.
>>>      
>>>
>>Wer hat Dir denn diesen Blödsinn beigebracht? Bis Du PHP/Perl
>>Programmierer? :-)
>>    
>>
>
>So einen Bloedsinn verzapft uA Alex Martelli, Autor des Cookbooks und sicher 
>einer der profiliertesten Python-Coder weltweit:
>
>http://groups.google.com/group/comp.lang.python/browse_frm/thread/82b36a09772bb37/e230ca916be58835?lnk=st&q=martelli+duck+typing+python&rnum=6&hl=en#e230ca916be58835
>
>  
>
Duck-Typing bedeutet keinesfalls, dass man einfach Operationen auf einem 
Objekt ausfuehrt, ohne zu pruefen, ob das Objekt diese Operationen auch 
zur Verfuegung stellt. Duck-Typing bedeutet lediglich, dass man nicht 
den Typ eines Objektes erfragt, um vom Typ des Objektes auf die 
implementierte Schnittstelle zu schliessen, sondern man prueft 
stattdessen, ob das Objekt die benoetigte Schnittstelle implementiert. 
Die Betonung liegt auf pruefen.
Eine Operation einfach zu probieren und eventuelle Fehler abzufangen 
wuerde ich eher Try-And-Error nennen und hat mit Duck-Typing nicht viel 
zu tun.

Zitat:
"In other words, don't check whether it IS-a duck: check whether it 
QUACKS-like-a duck, WALKS-like-a duck, etc, etc, depending on exactly 
what subset of duck-like behaviour you need to play your language-games 
with."

Allen, die vor der Ausfuehrung von Operationen nicht pruefen wollen 
(egal ob Typ oder Schnittstelle), sondern lieber die Operation einfach 
ausfuehren und den Fehlerfall mit try/except fangen, moechte ich das 
angehaengte Script nahelegen. Mal abgesehen davon, dass ich 
Try-And-Error fuer ganz schlechten Stil halte und es der Lesbarkeit 
nicht gerade zutraeglich ist, hat das Vefahren auch erhebliche 
Auswirkungen auf die Performance.

Ich halte mich an folgende Regeln:
1.) Ich verwende type() wenn moeglich oder zwingend notwendig.
2.) Ich verwende isinstance() wenn type() nicht geht (wg. Ableitungen).
3.) Duck-Typing kommt nur dann zur Anwendung, wenn ich vom Typ eines 
Objektes nicht auf seine implemetierte Schnittstelle schliessen kann, z. 
B. wenn die Schnittstelle eines Objektes zur Laufzeit erzeugt oder 
veraendert wird.
4.) Try-And-Error vermeide ich moeglichst immer.

---
Ulli



-------------- nächster Teil --------------
import time

# Diese Konvertierungs-Funktion verwendet type(),
# um den Typ des Objektes zu erfragen. Das ist nur
# dann brauchbar, wenn ich nicht mit Ableitungen
# des Typs rechnen muss.
def f1(obj):
    if type(obj) == str:
        return obj.lower()
    elif type(obj) == int:
        return hex(obj)
    elif type(obj) == float:
        return abs(obj)
    return obj

# Diese Konvertierungs-Funktion verwendet isinstance(),
# um den Typ des Objektes zu erfragen. Das funktioniert
# auch bei Ableitungen des Typs.
def f2(obj):
    if isinstance(obj, str):
        return obj.lower()
    elif isinstance(obj, int):
        return hex(obj)
    elif isinstance(obj, float):
        return abs(obj)
    return obj

# Dies ist Duck-Typing. Anstatt den Typ des Objektes zu
# erfragen und vom Typ des Objektes auf die implemetierte
# Schnittstelle zu schliessen, wird die Schnittstelle
# des Objektes erfragt. Man kann schon erkennen, dass das
# aufgrund der unten definierten Regeln problematisch werden
# kann.
def f3(obj):
    if hasattr(obj, 'lower'):
        return obj.lower()
    elif hasattr(obj, '__hex__'):
        return hex(obj)
    elif hasattr(obj, '__abs__'):
        return abs(obj)
    return obj

# Dies ist Try-And-Error. Wir versuchen einfach die
# Operation auszufuehren und fangen auftretende Fehler ab.
def f4(obj):
    try:
        return obj.lower()
    except (AttributeError, TypeError):
        pass
    try:
        return hex(obj)
    except (AttributeError, TypeError):
        pass
    try:
        return abs(obj)
    except (AttributeError, TypeError):
        pass
    return obj

#
# Main
#
if __name__ == '__main__':
    records = ((("A", "B", "C", 1, 2, 3, 0.1, 0.2, -0.3, None,
                 "D", "E", "F", 4, 5, 6, 0.4, 0.5, -0.6, None),)*100000)
    print "Verarbeite %d Datensaetze mit %d Spalten..." % (len(records), len(records[0]))
    print
    print "Regeln:"
    print "1.) Alle Strings in Kleinbuchstaben konvertieren."
    print "2.) Alle Integers in Hex-Strings konvertieren."
    print "3.) Von allen Floats wird der Absolutwert gebildet."
    print "4.) Alle anderen Typen bleiben unmodifiziert."
    print
    
    start_time = time.time()
    for record in records:
        for column in record:
            f1(column)
    print "%.3f Sek. mit type()" % (time.time() - start_time)

    start_time = time.time()
    for record in records:
        for column in record:
            f2(column)
    print "%.3f Sek. mit isinstance()" % (time.time() - start_time)

    start_time = time.time()
    for record in records:
        for column in record:
            f3(column)
    print "%.3f Sek. mit hasattr()" % (time.time() - start_time)

    start_time = time.time()
    for record in records:
        for column in record:
            f4(column)
    print "%.3f Sek. mit try/except" % (time.time() - start_time)