|First |Last |xxxXXxxxx|
|00000000|00111111|111122222|
|01234567|89012345|678901234|
So we'd know that the first name was 8 bytes and went from offset 0 to offset to offset 7 inclusive and the last name was 8 bytes and went from offset 8 to offset 15 inclusive and the social security number was 9 bytes and went from offset 16 to offset 24. And you should know that even with years of experience doing this in assembly language, I still made two mistakes.FNAMEOFFSET EQU 0
LNAMEOFFSET EQU 8
SSNOFFSET EQU 16#define FNAMELEN 8
#define LNAMELEN 8
#define SSNLEN 9
typedef struct {
char[FNAMELEN] first;
char[LNAMELEN] last;
char[SSNLEN] ssn;
} t_person;
This took away a lot of that assembly language crap. Hooray! I could just refer to somePerson.first for their first name! Life was good again! Except that we discovered that we were giving everyone the ability to change first and last at any time. This is bad. It's like if you went to the bathroom and came back and someone had moved your chair and you didn't know about it until you tried to sit down and crashed on the floor.extern char *getPersonFirstName(t_opaquePerson person); and hand it out to the clients of person. This was the only way they could get the first name now. No snooping into our opaque data structures.public class Person {
private:
char *first, *last;
public:
char *getFirstName() { return first; }
void setFirstName(char *newName) { free(first); first = strdup(newName); }
char *getLastName() { return last; }
void setLastName(char *newName) { free(first); first = strdup(newName); }
}
(note - don't write this code)char *QXZYSUIPerson_getFirstName(t_Person *this)
{
return this->first;
}
I swear, this is what happens. It's that easy. Now, that QXZYSUIPerson is some garbage that the compiler introduces to prevent you from accidentally creating a global (not object) function or variable that would conflict with getFirstName, but that's not important, so ignore it. I'm putting it in here because some pedant would call me out on it if I didn't (and other things too, so you pedants just relax, OK?).an_object.some_method("hello")
vvvvvvvvvvvvvvvvv
class SomeObject(object):
def some_method(self, greeting):
print greeting
To reiterate, you are not stupid for not getting OOP. It's a hard thing to grasp. It may just require enough time for your brain to finally see what's going on. This happens to all of us when faced with a sufficiently abstract idea.>>> class Person(object):
... pass
...
>>> guy = Person()
>>> guy.name = "bob"
>>> guy.favorite_color = "red"
>>> print guy.name
bob
They're weird dictionaries in the sense that you use . to access values in them (called "attributes") instead of square brackets. Also, to make a new object, you have to call the name of the class as though it were a function (Person()) instead of using curly brackets. Here's that same code, but written with a dictionary instead of an object:
>>> dict_person = dict()
>>> dict_person["name"] = "bob"
>>> dict_person["favorite_color"] = "red"
>>> print dict_person["name"]
bob
You can imagine making a function that would take a dictionary as a parameter, and then do something with that dictionary. Here's a function that prints out the value for a name key in a dictionary:
>>> def get_dict_name(d):
... print d["name"]
...
>>> get_dict_name(dict_thing)
bob
Likewise, you could make a function that takes an object as a parameter and prints out the value of its name attribute:
>>> def get_name(obj):
... print obj.name
...
>>> get_name(thing)
bob
We could make a function, let's call it init, that initializes an empty object, like so:
>>> def init(obj):
... obj.name = "John Q. Default"
... obj.favorite_color = "chartreuse"
...
>>> new_guy = Person()
>>> init(new_person)
>>> new_person.name
'John Q. Default'
>>> new_thing.favorite_color
'chartreuse'
So that's pretty cool. We could define another function that does something more interesting with our object, like display a declaration of hue-love:
>>> def exclaim(obj):
... print "my name is " + obj.name + " and I love " + obj.favorite_color
...
>>> exclaim(new_thing)
my name is John Q. Default and I love chartreuse
We're doing object-oriented programming! Sort of! We've at least got objects, and we're passing them into functions, and those functions operate on the data in the object. A problem arises when we want to have another kind of thing—another class. Maybe in addition to Persons, we also want to have Dogs. Easy enough to declare the class and start making objects:
>>> class Dog(object):
... pass
...
>>> fido = Dog()
>>> fido.name = "fido"
>>> fido.breed = "french bulbradoodle"
Hey, sweet. Our Dogs should have a way to exclaim stuff as well, right? Let's do that:
>>> def exclaim(obj):
... print "my name is " + obj.name + " and I'm proud to be a " + obj.breed
...
>>> exclaim(fido)
my name is fido and I'm proud to be a french bulbradoodle
Nice, but now what happens if we use the same function on our Person object? Disaster!
>>> exclaim(new_guy)
Traceback (most recent call last):
File "", line 1, in
File "", line 2, in exclaim
AttributeError: 'Person' object has no attribute 'breed'
When we redefined exclaim, we overwrote our old version. The new version only works with the attributes we're putting on Dog objects (like breed), not with the attributes on Person. We could solve this problem by making separate functions for both kinds of objects:
>>> def dog_exclaim(obj):
... print "my name is " + obj.name + " and I'm proud to be a " + obj.breed
...
>>> def person_exclaim(obj):
... print "my name is " + obj.name + " and I love " + obj.favorite_color
...
>>> dog_exclaim(fido)
my name is fido and I'm proud to be a french bulbradoodle
>>> person_exclaim(new_guy)
my name is John Q. Default and I love chartreuse
This is basically how programming got done before the object-oriented paradigm gained prominence. Separate functions to do things on different kinds of data. The problem with this solution is that it's verbose, clunky, and prone to error.Person class like so:
>>> class Person(object):
... def exclaim(self):
... print "my name is " + self.name + " and I love " + self.favorite_color
...
>>> dude = Person()
>>> dude.name = "Horatio"
>>> dude.favorite_color = "blue-green"
>>> dude.exclaim()
my name is Horatio and I love blue-green
When we write object_name.function_name(), Python translates this behind the scenes into something like "call the function function_name defined inside the class that object_name belongs to. Pass object_name to that function as the first parameter, so that the function knows what object it's operating on."object.function() communicates "call the method function on object" better than function(object))
Person class to incorporate a function to print out what that person's favorite color of X might be:
>>> class Person(object):
... def exclaim(self):
... print "my name is " + self.name + " and I love " + self.favorite_color
... def favorite_color_thing(self, thing):
... print "the best " + thing + " is a " + self.favorite_color + " " + thing
...
>>> dude = Person()
>>> dude.name = "Horatio"
>>> dude.favorite_color = "blue-green"
>>> dude.favorite_color_thing("apple")
the best apple is a blue-green apple
(2) Python lets you define a special, weird method called __init__ in your class. Python calls this method whenever you create an object using the ClassName() syntax. Any parameters that you include inside the parentheses in the ClassName() call will be passed to this function—with, again, the object that's being operated on as the first parameter. Let's make an __init__ method for Person that lets us initialize the object when we create it:
>>> class Person(object):
... def __init__(self, name, favorite_color):
... self.name = name
... self.favorite_color = favorite_color
... def exclaim(self):
... print "my name is " + self.name + " and I love " + self.favorite_color
... def favorite_color_thing(self, thing):
... print "the best " + thing + " is a " + self.favorite_color + " " + thing
...
>>> dude = Person('Dr. Tim Obtuse', 'orangish')
>>> dude.exclaim()
my name is Dr. Tim Obtuse and I love orangish
>>> dude.favorite_color_thing("hovercraft")
the best hovercraft is a orangish hovercraft
Hope that helps a little bit...?
class Room:
def __init__(self, name, description):
self.name = name
self.description = description
That represents your room and that's all you need to write for now. Do the same for any other data types you might need. Again, concentrate on the data and don't worry, yet, about "doing stuff."
def print_name(a_room):
print a_room.name
which you would use like this:
x = Room("a room", "it's a mess")
print_name(x)
You can instead make it a class method like so:
Class Room:
def __init__(self, name, description):
# initialization code
def print_name(self):
print self.name
which you use like this:
x = Room("a room", "it's a mess")
x.print_name()
Both versions of print_name are basically the same. "a_room" is changed to "self" and instead of explicitly passing "x" to the function, we call the function on x. (x.print_name() instead of print_name(x))
class Tiger(Animal):
def __init__(self, name):
self.name = name
super(Animal, self).__init__()
...
Tony the Tiger is an instance of Tiger, so he can maul. He can also do the other normal actions.
def maul(self, prey):
prey.die() # we assume here that the tiger successfully kills his prey
if self.is_hungry: # this is a universal property defined by the abstract Animal class
self.eat(prey)
First, notice that since the prey is an animal, like all Animals it shares the die action. This is because we can send individual objects/instances to a function, and we can act on that object.
class Choice(object):
def __init__(self, label, command, response=None, next=None):
self.label = label
self.command = command
self.response = response
self.next = next
def choose(self):
print self.response
return self.next
class Scene(object):
def __init__(self, description, choices=None):
self.description = description
self.choices = choices
def list_options(self):
return "Choices: %s" % ", ".join(
["%s (%s)" % (c.label, c.command) for c in current.choices
)
def decide(self, command):
choice = None
for c in current.choices:
if c.command.lower() == action.lower():
choice = c
break
if not choice:
raise ValueError("%s, bozo." % " or ".join([c.command for c in self.choices]))
return choice
class Engine(object):
def __init__(self, start):
self.scene = start
def play(self):
current = self.scene
print current.description
if current.choices:
print current.list_options()
choice = None
while not choice:
action = raw_input("> ")
try:
choice = current.decide(action)
except ValueError, error:
print error
self.scene = choice.choose()
if self.scene:
self.play()
death = Scene("You died")
home = Choice("home","h","Well, now you've gone home. Which is great, if you like home.")
drift = Choice("drift","d","You drift forever, a happy vagabond.")
outer_space = Scene("now you are in outer space! Do you want to go home, or drift?", choices=[home,drift])
taunt = Choice("taunt", "t", "improbably, that works!", outer_space)
shoot = Choice("shoot", "s", "You shoot! You miss! You die!", death)
corridor = Scene(""Gothons have killed your crew and one's about to blast you!"", choices=[shoot, taunt])
Gameplay starts with:game = Engine(corridor) game.play()Note that I haven't tested this code, so there are probably some typos and errors that you'd have to clear up, but I feel like it presents some good OOP ideas. Now, instead of a Map object with each location being a function, now each location is an Location instance. Locations have a list of choices (Choice instances). All the engine does is output the description in the location, and then if the location has choices it outputs the display of those locations and grabs input and sends it to the decide function of the location object, which accepts that input and returns the matching choice. If not, the location throws a ValueError (because it's not a valid value input) that is caught by the engine, which loops again to grab the input. As soon as something valid comes up, the engine receives a choice. It then calls the choose function on that choice, which returns a new location, and the engine plays again.
You're not an imbecile; these concepts are not intuitive. I just had to grapple with them for a long time before understanding them.
posted by massysett at 7:33 AM on April 6, 2012 [1 favorite]