Seite 1 von 1

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

Verfasst: Sonntag 27. Januar 2013, 18:02
von hypnoticum
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.

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

Verfasst: Sonntag 27. Januar 2013, 18:10
von derdon
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.

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

Verfasst: Sonntag 27. Januar 2013, 18:33
von cofi

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)

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

Verfasst: Sonntag 27. Januar 2013, 18:49
von Sirius3

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

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

Verfasst: Sonntag 27. Januar 2013, 18:58
von karolus
Hallo

Code: Alles auswählen

>>> from string import hexdigits
>>> hexset = set(hexdigits)
>>> set('abc0').issubset(hexset)
Karolus

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

Verfasst: Sonntag 27. Januar 2013, 19:05
von pillmuncher
@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.

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

Verfasst: Sonntag 27. Januar 2013, 19:51
von 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.

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

Verfasst: Sonntag 27. Januar 2013, 20:53
von pillmuncher
@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.

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

Verfasst: Sonntag 27. Januar 2013, 20:58
von hypnoticum
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 :)

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

Verfasst: Montag 28. Januar 2013, 00:02
von sma

Code: Alles auswählen

import re

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

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

Verfasst: Montag 28. Januar 2013, 09:34
von 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.

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

Verfasst: Montag 28. Januar 2013, 16:25
von Leonidas
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.

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

Verfasst: Montag 28. Januar 2013, 16:54
von pillmuncher
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.