如何用duck typing编写OOP一致代码?

时间:2015-10-19 19:53:01

标签: python oop duck-typing

我在决定在python程序中放置方法时遇到了麻烦,我以前依赖的鸭子打字方式似乎与我的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)

鸭子类型代码具有明显的优势,我可以随时执行尝试 - 除以确定我是否正在执行“合法”#34;动作:

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

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

然而,从OOP的角度来看,感觉很奇怪,负责装备自己,并且它接收英雄作为参数。装备行为似乎是由英雄执行的动作,因此,我觉得该方法应该属于 Hero 类。

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

感觉非常陌生。

是否更接近pythonic?要么更多OOP一致吗?我应该完全看一个不同的解决方案吗?

提前致谢。

1 个答案:

答案 0 :(得分:4)

没有明确的答案;这取决于你的课程。检查isinstance(weapon, Weapon)中的Hero.equip来检查物品是否是武器并不是那么可怕。此外,如果你要像第二个例子那样涉及两个对象,你可以将更多的处理移动到英雄中:

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)

这可能看起来有点奇怪,但是在一个类上只有一个方法代表另一个类的相关方法并不一定是坏事(因为,在这里调用sword.equip只调用{{ 1}})。你也可以反过来做,并hero.equip调用Hero.equipweapon.equip()或其他什么,如果项目不是武器就会失败,因此没有属性。

另一件事是你在第一个例子中仍然可以有鸭子打字行为,只是在你尝试用武器做其他事情之后才会提出错误。类似的东西:

weapon.ready()

这可能不是理想的,因为你不知道你装备了无效武器直到以后。但这就是鸭子打字的工作方式:在你真正尝试触发这种错误的行动之前,你不知道自己是否做错了什么。

如果你要用一个物体扔掉它,那么一个保龄球和一只鸭子一样好。只有当你试图让它游泳或飞行或其他任何你会注意到保龄球不是鸭子。同样地,如果你要做的就是装备一件“武器”,将它绑在你的腰带上或用手拿着它,你可以用苹果和剑来做到这一点;在你试图在战斗中实际使用苹果之前,你不会注意到任何不妥。