Woher kommt das Type = NoneType?

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.
Karl
User
Beiträge: 252
Registriert: Freitag 29. Juni 2007, 17:49

Woher kommt das Type = NoneType?

Beitragvon Karl » Dienstag 10. Juni 2008, 20:24

Ich bin gerade dabei, einen Brainfuckinterpreter in Python zu schreiben.
Ich hatte schon ein oder 2 Denkfehler die mich viel Zeit gekostet haben, bis ich endlich mal drauf gekommen bin, worauf ich nicht geachtet hab ... Aber das hier übersteigt meine Fähigkeiten oO
Die Variable i ist immer int, bis dann plötzlich mal ein TypeError auftritt, der mir sagt, dass ``i += 1`` ungütlig ist, da i vom Typ NoneType ist.

Ich poste jetzt nur mal den wichtigsten Teil:

Code: Alles auswählen

    def loop_begin(self, i):
        if self.array[self.pointer] == 0:
            x = 1
            for i in xrange(len(self.code)):
                if self.code[i] == "[":
                    x += 1
                if self.code[i] == "]":
                    x -= 1
                    if x == 0:
                        return i
        else:
            self.tmp.append(i)
            return i
               

    def loop_end(self, i):
        if self.array[self.pointer] == 0:
            self.tmp.pop()
            return i
        else:
            x = self.tmp.pop()
            self.tmp.append(x)
            return x
       

    def run(self):
        output = ""
        i = 0
        while i < len(self.code):
            if self.code[i] == "+":
                self.increase_field()
               
           [...]

            elif self.code[i] == "[":
                i = self.loop_begin(i) #HIER
               
            elif self.code[i] == "]":
                i = self.loop_end(i)
            i += 1
        print output
           

   
x = Brainfuck()
x.code = ",->,>+>>+<<<[>>>[<<<<+>>>>-]<<[-]<<[>>+<<-[>>>>+<<<<-]]>-]>[++++++.[-]]"
x.run()

Ich hab mal alles andere, was nicht's mit den Schleifen [] zu tun hat, ausgelassen.
Kann ich aber, wenn's funktioniert auch mal reinstellen, um mich anmeckern zu lassen :)
Wenn der Code an manchen Stellen etwas komplizierter geschrieben ist, als nötig, dann tut's mir leid ;)
Jedenfalls hab ich durch einsetzen von ``print`` an allen möglichen Stellen herausgefunden, wo genau das Problem liegt.
Unmittelbar, nachdem in der Methode ``self.run()`` die Zeile ``i = self.loop_begin(i)`` ausgeführt wird (im Code mit einem Kommentar geschildert, damit ihr's direkt findet), scheint i = NoneType zurückgegeben zu werden.
Innerhalb der Methode passiert scheinbar nichts mehr, denn ein ``print`` vor jedem return erzielt keinen Effekt.
Direkt danach hat i den Typ NoneType ...

Woran liegt das?
Ich versteh das nicht ...
Danke schonmal für eure Hilfe :)
Benutzeravatar
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Beitragvon Joghurt » Dienstag 10. Juni 2008, 20:58

Der erste Teil der if-Bedingung in loop_begin hat nicht für alle Fälle ein return. In den Fällen führt das zu einem impliziten return None, und somit ist i dann auch None
Benutzeravatar
Hyperion
Moderator
Beiträge: 7471
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Beitragvon Hyperion » Dienstag 10. Juni 2008, 21:00

Du benutzt i zum einen als Zählvariable und dann weist Du i gleichzeitig Werte aus einer Methoden zu? Ich wette da passiert das Unheil ... woher weißt Du, dass da immer etwas sinnvolles zurückkommt?

Desweiteren könnte etwas im gekürzten Teil passieren, was i nicht gut tut! Denn die Bedingungen, die Du hier aufführst müssen ja nicht zwangsweise eintreten.

Woher weißt Du, dass der Fehler genau da auftritt, wo Du ihn markierst? Und wo tritt er auf? Bei der Zuweisung wohl doch kaum! Und ansonsten muss es ja in der Methode loop_begin passieren ...

Wie gesagt, das i += 1 weiter unten sagt ja noch nichts darüber aus, wo das i zu None wird!

edit: Zu langsam!

Und Jogurt hat wohl Recht!
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Beitragvon numerix » Dienstag 10. Juni 2008, 21:03

