简体   繁体   中英

Python 2.7 — Calling an Abstract Base Class' method from an Instance

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM