Reputation: 95
I am new to Python but write programs for a hobby, so I have moderate knowledge of OOP and computer programming in general. I have started working on an simple animal simulator. In what might very well be a heathen move, I am trying to store all of the 'action functions' of the animal in a dictionary, so that each function is accessible by a string. For example, dict['SLEEP']()
calls the sleep function.
I could find no examples of what I am trying to accomplish, and frankly am not sure how to intelligently describe my problem. See the bare-bones code below:
class Animal:
def __init__(self):
self.health = 100
self.actions = {} # dictionary of functions
self.initializeAnimal()
def initializeAnimal(self):
self.actions['SLEEP'] = self.initializeSleep() # add sleep function
def initializeSleep(self):
RESTORED_HEALTH = 20
# other constants
def sleep(self):
self.health += RESTORED_HEALTH
# utilize other constants
return sleep
Then, the animal handler would perform something along the following lines:
for animal in animalList:
animal.actions['SLEEP']()
I'd of course like the animal's health to increase by 20 when the sleep function is called. Instead, nothing happens. After some research and experimenting, I see that the self
passed to the sleep()
function apparently refers to initializeSleep()
rather than the animal class.
I am at somewhat of a loss as to how I would change the health of the animal when calling functions in this manner. Do I have to somehow make use of super-class calls?
edit: clarify syntax
Upvotes: 1
Views: 518
Reputation: 110261
Python does some maneuvers so that functions defined in a class body actually behave as "methods" - and thus, get the "self" parameter added authomatically.
It is not hard to understand how that is done - and to emulate it for an explicit dictionary as you plan - but first, consider that you can retrieve a method name using a string, without resorting to storing them in dictionaries as you plan - you can simply do:
class Animal(object):
...
def sleep(self, ...):
...
my_cow = Animal()
function = getattr(my_cow, "sleep")
function ( )
# and of course, the two preceeding lines can be in a single expression:
getattr(a, "sleep")()
Now, let's see for the dicionary -
since you defien the actual "sleep" function as a nested function, it will "see" the "self" variable as it exists in the invocation of initializeSleep()
- which means what you are doing should just work - as soons as you fix the call to initializeSleep()
by prefixing it with the self.
, as in:
def initializeAnimal(self):
self.actions['SLEEP'] = self.initializeSleep() # add sleep function
And remove the "self" parameter from the actual "sleep" function - it does not need it,
as it will "see" the nonlocal self
variable in the enclosing scope:
def initializeSleep(self):
RESTORED_HEALTH = 20
# other constants
def sleep():
self.health = RESTORED_HEALTH
# utilize other constants
return sleep
(The other constants defined inside the initializeSLeep will also be visible inside sleep as nonlocal variables)
Upvotes: 4
Reputation: 10489
You don't need to put the self
attribute into the sleep
function.
Its perfectly valid to do the following:
class Animal:
def __init__(self):
self.health = 100
self.actions = {} # dictionary of functions
self.initializeAnimal()
def initializeAnimal(self):
self.actions['SLEEP'] = self.initializeSleep() # add sleep function
def initializeSleep(self):
RESTORED_HEALTH += 20
# other constants
def sleep():
self.health += RESTORED_HEALTH
# utilize other constants
return sleep
a = Animal()
print(a.health)
a.actions['SLEEP']()
print(a.health)
Output:
100
120
As stated, you forogt the +=
in self.health = RESTORED_HEALTH
.
You also missed the self
in self.initializeSleep()
Upvotes: 1