社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

在python中使用组合而不是继承的多态性的正确方法

Enzo • 5 年前 • 1574 次点击  

在做一个项目时,我在使用继承时陷入了设计中。现在我试着去掉它,改为使用构图,因为这似乎是解决我问题的合适方法。然而,我需要多动症,我不确定我是否以正确的方式实现了我的作文。

有人能看看我下面的代码吗?在最后三行中,我希望所有的动物都能行走,但前提是它们有能力行走。在对该属性调用函数之前,首先检查对象是否具有某个属性(在本例中为“legs”)是一种好的做法吗?我只是好奇这是一个正确的方式来做这件事还是有更好的方式。

class Animal:

    def __init__(self, name):
        self.name = name

    def make_sound(self):
        print("silence...")


class Wings:

    def flap(self):
        print("Wings are flapping")


class Legs:

    def walk(self):
        print("Legs are walking")


class Bird:

    def __init__(self):
        self.animal = Animal("Bird")
        self.wings = Wings()

    def make_sound(self):
        print(f"{self.animal.name} is Singing!")


class Dog:

    def __init__(self):
        self.animal = Animal("Dog")
        self.legs = Legs()

    def make_sound(self):
        print(f"{self.animal.name} is Barking")


class Cat:

    def __init__(self):
        self.animal = Animal("Cat")
        self.legs = Legs()

    def make_sound(self):
        print(f"{self.animal.name} is Meowing!")


if __name__ == '__main__':

    animals = list()

    animals.append(Bird())
    animals.append(Dog())
    animals.append(Cat())

    for animal in animals:
        animal.make_sound()

    for animal in animals:
        if hasattr(animal, 'legs'):
            animal.legs.walk()
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/40916
 
1574 次点击  
文章 [ 1 ]  |  最新文章 5 年前
bruno desthuilliers
Reply   •   1 楼
bruno desthuilliers    6 年前

你有点过分了实际上

继承描述的是一种“是”的关系,组合描述的是一种“有”的关系。所以在你的例子中,用构图来表示翅膀和腿之类的属性是非常有意义的,但是鸟、猫和狗都是动物——它们没有“拥有”动物(嗯,它们都有跳蚤,但那是另一个话题)——所以它们应该继承自 Animal .

而且,大多数鸟的腿都太长了,而且有相当一部分实际上根本不会飞(但有些鸟用腿游泳,而且很有效率)。

在对该属性调用函数之前,首先检查对象是否具有某个属性(在本例中为“legs”)是一种好的做法吗?

真的要看上下文了。作为一般规则,不,这被认为是不好的做法(参见“告诉不要问”和“德米特定律”),但有些情况下,这是合法的。而且,“好”的设计也取决于要解决的问题,我们在这里达到了玩具示例的极限,这些示例永远不能代表现实生活中的用例。

理论上,组合/委派对客户端代码应该是透明的,所以您应该只调用 whatever_animal.walk() 把它干掉。现在您(作为“客户代码”)可能想知道动物不能行走,在这种情况下,非行走动物应该提出一个例外时,收费步行…这也意味着 动物 必须有所有可能的“操作”的默认实现,并且客户机代码必须为“unsupportedAction”(或您想命名它们的方式)异常做好准备。

wrt/implementation,使委派透明可以像使用 __getattr__() 即:

class UnsupportedAction(LookupError):
    pass

class Animal(object):
    _attributes = ()

    def __init__(self, name):
        self.name = name

    def make_sound(self):
        print("silence...")

    def __getattr__(self, name):
        for att in self._attributes:
            if hasattr(att, name):
                return getattr(att, name)
        else:
            raise UnsupportedAction("{} doesn't know how to {}".format(type(self), name))



class Dog(Animal):
    _attributes = (Legs(), )


class Bird(Animal):
    _attributes = (Legs(), Wings())

这个解决方案的优点是它非常简单而且非常动态。不太好的一点是它既不可检查也不明确。

另一个解决方案是明确授权:

class UnsupportedAction(LookupError):
    pass

class Animal(object):
    _attributes = ()

    def __init__(self, name):
        self.name = name

    def make_sound(self):
        print("silence...")


    def walk(self):
        return self._resolve_action("walk")

    def fly(self):
        return self._resolve_action("walk")

    # etc            

    def _resolve_action(self, name):
        for att in self._attributes:
            if hasattr(att, name):
                return getattr(att, name)
        else:
            raise UnsupportedAction("{} doesn't know how to {}".format(type(self), name))

这是相当详细,少了很多动态,但移动明显,文件化,可读性和可检查性。

在上面的示例中,您实际上可以使用自定义描述符排除冗余代码:

class Action(object):
    def __init__(self, name):
        self.name = name

    def __get__(self, obj, cls):
        if obj is None:
            return self
        return obj._resolve_action(self.name)

    def __set__(self, obj, value):
        raise AttributeError("Attribute is readonly")


class Animal(object):
    _attributes = ()

    def __init__(self, name):
        self.name = name

    def make_sound(self):
        print("silence...")

    walk = Action("walk")
    fly = Action("fly")

    # etc

但再一次,如果没有一个真正需要解决的问题,这一切都没有意义,因为这个问题通常定义了正确的解决方案。