View Single Post
Old Jan 28th, 2008, 5:11 AM   #7
ptmcg
Newbie
 
Join Date: Aug 2005
Location: Austin, TX
Posts: 15
Rep Power: 0 ptmcg is on a distinguished road
Re: Object-Oriented Habits

I absolutely agree that it depends on the program.

In general, I use inheritance for modeling similarities in behavior or expectations of the class user. Strictly speaking, this is not required in Python the way it is in statically typed languages, since duck-typing allows you to invoke any method on any object, and find out at runtime whether or not the object implements that method. Contrast with, say, Java or C++, in which a program wont compile unless an object implements a method called on it.

So I use inheritance as a way of modeling interfaces, or defining common base class code that is the same for multiple subclasses. To answer your specific example, I prefer the attribute-based implementation given by ZenMasterJG (I also prefer the naming style - classes should be capitalized to be easily distinguishable from instances). But if you find your Child class starting to contain lots of code like:

if self.gender == Child.MALE:
    something boys do
elif self.gender == Child.FEMALE:
    something girls do
else:
    something children of other gender do

then you might consider creating MaleChild and FemaleChild subclasses of Child, and then letting polymorphism take care of the "if" logic. Like this (please forgive the sexism, you picked the example domain!):

class Child(object):
    MALE=1
    FEMALE=2
    def __init__(self, gender):
        self.gender=gender

    def favorite_color(self):
        if self.gender==Child.MALE:
            return "blue"
        elif self.gender==Child.FEMALE:
            return "pink"
        else:
            raise AttributeError("Invalid gender")
    
    def play_with_dolls(self):
        if self.gender==Child.MALE:
            return "I like GI Joe!"
        elif self.gender==Child.FEMALE:
            return "I like Barbie!"
        else:
            raise AttributeError("Invalid gender")

becomes:

class MaleChild(Child):
    SUBJECTIVE_PRONOUN = "he"
    OBJECTIVE_PRONOUN = "him"
    POSSESSIVE_PRONOUN = "his"
    def favorite_color(self):
        return "blue"
    def play_with_dolls(self):
        return "I like GI Joe!"

class FemaleChild(Child):
    SUBJECTIVE_PRONOUN = "she"
    OBJECTIVE_PRONOUN = "her"
    POSSESSIVE_PRONOUN = "her"
    def favorite_color(self):
        return "pink"
    def play_with_dolls(self):
        return "I like Barbie!"

and then your code can write:

for ch in childrenList:
    print "Give %s a %s toy" % (ch.OBJECTIVE_PRONOUN, ch.favorite_color())
    print '%s just said, "%s"' % (ch.SUBJECTIVE_PRONOUN.title(), 
                               ch.play_with_dolls())

and instead of the underlying class using "if self.gender==Child.MALE" conditionals, the object's type will just run the gender-specific version of favorite_color or play_with_dolls.

What separates Python from Java and C++ is that in Python, you could write all this same code and MaleChild and FemaleChild don't really have to inherit from Child. The class attributes and methods all get dynamically resolved at runtime, there is no compile time check to see whether an object actually implements favorite_color or play_with_dolls. Now if you then find that you are duplicating methods like this in both classes:

    def should_be_in_school(self):
        return (5 <= self.age <= 18)

then you could refactor MaleChild and FemaleChild to inherit from a common Child base class, and put the gender-inspecific code in that class, to be common to both MaleChild and FemaleChild objects. But personally, I would probably use a common base class in such a case, even if just to have a common __init__ method, and to help me keep track of classes that are intended to maintain a common set of supported methods.

Here is something NOT to do with inheritance: don't confuse inheritance "is-a" with composition "is-implemented-using-a". Here is a bad example:

class Class(list):
    def add_student(self,s):
        self.append(s)
    # etc.

Just because you might implement a roster of children in a classroom with a Python list does not mean that you should inherit from list for such a thing. Instead do this:

class Class(object):
    def __init__(self):
        self.students = []
    def add_student(self,s):
        self.students.append(s)

The time to inherit from a base type would be if you wanted a specialized version of that base type, such as a list that was always kept in sorted order. I would posit that domain classes pretty much never inherit from base types (such as int, dict, list, etc.).

-- Paul
ptmcg is offline   Reply With Quote