AttributeError: 'str' object has no attribute 'uppper'

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.
Antworten
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Hallo,
die obige Fehlermeldung bekomme ich, wenn ich folgendes Script ausführe:

Code: Alles auswählen

def checkHex(hexStr):
        boolResult = True
        for chr in hexStr:
            if not (48 <= ord(chr) and ord(chr) <= 57) or (65 <= ord(chr.uppper()) and ord(chr.upper()) <= 70):
                boolResult = False 
                
        return boolResult

checkHex("10")
kann mir bitte jemand sagen was daran falsch ist? Danke.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

upper schreibt man mit 2 p, nicht mit 3. Außerdem überschreibst du die eingebaute Funktion chr, womit du auf die Nase fallen könntest. Die Fehlermeldung kommt aber vom falsch geschriebenen upper. Es ist übrigens unnötig, in jedem Fall die komplette Schleife zu durchlaufen. Wenn du False zurückgeben willst, dann tu das sofort.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Code: Alles auswählen

any(not (48 <= ord(c) <= 57) and not (65 <= ord(c.upper()) <= 70) for c in hex_string)
Vielleicht reicht ja aber auch schon `int(hex_string, 16)` fuer dein Vorhaben.

Ich glaube sogar, zu testen ob der Buchstabe im entsprechenden String enthalten ist, macht es lesbarer:

Code: Alles auswählen

any(c not in [chr(i) for i in range(48, 58) + range(65, 71)] for c in hex_string)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Code: Alles auswählen

def checkHex(hexStr):
    for c in hexStr.upper():
        if not ('0'<=c<='9' or 'A'<=c<='F'):
            return False
    return True

Code: Alles auswählen

def checkHex(hexStr):
    return all(c in '0123456789ABCDEF' for c in hexStr.upper())

Code: Alles auswählen

import re
def checkHex(hexStr):
    return re.search('[^0-9A-F]',hexStr,re.I) is None
Und der Geschwindigkeitssieger:

Code: Alles auswählen

import re
CHECK_HEX = re.compile('[^0-9A-Fa-f]').search
def checkHex(hexStr):
    return CHECK_HEX(hexStr) is None
Zuletzt geändert von Sirius3 am Sonntag 27. Januar 2013, 18:58, insgesamt 1-mal geändert.
karolus
User
Beiträge: 141
Registriert: Samstag 22. August 2009, 22:34

Hallo

Code: Alles auswählen

>>> from string import hexdigits
>>> hexset = set(hexdigits)
>>> set('abc0').issubset(hexset)
Karolus
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@hypnoticum: Was derdon gesagt hat.

Außerdem könnte man dein Programm pythonischer machen.

Variablen sollte man nicht so benennen, dass sie builtins überschreiben. Außerdem gibt es Operator Chaining. Und die Klammern um die Ausdrücke rechts und links vom or sind auch überflüssig:

Code: Alles auswählen

def checkHex(hexStr):
    boolResult = True
    for c in hexStr:
        if not (48 <= ord(c) <= 57 or 65 <= ord(c.upper()) <= 70):
            boolResult = False
    return boolResult
Schon besser. Allerdings gibt es die Funktion all(), mittels derer man boolResult loswerden kann:

Code: Alles auswählen

def checkHex(hexStr):
    return all(48 <= ord(c) <= 57 or 65 <= ord(c.upper()) <= 70 for c in hexStr)
Als nächstes fällt auf, dass die Aufrufe von ord() und c.upper() teuer sind und durch eine - zudem besser lesbare - Konstruktion ersetzt werden können, unter Zuhilfenahme des string-Moduls:

Code: Alles auswählen

from string import hexdigits

def checkHex(hexStr):
    return all(c in hexdigits for c in hexStr)
Wenn wir statt eines Strings eine Buchstabenmenge verwenden, können wir jetzt die all()-Konstruktion loswerden, weil Mengen schon eine entsprechende Methode mitbringen:

Code: Alles auswählen

from string import hexdigits

def checkHex(hexStr):
    return set(hexdigits).issuperset(hexStr)
Wenn wir das genau ansehen, stellen wir fest, dass checkHex() dieselbe Signatur besitzt, wie set(hex_chars).issuperset(), nämlich str --> bool. Also können wir checkHex auch einfach direkt mittels Zuweisung definieren:

Code: Alles auswählen

from string import hexdigits

checkHex = set(hexdigits).issuperset
Diesen Programmierstil nennt man point-free, oder auch tacit.
Jetzt müssen wir nun noch den Namen unserer Funktion PEP8-konform machen:

