我在决定方法在 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/