2

I'm working through LearnPythontheHardWay by Zed Shaw, and I'm stumped.

I'm getting the error:

[Attribute Error:'NoneType' object has no attribute 'enter']

at the line: next_scene_name = current_scene.enter() under the Engine class.

from sys import exit

class Scene(object):

    def enter(self):
        print "This scene is not yet configured. Subclass it and implement\
enter()."
        exit(1)

class Engine(object):

    def __init__(self, scene_map):
        self.scene_map = scene_map

    def play(self):
        current_scene = self.scene_map.opening_scene()
        last_scene = self.scene_map.next_scene('finished')

        while current_scene != last_scene:
            next_scene_name = current_scene.enter()
            current_scene = self.scene_map.next_scene(next_scene_name)

        current_scene.enter()   

class EmptyScene(Scene):

    def enter(self):
        pass

class FinishScene(Scene):

    def enter(self):
        pass

class Map(object): 

    scenes = {
        'empty_scene': EmptyScene(),
        'finished': FinishScene(),
    }

    def __init__(self, start_scene):
        self.start_scene = start_scene

    def next_scene(self, scene_name):
        val = Map.scenes.get(scene_name)
        return val

    def opening_scene(self):
        return self.next_scene(self.start_scene)


a_map = Map('empty_scene')
a_game = Engine(a_map)
a_game.play()`
Leb
  • 15,483
  • 10
  • 56
  • 75
Steven Myers
  • 65
  • 1
  • 7
  • possible duplicate of [Python: Attribute Error - 'NoneType' object has no attribute 'something'](http://stackoverflow.com/questions/8949252/python-attribute-error-nonetype-object-has-no-attribute-something) – dimo414 Jun 26 '15 at 05:31
  • @dimo414 I disagree, from the initial title it would appear to be a duplicate, but the issue is more extensive than that of the question you've suggested. – randomusername Jun 26 '15 at 05:41

3 Answers3

2

The problem is that current_scene is actually None. This is caused by the call to self.scene_map.next_scene calling dict.get. The issue with using dict.get is that if the key is not in the dictionary, then it will just return None rather than throw an exception like you would expect. So your program keeps on running, thinking that everything is okay, until much later in the code a problem appears.

randomusername
  • 7,927
  • 23
  • 50
  • Why does it return none if 'finished' is the next key it searches for? It ought to return the next scene, correct? – Steven Myers Jun 26 '15 at 05:36
  • @StevenMyers No, you're looking at the wrong line. The error occurs when the function is called in the body of the while loop. Because the key that it searches for is the value returned by `current_scene.enter()`. But note that a function that doesn't return a value (say one that ends in `pass`) will return `None`. So it is using `None` as the key to look up. – randomusername Jun 26 '15 at 05:39
  • Ah, I discovered what I did wrong, and I understand what you're saying now. This will work if I do something at the end of a scene, like "return finished()" or "return empty_scene" – Steven Myers Jun 26 '15 at 05:41
1

This is happening because current_scene is None so when you try the enter() function it's giving you that error.

One way to go about that is to provide an if statement:

while current_scene != last_scene:
    if current_scene != None:
        next_scene_name = current_scene.enter()
        current_scene = self.scene_map.next_scene(next_scene_name)
    else:
        break 
Leb
  • 15,483
  • 10
  • 56
  • 75
1

Seems like your code is incomplete .

The issue occurs because first time, your current_scene has a value which is the empty_scene , then you try to call current_scene.enter() to get the next scene, but the EmptyScene class' enter method does not return anything, hence you are getting None back, then you change the current_scene to be next_scene, which is None, and again try to call enter function in it, causing the issue.

you can change the while condition to -

while current_scene != last_scene and current_scene is not None:
    next_scene_name = current_scene.enter()
    current_scene = self.scene_map.next_scene(next_scene_name)

If you want finished to be the next scene after empty_scene , then you need to change the EmptyScene class to return finished instead of passing the function. Example -

class EmptyScene(Scene):

    def enter(self):
        return 'finished'
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176