Das ist einfach erklärt:
In Python liefert JEDE Funktion einen Rückgabewert. Wenn du nicht mit return .. explizit einen Wert zurücklieferst, dann ist es None.
Im Fall von loop_begin() ist die Rückgabe von i an bestimmte Bedingungen geknüpft. Trifft keine davon zu - und der Fall ist bei dir möglich - dann endet
die Funktion ohne expliziten Rückgabewert und liefert None zurück.

Da du zu None nicht 1 dazuaddieren kannst, kommt die Fehlermeldung.

Edit: Zu langsam :cry:
Karl
User
Beiträge: 252
Registriert: Freitag 29. Juni 2007, 17:49

Beitragvon Karl » Dienstag 10. Juni 2008, 21:14

Joghurt hat geschrieben:Der erste Teil der if-Bedingung in loop_begin hat nicht für alle Fälle ein return. In den Fällen führt das zu einem impliziten return None, und somit ist i dann auch None

Ich hab auch nach Absenden des Posts genau daran gedacht, aber dann hab ich mir die Funktion nochmal angeschaut:
if und else.
Da sind doch alle Fälle mit einbegriffen? Oder hab ich das irgendwas falsch verstanden?
Aber okay, wenn ich so weiter drüber nachdenke, ist das die einzig logische Erklärung, nur hab ich vorhin nicht dran geglaubt :o

Hyperion hat geschrieben:Du benutzt i zum einen als Zählvariable und dann weist Du i gleichzeitig Werte aus einer Methoden zu? Ich wette da passiert das Unheil ... woher weißt Du, dass da immer etwas sinnvolles zurückkommt?

Desweiteren könnte etwas im gekürzten Teil passieren, was i nicht gut tut! Denn die Bedingungen, die Du hier aufführst müssen ja nicht zwangsweise eintreten.

Woher weißt Du, dass der Fehler genau da auftritt, wo Du ihn markierst? Und wo tritt er auf? Bei der Zuweisung wohl doch kaum! Und ansonsten muss es ja in der Methode loop_begin passieren ...

Wie gesagt, das i += 1 weiter unten sagt ja noch nichts darüber aus, wo das i zu None wird!

edit: Zu langsam!

Und Jogurt hat wohl Recht!

Du hast gar nicht gelesen, was ich schon gesagt hab. Dann hättest du dir viele sinnlose Fragen und Ausrufe sparen können ...
Mir war also klar, dass die Variable durch die Methode loop_begin auf NoneType gesetzt wird und mir war auch klar, dass dies direkt passiert und kein return zurückkommt. Nur wie oben schon geschrieben hab ich das nicht für den möglichen Grund gehalten.

@pütone, ha ha! zu langsam :p
Ich danke euch, und werd das jetzt mal umsetzen, hoffentlich klappts :)
Immer schön wie schnell man hier Antworten bekommt.

Edit: Wunderbar! :) Einfach das else raus, kurz die 2 Zeilen eingerückt und schon klappt's für wirklich alle else-Fälle :)
Vielen Dank, jetzt klappt alles perfekt
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Dienstag 10. Juni 2008, 22:48

Karl hat geschrieben:Ich hab auch nach Absenden des Posts genau daran gedacht, aber dann hab ich mir die Funktion nochmal angeschaut:
if und else.
Da sind doch alle Fälle mit einbegriffen? Oder hab ich das irgendwas falsch verstanden?
Aber okay, wenn ich so weiter drüber nachdenke, ist das die einzig logische Erklärung, nur hab ich vorhin nicht dran geglaubt :o

Nein. Wenn ``self.array[self.pointer] == 0`` zutrifft wechselt er in den 1. ``if``-Zweig dort wird nur wenn ``self.code[i] == "]":`` und gleichzeitig ``x == 0`` ist überhaupt ein Rückgabewert gegeben. In allen anderen Fällen kommt da ``None`` raus.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Karl
User
Beiträge: 252
Registriert: Freitag 29. Juni 2007, 17:49

Beitragvon Karl » Dienstag 10. Juni 2008, 23:23

Leonidas hat geschrieben:Nein. Wenn ``self.array[self.pointer] == 0`` zutrifft wechselt er in den 1. ``if``-Zweig dort wird nur wenn ``self.code[i] == "]":`` und gleichzeitig ``x == 0`` ist überhaupt ein Rückgabewert gegeben. In allen anderen Fällen kommt da ``None`` raus.

