0

I am searching for the proper way to call an abstract base class' method from an instance of a class that is registered as a subclass of the ABC. This is some very basic test code to first figure out how to make this work. This is where I am currently at:

from abc import ABCMeta

# Dog class
class Dog(object):
    def speak(self):
        pass
    def move(self):
        pass

# Barking behaviors
class Bark(object):
    __metaclass__ = ABCMeta
    def speak(self):
        print "bark bark bark"

class Howl(object):
    __metaclass__ = ABCMeta
    def speak(self):
        print "ahwoooooo"

# Movement behaviors
class Run(object):
    __metaclass__ = ABCMeta
    def move(self):
        print "I'm running"

class Walk(object):
    __metaclass__ = ABCMeta
    def move(self):
        print "I'm walking"


# Dog implementations
class Beagle(Dog):
    pass
Howl.register(Beagle)
Run.register(Beagle)

nora = Beagle()
nora.speak() # THIS IS THE ISSUE: Calls speak() from original Dog class
nora.move()  # Need to call move() from registered ABC

# Test to be sure .register() was used properly
assert isinstance(nora, Howl)

While this approach may seem overly involved to alter the two Dog methods, I am looking to have the flexibility of being able to assign the behaviors to an unknown amount of instances. I'd like to be able to call speak() and move() without the instance knowing the actual behavior. I also like this approach because I am able to easily remove or change the behavior that a class is registered too, without altering any existing code.

The way the code reads currently nora.speak() and nora.move() call the inherited methods from Dog to Beagle, which just contain pass.

I'd appreciate if anyone has any insight on what I need to do from this point to make the registered behavior's methods callable, or if my approach is flawed entirely.

  • 2
    I don't know if I follow what you are after. If you want to change your behavior on a subclass on class creation, why not create classes that pass the behaviors you want and then call the methods you inherit? I can provide an example if you like – salparadise Apr 09 '15 at 04:26
  • @salparadise Thanks, sorry I may not have explained it crystal clear. I also want this approach for future unknown modification purposes. Suppose later I want to introduce swim() and isMansBestFriend = True. It would not be appropriate for all subclasses of Dog to inherit swim() as not all dogs swim. But I still want some inheritance for what is not going to change with each future instantiation, such as all subclasses of Dog will have .isMansBestFriend = True. I hope that clarifies some more of my reason for this approach. My main issue is I am not able to call the ABCs methods. Thanks again! –  Apr 10 '15 at 00:34
  • `Suppose later I want to introduce swim() ` Where will you introduce this? – salparadise Apr 10 '15 at 00:35
  • @salparadise I would want to treat it the same way I am attempting to handle speak( ) and move( ). Have an ABC SwimFast and an ABC SwimSlow that are called by swim( ) when they are registered to a desired subclass. That way any changes to them are encapsulated away from the other behaviors, and they are only usable by appropriate objects. Which brings forth my original flaw of being able to call these desired methods. I am willing to accept that the original approach is incorrect and python ABC is not how I should solve this. I would like to avoid coding all the new methods to each instance. –  Apr 10 '15 at 00:56

1 Answers1

0

Here is my attempt (probably not the most pythonic way, but something close to your post):

class Animals(object):
    def speak(self):
        return self.speak_action

    def swim(self):
        return self.swim_action

    def move(self):
        return self.move_action


class Dog(Animals):
    @property
    def speak_action(self):
        return "bark bark bark"

    @property
    def move_action(self):
        return "I'm Running"


class Beagle(Dog):
    @property
    def speak_action(self):
        return "ahwoooooo"


class Duck(Animals):
    @property
    def swim_action(self):
        return "Im floating"

    @property
    def speak_action(self):
        return "Quack!!"

    @property
    def move_action(self):
        return "I Fly!"

class Mallard(Duck):
    @property
    def speak_action(self):
        return "I'm Flying higher"

(It is good practice to let exceptions to bubble up)

In [825]: d = Dog()

In [826]: d.speak()
Out[826]: 'bark bark bark'

In [827]: d.move()
Out[827]: "I'm Running"

In [828]: d.swim()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-828-c6d2ef2b464d> in <module>()
----> 1 d.swim()

.stuff/python/git_py/help_so.py in swim(self)
      4
      5     def swim(self):
----> 6         return self.swim_action
      7
      8     def move(self):

AttributeError: 'Dog' object has no attribute 'swim_action'

You can chose what you want to delegate:

In [830]: b = Beagle()

In [831]: b.speak()
Out[831]: 'ahwoooooo'

In [832]: b.move()
Out[832]: "I'm Running"

In [833]: b.swim()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-833-9c7b1a0c0dca> in <module>()
----> 1 b.swim()

/stuff/python/git_py/help_so.py in swim(self)
      4
      5     def swim(self):
----> 6         return self.swim_action
      7
      8     def move(self):

AttributeError: 'Beagle' object has no attribute 'swim_action'

And create other animals that with more skills:

In [849]: dd = Duck()

In [850]: dd.speak()
Out[850]: 'Quack!!'

In [851]: dd.move()
Out[851]: 'I Fly!'

In [852]: dd.swim()
Out[852]: 'Im floating'

You can specific things to individual classes, and even defaults, to the main class, and you can extended as you want/need.

salparadise
  • 5,699
  • 1
  • 26
  • 32