Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   Python (http://www.programmingforums.org/forum43.html)
-   -   Object-Oriented Habits (http://www.programmingforums.org/showthread.php?t=14682)

Sane Dec 4th, 2007 2:08 PM

Object-Oriented Habits
 
When is inheritance overdoing it? Which of these two programs is better? Why? I'd like to hear your thoughts. My object oriented habits are limited to my experiences, so I'd like to hear others.

I, personally, would do either one. I could not give a definite answer.

Inheritance
:

class child:
    def __init__(self, x):
        self.x = x
    def get(self):
        return self.validate()

class male(child):
    def validate(self):
        # Some arbitrary validation unique to a male child
        if not self.x:
            return False
        return self.x & 1 == 1

class female(child):
    def validate(self):
        # Some arbitrary validation unique to a female child
        return self.x == None

if __name__ == '__main__':
    children = [male(3), female(3), male(None), female(None)]
    for kid in children:
        print kid.validate()


Flags
:

MALE = 1
FEMALE = 2

class child:
    def __init__(self, x, gender):
        self.x = x
        self.gender = gender

    def validate(self):
        if self.gender == MALE:
            if not self.x:
                return False
            return self.x & 1 == 1
       
        elif self.gender == FEMALE:
            return self.x == None   

if __name__ == '__main__':
    children = [child(3, MALE),
                child(3, FEMALE),
                child(None, MALE),
                child(None, FEMALE)]
    for kid in children:
        print kid.validate()


ZenMasterJG Dec 4th, 2007 6:27 PM

Re: Object-Oriented Habits
 
Personally, I think it depends on the program. If the two inheriting classes would be implemented substantially differently, then definitely go with inheritance. If the differences are fairly small, then I'd go with your second solution, though I'd probably wrap the constants into the class as class attributes, like so:
:

  1. class Child(object):
  2.     MALE=1
  3.     FEMALE=2
  4.     def __init__(self, gender):
  5.         self.gender=gender
  6.  
  7.     def doSomething(self):
  8.         if self.gender==Child.MALE:
  9.             print 'male'
  10.         elif self.gender==Child.FEMALE:
  11.             print 'female'
  12.         else:
  13.             print 'error'
  14.  
  15. boy=Child(Child.MALE)
  16. boy.doSomething()

I think that way achieves better encapsulation, and if nothing else it avoids cluttering up your namespaces a bit.

(And, as an aside, you should probably start getting in the habit of using new style classes (i.e inheriting everything from object, if it isn't inheriting anything else) instead of old-style as you're using now.

Sane Dec 4th, 2007 6:38 PM

Re: Object-Oriented Habits
 
Yes, I like your suggestion. I tend to stay away from non-global-scope constants. But I'll shift gradually.

Oh? What does inheriting 'object' provide? I thought that only added some more callable methods, or something to that effect...

ZenMasterJG Dec 5th, 2007 10:20 AM

Re: Object-Oriented Habits
 
For now its mostly just style, though old-style classes will be gone in 3.0.
New style-classes are mostly useful for the descriptor protocol and some meta-class goodness, neither of which you need very frequently.

Sane Dec 5th, 2007 11:25 AM

Re: Object-Oriented Habits
 
Oh, ****. That must be why my meta-class wasn't working before.

Klipt Dec 13th, 2007 2:18 AM

Re: Object-Oriented Habits
 
The inheritance based model is just Java/C++'s version of OOP. Python can probably handle more sophisticated techniques like Mixins.

ptmcg Jan 28th, 2008 5:11 AM

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


All times are GMT -5. The time now is 2:13 PM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC