Python Design Patterns - composition vs. inheritance

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • snewman18@gmail.com

    Python Design Patterns - composition vs. inheritance

    In learning about design patterns, I've seen discussion about using
    inheritance when an object's relationship to another object is 'is-a'
    and composition when the relationship is 'has-a'.

    Since this is all new and I'm still learning, I was hoping someone can
    give me some pointers on best practices on applying these ideas. If my
    logic is incorrect on anything, please don't hesitate to tell me where
    I'm wrong - I'm completely open to any help offered.

    As a very simplified example, if I had two classes, Pet and Owner, it
    seems that I would not have Pet inherit from Owner, since a pet 'has
    an' owner, but not 'is an' owner. If this is correct, does my code
    below reflect this? I passed the owner object into the pet object's
    constructor - is this the right way to do it?

    Also, I've seen talk that ideally you shouldn't have too many "dots"
    in your method calls, instead using delegates to the methods and
    attributes. Can anyone elaborate on this? Ideally, should I be writing
    getattr() methods so I can do pet.address instead of
    pet.owner.addre ss? Should I be doing the same with owner's methods
    like I did with get_foo()?

    Thanks in advance to anyone who can share their experience with me -
    it's very appreciated.

    class Owner(object):
    def __init__(self):
    self.address = '123 Fake Street'

    def get_foo(self):
    return 'bar'

    class Pet(object):
    def __init__(self, owner):
    self.owner = owner

    def __getattr__(sel f, name):
    if name == 'address':
    return self.owner.addr ess
    else:
    raise AttributeError, name

    def get_foo(self):
    return self.owner.get_ foo()

    owner = Owner()
    pet = Pet(owner)
  • Diez B. Roggisch

    #2
    Re: Python Design Patterns - composition vs. inheritance

    As a very simplified example, if I had two classes, Pet and Owner, it
    seems that I would not have Pet inherit from Owner, since a pet 'has
    an' owner, but not 'is an' owner. If this is correct, does my code
    below reflect this? I passed the owner object into the pet object's
    constructor - is this the right way to do it?
    Yes. Of course there are other ways, establishing the connection later,
    and of course making the Owner know her pets. But your unidirectional,
    ctor-passed implementation is sensible.
    Also, I've seen talk that ideally you shouldn't have too many "dots"
    in your method calls, instead using delegates to the methods and
    attributes. Can anyone elaborate on this? Ideally, should I be writing
    getattr() methods so I can do pet.address instead of
    pet.owner.addre ss? Should I be doing the same with owner's methods
    like I did with get_foo()?
    No, that's certainly not a good idea. And I'm under the impression you
    misunderstood something there in the original lecture/explanation.

    The reason is simple: by adding these methods, you essentially extend
    Pet's knowledge as a class to what only its owner has to know. Which
    makes no sense. Why should pets know about addresses? Or cars? Or
    anything else that belongs to the owner only.

    Of course there are just reasons to create such delegation methods - if
    for example the property/method is of general interest to the Pet, but
    implemented by means of the owner. But I've got difficulties even to
    imagine such thing, at least in your actual example.

    Diez

    Comment

    • Carl Banks

      #3
      Re: Python Design Patterns - composition vs. inheritance

      On Nov 15, 3:37 pm, "Diez B. Roggisch" <de...@nospam.w eb.dewrote:
      Also, I've seen talk that ideally you shouldn't have too many "dots"
      in your method calls, instead using delegates to the methods and
      attributes. Can anyone elaborate on this? Ideally, should I be writing
      getattr() methods so I can do pet.address instead of
      pet.owner.addre ss? Should I be doing the same with owner's methods
      like I did with get_foo()?
      >
      No, that's certainly not a good idea. And I'm under the impression you
      misunderstood something there in the original lecture/explanation.
      Having read lots of "perspectiv es" on OO style, it wouldn't surprise
      me if the OP understood the point perfectly.

      I.e., there probably are people out there who suggest that when you
      have something like this:

      mypet.owner.get _attributes()

      you should reverse-refactor it into something like this:

      mypet.get_owner _attributes()


      I would agree that the former is preferable, especially in cases like
      this one where the classes aren't really closely related.


      Carl Banks

      Comment

      • Chris Mellon

        #4
        Re: Python Design Patterns - composition vs. inheritance

        On Nov 15, 2007 2:37 PM, Diez B. Roggisch <deets@nospam.w eb.dewrote:
        >
        As a very simplified example, if I had two classes, Pet and Owner, it
        seems that I would not have Pet inherit from Owner, since a pet 'has
        an' owner, but not 'is an' owner. If this is correct, does my code
        below reflect this? I passed the owner object into the pet object's
        constructor - is this the right way to do it?
        >
        Yes. Of course there are other ways, establishing the connection later,
        and of course making the Owner know her pets. But your unidirectional,
        ctor-passed implementation is sensible.
        >
        Also, I've seen talk that ideally you shouldn't have too many "dots"
        in your method calls, instead using delegates to the methods and
        attributes. Can anyone elaborate on this? Ideally, should I be writing
        getattr() methods so I can do pet.address instead of
        pet.owner.addre ss? Should I be doing the same with owner's methods
        like I did with get_foo()?
        >
        No, that's certainly not a good idea. And I'm under the impression you
        misunderstood something there in the original lecture/explanation.
        >
        The reason is simple: by adding these methods, you essentially extend
        Pet's knowledge as a class to what only its owner has to know. Which
        makes no sense. Why should pets know about addresses? Or cars? Or
        anything else that belongs to the owner only.
        >
        Of course there are just reasons to create such delegation methods - if
        for example the property/method is of general interest to the Pet, but
        implemented by means of the owner. But I've got difficulties even to
        imagine such thing, at least in your actual example.
        >
        An example might be something like pet.has_foodbow l, which is
        implemented as self.owner.has_ foodbowl_for(se lf). If you've got pets
        with more than one owner, it might be any(owner.has_f oodbowl_for(sel f)
        for owner in self.owners) - certainly a candidate for an easy accessor
        in the Pet class.

        This is more an example of API convenience than it is any sort of
        general OO or type theory concept. Note that any time you do this sort
        of reverse binding you're codifying assumptions about your object
        model (by which I mean domain objects, rather than your inheritance
        hierarchy) into your code.

        I don't worry to much about is-a or has-a or any sort of strict class
        hierarchy or type system. I view inheritance, in Python, as a way of
        inheriting implementation and I do it when I want some other classes
        implementation and not otherwise. In other languages (not C++ family,
        but ML family) I think much more in terms of types and type systems,
        but in Python I stay pragmatic and flexible.

        Comment

        • A.T.Hofkamp

          #5
          Re: Python Design Patterns - composition vs. inheritance

          On 2007-11-15, snewman18@gmail .com <snewman18@gmai l.comwrote:
          inheritance when an object's relationship to another object is 'is-a'
          and composition when the relationship is 'has-a'.
          >
          Since this is all new and I'm still learning, I was hoping someone can
          give me some pointers on best practices on applying these ideas. If my
          logic is incorrect on anything, please don't hesitate to tell me where
          I'm wrong - I'm completely open to any help offered.
          >
          As a very simplified example, if I had two classes, Pet and Owner, it
          seems that I would not have Pet inherit from Owner, since a pet 'has
          an' owner, but not 'is an' owner. If this is correct, does my code
          below reflect this? I passed the owner object into the pet object's
          constructor - is this the right way to do it?
          This is indeed one way. One of the things you enforce in this way that there is
          no Pet object without an owner.
          Also, I've seen talk that ideally you shouldn't have too many "dots"
          in your method calls, instead using delegates to the methods and
          attributes. Can anyone elaborate on this? Ideally, should I be writing
          As with most design stuff, they are right and they are also wrong. It's a
          trade-off.

          They are right in the sense that if you change the structure of your links, you
          will break potentionally a lot of code. Imagine you have to ask a data base for
          the owner rather than having a direct link. All "pet.owner" references will
          need to be changed then. If you hide the connection inside your Pet, there is
          only one place that needs to be changed.

          They are wrong in the sense that it is not always appropiate for a Pet object
          to perform such a function. By hiding "self.owner.add ress" inside Pet, you are
          designing a smart pet that knows how to find the address of its owner.
          Unfortunately, smarter also means more complex, and at some point you will
          defeat the advantage of OO, namely spreading responsibilitie s, and having
          several simple objects that work together to realize your application.

          So there is a trade-off. There is no universal rule, it highly depends on your
          application, your future plans, and the lifetime of the app (to name a few).
          getattr() methods so I can do pet.address instead of
          pet.owner.addre ss? Should I be doing the same with owner's methods
          like I did with get_foo()?
          I'd say no.
          One of the 'rules' (guide lines) of Python is "Be explicit rather than
          implicit" [1]. You may save a few dots, on the other hand, you obfuscate how
          the link is realized (that is "pet.addres s" doesn't say it uses owner to make
          the connection, unlike "pet.owner.addr ess").
          In the long run, the latter may be more important.


          In general, I think you shouldn't need advanced trickery like __getattr__() for
          your design. If you do, your design is most likely wrong.

          [1]: The Zen of Python: http://www.python.org/dev/peps/pep-0020/


          Sincerely,
          Albert

          Comment

          • Neil Cerutti

            #6
            Re: Python Design Patterns - composition vs. inheritance

            On 2007-11-16, Dennis Lee Bieber <wlfraed@ix.net com.comwrote:
            On Thu, 15 Nov 2007 12:28:28 -0800 (PST), "snewman18@gmai l.com"
            ><snewman18@gma il.comdeclaimed the following in comp.lang.pytho n:
            >
            >>
            >As a very simplified example, if I had two classes, Pet and Owner, it
            >seems that I would not have Pet inherit from Owner, since a pet 'has
            >an' owner, but not 'is an' owner. If this is correct, does my code
            >below reflect this? I passed the owner object into the pet object's
            >constructor - is this the right way to do it?
            >>
            Well, other than the facet that I'd say "Owner" has-a "Pet", but
            this example is a bit too disjoint for the association (IE, it goes both
            ways)
            >
            A common example is something like:
            >
            class Vehicle(object) :
            #which has attributes for speed, direction, cargo capacity,
            passenger capacity
            >
            class RoadVehicle(Veh icle):
            #inherits (RoadVehicle IS-A Vehicle with Axles)
            >
            class Auto(RoadVehicl e)
            # HAS-A Front-Axle, Rear-Axle
            # and Axle HAS-A Left-Tire, Right-Tire (unless a Dual -- with
            inner left, outer left, inner right, outer right, those those would
            appear on Bus(RoadVehicle ) and Truck(RoadVehic le) classes)
            >
            Look up "Law of Demeter" (I believe that is the name). The idea
            is that a "user" of, say, Auto, should NOT need to know that it
            has tires (may know it has axles, so it may have a
            "listAxles( )" method, which returns a list/tuple of the axles
            that the Auto has. You could then use that list to access the
            tires on each axle { for tire in aRoadVehicle.li stAxles():
            print tire.specificat ion() }
            The Law of Demeter constrains the attributes that the methods of
            Auto may invoke. Given the above containment hierarchy, the Law
            says that no method of Auto may depend on any attribute or method
            of Tire. So if you needed to compute, in an Auto method,
            something that depends on an attribute of Tire, you must make
            that information available directly through the Axle interface.

            The benefit is that this lowers the coupling between classes. The
            Tire interface can change completely without affecting the Auto
            class.
            BUT... Just look at the Python library... sys.stdout.writ e(),
            if following Law of Demeter would turn into:
            >
            myStdout = sys.getStdout()
            myStdout.write( )
            The Law of Demeter doesn't apply to that example.

            --
            Neil Cerutti

            Comment

            • Bruno Desthuilliers

              #7
              Re: Python Design Patterns - composition vs. inheritance

              snewman18@gmail .com a écrit :
              In learning about design patterns, I've seen discussion about using
              inheritance when an object's relationship to another object is 'is-a'
              and composition when the relationship is 'has-a'.
              wrt/ inheritance, it only makes sens with declarative static type
              systems where polymorphic dispatch depends on subclassing (so
              inheritance is used for both implementation and subtyping). In the
              context of a dynamic type system, inheritance is only for implementation
              reuse, ie given the following class definitions :

              class Foo(object):
              def bar(self):
              return 42

              class Baaz(object):
              def bar(self):
              return 84


              Baaz actually 'is a' (proper subtype of) Foo (and vice-versa).
              >
              Since this is all new and I'm still learning, I was hoping someone can
              give me some pointers on best practices on applying these ideas. If my
              logic is incorrect on anything, please don't hesitate to tell me where
              I'm wrong - I'm completely open to any help offered.
              >
              As a very simplified example, if I had two classes, Pet and Owner, it
              seems that I would not have Pet inherit from Owner, since a pet 'has
              an' owner, but not 'is an' owner.
              Both (I mean 'has a' and 'is a') are not necessary exclusive.
              If this is correct, does my code
              below reflect this? I passed the owner object into the pet object's
              constructor - is this the right way to do it?
              Here again, it depends on the lifetime cycles of both objects. Look for
              the difference between composition and aggregation. And it of course
              depends on the problem you're trying to solve - there's no such thing as
              an 'absolute' model.
              Also, I've seen talk that ideally you shouldn't have too many "dots"
              in your method calls, instead using delegates to the methods and
              attributes. Can anyone elaborate on this?
              "should", "should not", laws and golden rules... Indeed, it's usually
              better to respect encapsulation.
              Ideally, should I be writing
              getattr() methods so I can do pet.address instead of
              pet.owner.addre ss?
              What's the use of pet.address ? Is really 'adress' a responsability of
              'Pet' ?
              Should I be doing the same with owner's methods
              like I did with get_foo()?
              Python's methods actually are attributes, so you don't (technically)
              need a distinct scheme for methods - __getattr__ will work as well.

              __getattr__ is fine when you really have a generic delegation (look for
              the Proxy pattern for an example). Else (ie, you decide that, for this
              given problem, 'address' is really a responsability of Pet), you might
              be better defining an explicit 'address' attribute in Pet - preferably
              using a computed attribute (either a property object or a custom
              descriptor).

              Anyway, the simplest your design and implementation, the better.


              My 2 cents...

              Comment

              • Neil Cerutti

                #8
                Re: Python Design Patterns - composition vs. inheritance

                On 2007-11-17, Odalrick <odalrick@hotma il.comwrote:
                >But that breaks expectations: a user doesn't expect
                >set_width() to affect the height.
                >
                I can't speak for everyone but I certainly expect setting the
                width of a Square to change it's height. In fact, that would
                probably be the reason I used a Square rather than a Rectangle
                in the first place.
                Some of the dissonance is from assuming that rectangles can
                mutate themselves. I've never seen one do that! ;) The rest of
                the dissonance is caused by strong typing. A rectangle with equal
                width and height is still a rectangle, in a strongly typed
                language.

                --
                Neil cerutti

                Comment

                Working...