[英]How to write OOP consistent code with duck typing?
I'm having trouble in deciding on the placement of method in a python program, where it seems like the duck-typing approach I'm used to rely on is at odds with my OOP instincts. 我在确定方法在python程序中的位置时遇到了麻烦,似乎我习惯依赖的鸭子打字方法与我的OOP本能不一致。
To illustrate, suppose we have three classes: Hero, Sword and Apple. 为了说明,假设我们有三个类:Hero,Sword和Apple。 A hero can equip a sword and a hero can eat an apple.
英雄可以装备剑,英雄可以吃苹果。
If I were to follow my OOP gut, I think the code would look like this: 如果我要遵循我的OOP直觉,我认为代码看起来像这样:
duckless.py 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)
Which feels very intuitive and readable. 这感觉非常直观和可读。
If I were to duck-type, however, I feel like the code would look something like this: 但是,如果我要使用duck-type,我觉得代码看起来像这样:
duckfull.py 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)
The duck-typed code has the clear advantage that I can perform a try-except at any time to determine whether I'm performing a "legal" action: 鸭子类型的代码具有明显的优势,我可以在任何时候执行尝试 -以确定我是否正在执行“合法”操作:
try:
apl.equip()
except AttributeError:
print("I can't equip that!")
Which feels very pythonic, while the alternative would require me to perform dreaded type checks . 这感觉非常pythonic,而替代方案将要求我执行可怕的类型检查 。
However, from an OOP standpoint, it feels weird to be that a sword is responsible for equipping itself, and that it receives a hero as a parameter. 然而,从OOP的角度来看, 剑是负责装备自己并且它接收英雄作为参数感觉很奇怪。 The act of equipping seems like an action performed by the hero , and as such, I feel the method should belong in the Hero class.
装备的行为看起来像是由英雄执行的动作,因此,我觉得该方法应该属于英雄级别。 The whole syntax of
整个语法
def eat(self, hero):
self.define()
hero.onEat()
hero.inTummy = self
Feels very alien. 感觉非常陌生。
Is either approach more pythonic? 是否更接近pythonic? Is either more OOP consistent?
要么更多OOP一致吗? Should I be looking at a different solution altogether?
我应该完全看一个不同的解决方案吗?
Thanks in advance. 提前致谢。
There is no clear-cut answer; 没有明确的答案; it depends on what your classes do.
这取决于你的课程。 It is not so horrible to check
isinstance(weapon, Weapon)
in your Hero.equip
to check if the item is a weapon. 检查你的
Hero.equip
isinstance(weapon, Weapon)
以检查该物品是否是武器并不是那么可怕。 Also, if you're going to involve both objects as in your second example, you can move more of the handling into the Hero: 此外,如果您要在第二个示例中涉及两个对象,则可以将更多处理移动到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)
This may seem a bit strange, but it is not necessarily a bad thing to have a method on one class that just delegates to a related method on another class (as, here, calling sword.equip
just calls hero.equip
). 这可能看起来有点奇怪,但是在一个类上只有一个方法代表另一个类的相关方法并不一定是坏事(因为,这里调用
sword.equip
只调用hero.equip
)。 You could also do it the other way around, and have Hero.equip
call weapon.equip()
or weapon.ready()
or whatever, which will fail if the item isn't a weapon and so doesn't have such an attribute. 你也可以
Hero.equip
做,并让Hero.equip
调用weapon.equip()
或weapon.ready()
或者其他什么,如果该物品不是武器就会失败,因此没有这样的属性。
The other thing is that you can still have duck-typing behavior in your first example, it's just that the error won't be raised until a later stage when you try to do something else with the weapon. 另一件事是你在第一个例子中仍然可以有鸭子打字行为,只是在你尝试用武器做其他事情之后才会提出错误。 Something like:
就像是:
hero.equip(apple) # no error
hero.weapon.calculateDamage() # AttributeError: Apple object has no attribute `damage`
This may not be considered ideal, because you don't know you equipped an invalid weapon until later. 这可能不是理想的,因为你不知道你以后装备了无效武器。 But that's how duck-typing works: you don't know if you did something wrong until you actually attempt an action that triggers that wrongness.
但这就是鸭子打字的工作方式:在你真正尝试触发这种错误的行动之前,你不知道自己是否做错了什么。
If all you're going to do with an object is throw it, a bowling ball will work as well as a duck. 如果你想要用一个物体扔掉它,那么保龄球就像一只鸭子一样。 It's only when you try to make it swim or fly or whatever that you'll notice the bowling ball is not a duck.
只有当你试图让它游泳或飞行或其他任何你会注意到保龄球不是鸭子。 Likewise, if all you're going to do to equip a "weapon" is strap it to your belt or hold it in your hand, you can do that with an apple as well as with a sword;
同样地,如果你要做的就是装备一件“武器”,将它绑在你的腰带上或用手拿着它,你可以用苹果和剑来做到这一点; you won't notice anything amiss until you try to actually wield the apple in battle.
在你试图在战斗中实际运用苹果之前,你不会发现任何不妥之处。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.