当前位置: 代码迷 >> python >> 如何用鸭子打字编写OOP一致代码?
  详细解决方案

如何用鸭子打字编写OOP一致代码?

热度:73   发布时间:2023-06-13 14:01:10.0

我在确定方法在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)

鸭子类型的代码具有明显的优势,我可以在任何时候执行尝试 -以确定我是否正在执行“合法”操作:

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

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

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

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

感觉非常陌生。

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

提前致谢。

没有明确的答案; 这取决于你的课程。 检查你的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)

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

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

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

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

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

  相关解决方案