Nein, das kann's nicht sein.
Ich nehm's dir jetzt auch nicht übel, dass du dich nicht genau mit dem Code befasst hast, aber meiner Meinung nach führt die for-Schleife zwangsläufig zum Ende. Da wird lediglich überprüft, wo die zugehörige end-Klammer ist, und die existiert - in einem funktionierendem Brainfuck Programm - immer. Folglich geht's in jedem Fall bis zum return weiter.
Was noch dagegn spricht, es funktioniert alles, obwohl ich an diesem Teil gar nichts geändert hab, ich habe nur das else weggenommen:

Code: Alles auswählen

    def loop_begin(self, i):
        if self.array[self.pointer] == 0:
            x = 1
            for i in xrange(len(self.code)):
                if self.code[i] == "[":
                    x += 1
                if self.code[i] == "]":
                    x -= 1
                    if x == 0:
                        return i
        #hier wäre normalerweise else
        self.tmp.append(i)
        return i

Aber das versteh ich immer noch nicht so ganz, ich dachte ein if/else Konstrukt würde alle möglichen Fälle abdecken :x

PS: Mir fällt grad auf, dass ich aus'm 2. if auch einfach ein elif hätte machen können. Gibt sich das irgendwas? Oder ist das im Prinzip egal und nur in Verbindung mit else nützlich?[/code]
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Dienstag 10. Juni 2008, 23:29

Karl hat geschrieben:PS: Mir fällt grad auf, dass ich aus'm 2. if auch einfach ein elif hätte machen können. Gibt sich das irgendwas? Oder ist das im Prinzip egal und nur in Verbindung mit else nützlich?

Naja, zwischen if und elif besteht durchaus ein Unterschied.

Code: Alles auswählen

val = "ab"
if 'a' in val:
   print 'ja'
elif 'b' in val:
    print 'ja, auch'


Code: Alles auswählen

val = "ab"
if 'a' in val:
   print 'ja'
if 'b' in val:
    print 'ja, auch'
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Karl
User
Beiträge: 252
Registriert: Freitag 29. Juni 2007, 17:49

Beitragvon Karl » Dienstag 10. Juni 2008, 23:37

Okay stimmt ;)
Daran hab ich gerade echt nicht gedacht. Wobei das bei mir aufgrund von sich gegenseitig ausschließenden Bedingungen dann wohl egal ist-
Aber hast du (oder jemand anders) ne Idee, warum in der Methode ein if/else-Konstrukt nicht alle Fälle abdeckt? Ist ja offensichtlich so, aber ich weiß immer noch nicht, warum :x
Edit: Sry die Doppelpunkt-X Smilies sehen so böse aus, ist aber nicht so gemeint ;) Ich bin's nur gewohnt das zu schreiben, keine Ahnung wieso
EyDu
User
Beiträge: 4866
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Beitragvon EyDu » Dienstag 10. Juni 2008, 23:49

Das liegt daran, dass der else-Block nicht mehr ausgewertet wird, sobald du den dazugehörigen if-Block betreten hast. Die inneren ifs sind vollkommen unabhängig von den umschließenden.

Teste das einfach mal am diesem Stück Code indem du mit den Werten von a und b ein wenig rumspielst.

Code: Alles auswählen

if a:
    print "if"
    if "b":
        print "b"
else:
    print "else"
Karl
User
Beiträge: 252
Registriert: Freitag 29. Juni 2007, 17:49

Beitragvon Karl » Mittwoch 11. Juni 2008, 00:00

EyDu hat geschrieben:Das liegt daran, dass der else-Block nicht mehr ausgewertet wird, sobald du den dazugehörigen if-Block betreten hast. Die inneren ifs sind vollkommen unabhängig von den umschließenden.

Teste das einfach mal am diesem Stück Code indem du mit den Werten von a und b ein wenig rumspielst.

Code: Alles auswählen

if a:
    print "if"
    if "b":
        print "b"
else:
    print "else"

Hm, das ist mir schon klar.
Sorry, ich glaub ich kann mich einfach nicht ausdrücken :)
Ich mach's nochmal ganz langsam und ganz deutlich, was ich meine.

Also der Code sah, als er noch nicht funktionierte folgendermaßen aus:

Code: Alles auswählen

    def loop_begin(self, i):
        if self.array[self.pointer] == 0:
            x = 1
            for i in xrange(len(self.code)):
                if self.code[i] == "[":
                    x += 1
                if self.code[i] == "]":
                    x -= 1
                    if x == 0:
                        return i
        else:              # else noch da -> funktioniert NICHT!
            self.tmp.append(i)
            return i

