[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