Code: Alles auswählen

from string import hexdigits

is_hex_string = set(hexdigits).issuperset
Das Ergebnis ist deklarativer, kürzer, expliziter und besser lesbar, und vielleicht sogar performanter, was ich allerdings nicht getestet habe.
In specifications, Murphy's Law supersedes Ohm's.
lunar

@pillmuncher Python ist nicht Haskell, und point-free ist kein sonderlich gute Idee. Eine derartige Namensbindung ist nicht equivalent zu einer Funktionsdefinition, da die Metadaten der Funktion nicht stimmen, insbesondere ihr Docstring. "help(is_hex_string)" ist bei Deiner Definition mehr verwirrend als hilfreich.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@lunar: Dasselbe Argument könntest du auch gegen jede Verwendung von functools.partial() anführen. Hier das Beispiel aus der Doku:

Code: Alles auswählen

>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'  # <-- !!!!!!!!!!!!!!!
>>> basetwo('10010')
18
>>> basetwo.__doc__
'Convert base 2 string to an int.'
>>> help(basetwo)
Help on partial object:

class partial(builtins.object)
 |  partial(func, *args, **keywords) - new function with partial application
 |  of the given arguments and keywords.
 |
 |  Methods defined here:
 |
 |  __call__(...)
 |      x.__call__(...) <==> x(...)
 | ...
Ich halte point-free style für pythonisch, denn wenn point-free style von den powers that be als nicht pythonisch angesehen würde, hätten sie functools.partial() sicherlich nicht in die std-lib aufgenommen.

Außerdem hängt es letztlich von der Aufgabenstellung ab. Wenn die Aufgabe lautet "schreib eine Funktion, die genau das macht", dann ist meine Implementation völlig korekt. Wenn die Aufgabe dagegen lautet "schreib eine Funktion, die genau das macht und zusätzlich einen doc string hat, der ihre Funktionsweise in den Begriffen von Strings und Hexadezimalzahlen beschreibt", dann ist eine point-free Implementation in Python fehlerhaft. Wenn functools.partial() so funktionieren würde, wie in der Dokumentation angedeutet, dann könnte man es etwa so machen:

Code: Alles auswählen

from string import hexdigits
from functools import partial

is_hex_string = partial(set(hexdigits).issuperset)
is_hex_string.__name__ = 'is_hex_string'
is_hex_string.__doc__ = 'Ein Prädikat, das angibt, ob der übergebene String ein gültiger Hexadezimalstring ist.'
Aber das funktioniert eben nicht richtig.
In specifications, Murphy's Law supersedes Ohm's.
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Ich danke euch für die Beantwortung meiner Frage.
Was ein Tippfehler so alles zu Tage bringt - hätte ich nicht gedacht, dass soviele Leute hier Vorschläge zu diesem Thema machen. Und ich kann wirklich was davon lernen :)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Code: Alles auswählen

import re

def check_hex(hs):
    return bool(re.match('[0-9a-f]+$', hs, re.I))
Stefan
lunar

@pillmuncher ".partial()" erzeugt ein neues, unabhängiges Objekt, und ist damit nahezu äquivalent zu einer richtigen Funktionsdefinition. Das ist der entscheidende Unterschied zu Deiner vorherigen Definition.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich sehe da, wie pillmuncher auch, keinen Konflikt. Sowohl bei ``partial`` als auch bei Referenzen auf Funktionen stimmt der Docstring nicht und wenn man einmal ``partial`` um die Referenz wrappt wird das IMHO nicht "pythonischer" nur weils jetzt ein separates Objekt ist.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Man kann sich partial() natürlich auch selber definieren:

Code: Alles auswählen

from string import hexdigits

def partial(f, *a0, **k0):
    def curried(*a1, **k1):
        k0.update(k1)
        return f(*(a0 + a1), **k0)
    return curried

is_hex_string = partial(set(hexdigits).issuperset)
is_hex_string.__doc__ = 'Ein Prädikat, das angibt, ob der übergebene String ein gültiger Hexadezimalstring ist.'
is_hex_string.__name__ = 'is_hex_string'
Dann geht es halbwegs:

Code: Alles auswählen

>>> from is_hex import is_hex_string
>>> is_hex_string('44af')
True
>>> help(is_hex_string)
Help on function is_hex_string in module is_hex:

is_hex_string(*a1, **k1)
    Ein Prädikat, das angibt, ob der übergebene String ein gültiger Hexadezimalstring ist.
(END)
Nur die Parameterliste ist irreführend.
In specifications, Murphy's Law supersedes Ohm's.
Antworten