我很难在Python程序中确定方法的放置,在这里,像我习惯依赖的鸭子打字方法和我的OOP本能是不一致的。
举例来说,假设我们有三个类:英雄,剑和苹果。英雄可以装备剑,英雄可以吃苹果。
如果按照我的直觉,我想代码应该是这样的:
无鸭.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!")

感觉很像蟒蛇,而另一种方法需要我执行可怕的类型检查。
然而,从OOP的观点来看,奇怪的是,一把剑负责装备自己,并且它接收一个英雄作为一个参数。装备的动作看起来像是英雄表演的动作,因此,我觉得这个方法应该属于英雄类。整个语法
def eat(self, hero):
    self.define()
    hero.onEat()
    hero.inTummy = self

感觉很陌生。
两种方法都更像蟒蛇吗?是不是OOP更一致?我是不是应该换一个解决方案?
提前谢谢。

最佳答案

没有明确的答案,这取决于你的班级做什么。在你的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只是调用hero.equip)并不一定是坏事。你也可以用另一种方法,用Hero.equipcallweapon.equip()weapon.ready()或其他什么方法,如果物品不是武器,因此没有这样的属性,则失败。
另一件事是,在第一个例子中,你仍然可以有duck输入行为,只是当你试图用武器做其他事情的时候,这个错误直到后期才会出现。类似:
hero.equip(apple)  # no error
hero.weapon.calculateDamage() # AttributeError: Apple object has no attribute `damage`

这可能不被认为是理想的,因为你不知道你装备了一个无效武器,直到后来。但这就是鸭子打字的原理:你不知道你做了什么错事,直到你真的尝试了一个触发错误的动作。
如果你要做的就是扔掉一个物体,保龄球和鸭子一样管用。只有当你试着让它游起来或飞起来时,你才会注意到保龄球不是鸭子。同样,如果你要装备的“武器”只是把它系在腰带上或握在手上,你可以用苹果和剑来装备它;除非你真的想在战场上挥舞苹果,否则你不会发现任何问题。

07-24 14:39