Jetzt funktioniert er und sieht so aus:

Code: Alles auswählen

    def loop_begin(self, i):
        if self.array[self.pointer] == 0:
            x = 1
            for i in xrange(len(self.code)):
                if self.code[i] == "[":
                    x += 1
                if self.code[i] == "]":
                    x -= 1
                    if x == 0:
                        return i
        # else nicht mehr da ... der Code FUNKTIONIERT!
        self.tmp.append(i)
        return i


So das hat jetzt gar nichts mit den inneren if's zu tun, da die, wie ich oben erklärt habe, zwangsläufig innerhalb einiger Schleifendurchläufe True sind und somit ``return i`` ausgeführt wird.
Deshalb geht es nur um das äußere if und das else.
Und _mit_ else krieg ich folgendes:

Code: Alles auswählen

>>> ================================ RESTART ================================
>>>
5
6

Traceback (most recent call last):
  File "C:\Python25\test.py", line 120, in <module>
    x.run()
  File "C:\Python25\test.py", line 113, in run
    i += 1
TypeError: unsupported operand type(s) for +=: 'NoneType' and 'int'

Ohne klappt es ...
Daraus schließe ich, dass if/else nicht alle Fälle berücksichtigt? Das hat mich sehr gewundert, darum war ich auch erst so ratlos, als ich hier gefragt habe, warum zur Hölle mein Programm nicht so richtig will.
Ich hoffe ihr habt jetzt verstanden, was ich meine. Das andere wäre ja recht trivial gewesen ;)
EyDu
User
Beiträge: 4866
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Beitragvon EyDu » Mittwoch 11. Juni 2008, 00:59

Hab mir schon gedacht, dass meine Antwort zu trivial ist, aber lieber so eine kurze Antwort geben, als erst mal stundenlang zu suchen. Jetzt sehe ich auch die Markierung "HIER"... :roll:

Finde doch mal die Variablenbelegungen raus, bevor "loop_begin" das "None" liefert. Etwas seltsam finde ich, dass du in "loop_begin" immer über die vollständige Länge des Codes iterierst.

Das

Code: Alles auswählen

if self.array[self.pointer] == 0:
            x = 1
            for i in xrange(len(self.code)):
                if self.code[i] == "[":
                    x += 1
                if self.code[i] == "]":
                    x -= 1
                    if x == 0:
                        return i


Kanns man übrigens besser als

Code: Alles auswählen

if self.array[self.pointer] == 0:
            x = 1
            for index, elem enumerate(self.code)
                if elem == "[":
                    x += 1
                elif elem == "]":
                    x -= 1
                    if x == 0:
                        return index


schreiben.
Karl
User
Beiträge: 252
Registriert: Freitag 29. Juni 2007, 17:49

Beitragvon Karl » Mittwoch 11. Juni 2008, 01:26

EyDu hat geschrieben:Hab mir schon gedacht, dass meine Antwort zu trivial ist, aber lieber so eine kurze Antwort geben, als erst mal stundenlang zu suchen. Jetzt sehe ich auch die Markierung "HIER"... :roll:

Finde doch mal die Variablenbelegungen raus, bevor "loop_begin" das "None" liefert. Etwas seltsam finde ich, dass du in "loop_begin" immer über die vollständige Länge des Codes iterierst.

Ich mach euch ja keinen Vorwurf daraus, dass ihr euch nicht durch jeden Code wühlen wollt ;)
Aber irgendwie nervt's mich langsam, da eigentlich noch keiner was geschrieben hat, was das Problem betrifft :) Das war eher Zufall, dass die Antworten praktisch eine Problemlösung darstellen.
Wie im 1. Post geschrieben (jaja so lange her :D) hab ich schon an allen möglichen stellen ein print eingebaut. ``print i`` um genau zu sein ;)
Daraus hat sich ergeben, dass unmittelbar nach Aufruf der Funktion ``loop_begin``, in welcher die return-Anweisungen nicht mehr erreicht werden (beide nicht) der Wert NoneType zugewiesen wurde.
Ich hab aber jetzt noch ein paar Kleinigkeiten geändert, es scheint jetzt doch auch mit dem else-Teil zu funktionieren. Wobei ich das immer noch ein wenig merkwürdige finde, aber wahrscheinlich würde man den Fehler entdecken, wenn man jeden Schritt des Programms nachvollziehen würde.
Vielleicht gibt's ja doch einen Fall, in dem die return-Anweisung im if-Zweig nicht erreicht wird. Jedenfalls klappt's jetzt bei mir :)
Aber genau wissen, was ich alles geändert hab, tu ich auch nicht mehr ...

