this问题的一个副作用是我引导this post,其中指出:
每当使用isinstance时,控制流程叉;一种类型的对象沿着一条代码路径向下,而其他类型的对象沿着另一条对象向下移动 - 即使它们实现了相同的接口!
并暗示这是一件坏事。
然而,我之前使用过这样的代码,我认为这是一种OO方式。如下所示:
class MyTime(object):
def __init__(self, h=0, m=0, s=0):
self.h = 0
self.m = 0
self.s = 0
def __iadd__(self, other):
if isinstance(other, MyTime):
self.h += other.h
self.m += other.m
self.s += other.s
elif isinstance(other, int):
self.h += other/3600
other %= 3600
self.m += other/60
other %= 60
self.s += other
else:
raise TypeError('Addition not supported for ' + type(other).__name__)
所以我的问题:
是否使用isinstance
“pythonic”和“good”OOP?
答案 0 :(得分:5)
不一般。对象的接口应该定义它的行为。在上面的示例中,如果other
使用了一致的接口,那会更好:
def __iadd__(self, other):
self.h += other.h
self.m += other.m
self.s += other.s
即使这看起来功能较少,但从概念上来说它更清洁。现在,如果other
与接口不匹配,则将其保留为语言以引发异常。您可以通过 - 例如 - 使用整数的“接口”创建int
“构造函数”来解决添加MyTime
次的问题。这样可以使代码更清晰,为下一个人留下更少的惊喜。
其他人可能不同意,但我觉得如果你在特殊情况下使用反射,例如在实现插件架构时,可能会有isinstance
的位置。
答案 1 :(得分:4)
isinstance
,自从Python 2.6以来,只要你遵循经典的“4人帮”书中所解释的“良好设计的关键规则”已经变得非常好:设计到界面,不是实施。具体来说,2.6的新抽象基类是您应该用于isinstance
和issubclass
检查的唯一内容,不是具体的“实现”类型。
不幸的是,在2.6的标准库中没有抽象类来概括“这个数字是积分”的概念,但你可以通过检查类是否有一个特殊的方法__index__
来构建一个这样的ABC(< strong>不使用__int__
,这也是由float
和str
- __index__
等明确的非整数类提供的断言“这个类的实例可以变成整数而不会丢失重要信息”)并在那个“接口”(抽象基类)而不是特定的实现isinstance
上使用int
,这是一种方式限制太多了。
您还可以制作一个ABC,总结“拥有m,h和s属性”的概念(可能有助于接受属性同义词以容忍datetime.time
或timedelta
个实例,例如 - 不确定你是代表你的MyTime
班级的瞬间或时间流逝,名称暗示前者但存在加法表明后者),再次避免isinstance
的限制性影响{ {1}}使用具体实现 cass。
答案 2 :(得分:2)
第一次使用很好,第二种不是。将参数传递给int()
,以便您可以使用类似数字的类型。
答案 3 :(得分:1)
为了进一步阐述我在Justin的回答中所做的评论,我会保留他的代码__iadd__
(即,所以MyTime对象只能添加到其他MyTime对象)并在此重写__init__
方式:
def __init__(self, **params):
if params.get('sec'):
t = params['sec']
self.h = t/3600
t %= 3600
self.m = t/60
t %= 60
self.s = t
elif params.get('time'):
t = params['time']
self.h = t.h
self.m = t.m
self.s = t.s
else:
if params:
raise TypeError("__init__() got unexpected keyword argument '%s'" % params.keys()[0])
else:
raise TypeError("__init__() expected keyword argument 'sec' or 'time'")
# example usage
t1 = MyTime(sec=30)
t2 = MyTime(sec=60)
t2 += t1
t3 = MyTime(time=t1)
我只是尝试选择简短的关键字参数,但您可能希望获得比我更具描述性的信息。