python - 如何使用 duck typing 编写 OOP 一致的代码?

标签 python oop duck-typing

我在决定方法在 python 程序中的位置时遇到了麻烦,我过去依赖的鸭子类型(duck typing)方法似乎与我的 OOP 直觉不一致。

为了说明,假设我们有三个类:Hero、Sword 和 Apple。英雄可以装备剑,英雄可以吃苹果。

如果我遵循我的 OOP 直觉,我认为代码将如下所示:

duckless.py

class Hero:
    def __init__(self):
        self.weapon = None
        self.inTummy = None

    def equip(self, weapon):
        weapon.define()
        print("I shall equip it.")
        self.weapon = weapon

    def eat(self, food):
        food.define()
        print("I shall consume it.")
        self.inTummy = food

class Sword:
    def define(self):
        print("'tis a shiny sword")

class Apple:
    def define(self):
        print("'tis a plump apple")

hero = Hero()
swd = Sword()
apl = Apple()

hero.equip(swd)
hero.eat(apl)

感觉非常直观和可读。

但是,如果我是 duck-type,我觉得代码看起来像这样:

duckfull.py

class Hero:
    def __init__(self):
        self.weapon = None
        self.inTummy = None

    def onEquip(self):
        print("I shall equip it.")

    def onEat(self):
        print("I shall eat it.")

class Sword:
    def define(self):
        print("'tis a shiny sword")

    def equip(self, hero):
        self.define()
        hero.onEquip()
        hero.weapon = self


class Apple:
    def define(self):
        print("'tis a plump apple")

    def eat(self, hero):
        self.define()
        hero.onEat()
        hero.inTummy = self

hero = Hero()
swd = Sword()
apl = Apple()

swd.equip(hero)
apl.eat(hero)

duck 类型的代码具有明显的优势,我可以随时执行 try-except 以确定我是否在执行“合法”操作:

try:
    apl.equip()
except AttributeError:
    print("I can't equip that!")

这感觉非常 pythonic,而替代方案将要求我执行可怕的类型检查

但是,从 OOP 的角度来看,sword 负责装备自己,并且接收 hero 作为参数,这感觉很奇怪。装备的行为看起来像是英雄的行为,因此我觉得该方法应该属于英雄类。

的完整语法
def eat(self, hero):
    self.define()
    hero.onEat()
    hero.inTummy = self

感觉很陌生。

这两种方法更像 pythonic 吗?是否更符合 OOP?我应该考虑完全不同的解决方案吗?

提前致谢。

最佳答案

没有明确的答案;这取决于你的类(class)做什么。在你的 Hero.equip 中检查 isinstance(weapon, Weapon) 来检查元素是否是武器并没有那么可怕。此外,如果您要像第二个示例中那样同时涉及两个对象,则可以将更多处理移至 Hero 中:

class Hero:
    def __init__(self):
        self.weapon = None
        self.inTummy = None

    def equip(self, weapon):
        print("I shall equip it.")
        self.weapon = weapon

class Sword:
    def equip(self, hero):
        hero.equip(self)

这可能看起来有点奇怪,但是在一个类上有一个方法只是委托(delegate)给另一个类上的相关方法(例如,在这里调用 sword.equip 只需调用 hero.equip)。你也可以反过来做,让 Hero.equip 调用 weapon.equip()weapon.ready() 或其他, 如果该元素不是武器因此没有这样的属性,它将失败。

另一件事是,在您的第一个示例中您仍然可以有鸭子类型(duck typing)的行为,只是直到稍后您尝试使用武器做其他事情时才会出现错误。像这样的东西:

hero.equip(apple)  # no error
hero.weapon.calculateDamage() # AttributeError: Apple object has no attribute `damage`

这可能不被认为是理想的,因为你直到后来才知道你装备了一把无效的武器。但这就是鸭子类型(duck typing)的工作原理:在您实际尝试触发错误的操作之前,您不知道自己是否做错了什么。

如果您对一个物体要做的只是扔它,保龄球和鸭子类型(duck typing)一样有效。只有当您尝试让它游泳或飞行时,您才会注意到保龄球不是鸭子类型(duck typing)。同样,如果你要装备一件“武器”只是把它系在腰带上或拿在手里,你可以用苹果和剑来做到这一点;在您尝试在战斗中实际使用苹果之前,您不会发现任何不对劲。

关于python - 如何使用 duck typing 编写 OOP 一致的代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33222741/

相关文章:

python - 如何在运行时更改工具栏中 Action 的图标?

python - 使用 python 获取 youtube 数据

C++ 列表插入奇怪的行为

python - 表单对象没有属性 'save_m2m' django

python - 为什么小数不能与 float 互操作

javascript - 我如何命名伪经典javascript

javascript oo 问题

C++多重继承和鸭子类型(duck typing)

Java:如何声明一个变量实现了一个接口(interface)?

c# - 为什么集合初始化抛出 NullReferenceException