EyDu hat geschrieben:Das

Code: Alles auswählen

if self.array[self.pointer] == 0:
            x = 1
            for i in xrange(len(self.code)):
                if self.code[i] == "[":
                    x += 1
                if self.code[i] == "]":
                    x -= 1
                    if x == 0:
                        return i


Kanns man übrigens besser als

Code: Alles auswählen

if self.array[self.pointer] == 0:
            x = 1
            for index, elem enumerate(self.code)
                if elem == "[":
                    x += 1
                elif elem == "]":
                    x -= 1
                    if x == 0:
                        return index


schreiben.

Hm, enumerate kenn ich eigentlich gar nicht und der Code hat wohl ein paar Syntaxfehler ;o
Benutzeravatar
Hyperion
Moderator
Beiträge: 7471
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Beitragvon Hyperion » Mittwoch 11. Juni 2008, 06:45

Karl hat geschrieben:Jetzt funktioniert er und sieht so aus:

Code: Alles auswählen

    def loop_begin(self, i):
        if self.array[self.pointer] == 0:
            x = 1
            for i in xrange(len(self.code)):
                if self.code[i] == "[":
                    x += 1
                if self.code[i] == "]":
                    x -= 1
                    if x == 0:
                        return i
        # else nicht mehr da ... der Code FUNKTIONIERT!
        self.tmp.append(i)
        return i


So das hat jetzt gar nichts mit den inneren if's zu tun, da die, wie ich oben erklärt habe, zwangsläufig innerhalb einiger Schleifendurchläufe True sind und somit ``return i`` ausgeführt wird.

Kann ja nicht sein! Wenn dem so wäre, hätte der Code mit else auch funktionieren müssen. Das das in irgend einer Form klappt ist nun klar, da im Falle, dass an einem Punkt der If-Verschachtelungen keine Bedingung True wird, auf jeden Fall am Ende ein expliziter return Wert zurückgegeben wird.
Deshalb geht es nur um das äußere if und das else.

Nein. s.o.
EyDu hatte Dir doch ein schönes kleines Beispiel geschrieben. Hast Du damit mal herumprobiert? Ansonsten mach doch mal folgendes: Mal Dir das ganze Konstrukt als Baum auf (Weißt Du was Bäume sind in der Domäne Informatik? http://de.wikipedia.org/wiki/Bin%C3%A4rbaum)
Es muss ein vollständiger binärer Baum - d.h. an jedem inneren Knoten müssen zwei Äste abzweigen - dabei rauskommen, wenn Du alle Fälle abdecken und nicht an irgend einer Stelle aus der Verschachtelung rausfliegen willst. Wenn Du das tust, wirst Du sofort merken, dass da an einer Stelle ein Ast fehlt!

@all: Wäre so etwas nicht eigentlich leicht als Test möglich? Kann ja eigentlich nicht so schwer sein, ein Tool zu schreiben, dass das testet - oder kann pylint so etwas?
BlackJack

Beitragvon BlackJack » Mittwoch 11. Juni 2008, 07:44

@Karl: Hör doch mal auf anderen vor zu werfen, sie würden sich nicht mit Deinem Quelltext im Detail beschäftigen wollen und gehe mal auf die direkten Antworten auf das Problem ein. In diesem Quelltext:

Code: Alles auswählen

    def loop_begin(self, i):
         if self.array[self.pointer] == 0:
             x = 1
             for i in xrange(len(self.code)):
                 if self.code[i] == "[":
                     x += 1
                 if self.code[i] == "]":
                     x -= 1
                     if x == 0:
                         return i
         else:
             self.tmp.append(i)
             return i


besteht im ``if``-Zweig die Möglichkeit, dass *kein* ``return`` ausgeführt wird, und dann wird `None` zurückgegeben. Und das trotz Deiner Behauptung das könne nie passieren. Da es aber *doch* passiert, stell doch mal Deine Sicht der Dinge in Frage und nicht die der Leute, deren Sicht mit denen des Python-Interpreters deckt. *Der* hat nämlich immer Recht.

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder