[Tutor] Object and Attribute Error

Cameron Simpson cs at cskk.id.au
Fri Mar 27 20:59:12 EDT 2020


On 27Mar2020 17:49, Alan Thwaits <basicbare at gmail.com> wrote:
>Another error message in my game script from Exercise 43 in Zeb Shaw's
>"Learn Python the Hard Way."
>
>The first block of code which the error message references is this:
>
>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)
>
>        #be sure to print out the last scene
>        current_scene.enter()
[...]
>  File "C:/Users/Alan/Documents/Python/ex43.py", line 29, in play
>    next_scene_name = current_scene.enter()
>AttributeError: 'NoneType' object has no attribute 'enter'
[...]

This indicates that current_scene.enter cannot be accessed because 
current_scene is None.

The reason for this is twofold:

1: self.scene_map.next_scene(next_scene_name) can return None because it 
does a .get(), which returns None if there is no matching key. If you 
used Map.scenes[scene_name] that would not happen, though you would of 
course get a KeyError on no matching key; at least that would be closer 
to the source of your problem.

2: your while loop:

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

is written on the presumption that self.scene_map.next_scene always 
returns a scene, when in fact it can return None. I would take this to 
indicate that your graph-of-scenes does not in fact flow through to the 
'finished' scene. That can be as simple as mistyping the name of the 
next scene.

I'd be inclined to write the loop like this:

    while current_scene != last_scene:
        next_scene_name = current_scene.enter()
        next_scene = self.scene_map.next_scene(next_scene_name)
        if next_scene is None:
            raise ValueError(
                "current_scene %s: no next scene with name %r"
                % (current_scene, next_scene_name))
        current_scene = next_scene

This provides a nice exception detailing the name of the scene which was 
not found.

BTW, this function:

>class Map(object):
[...]
>    scenes = {
[...]
>    def next_scene(self, scene_name):
>        val = Map.scenes.get(scene_name)
>        return val

The class is in an instances attribute search space, so you can write:

    val = self.scenes.get(scene_name)

and avoid hardwiring the class name into the method. (This also means 
you can write a subclass with a different next_scene method easily.)

Cheers,
Cameron Simpson <cs at cskk.id.au>


More information about the Tutor mailing list