代码之家  ›  专栏  ›  技术社区  ›  Wayne Werner

isinstance pythonic/“good”的用法是否正确?

  •  9
  • Wayne Werner  · 技术社区  · 14 年前

    this 问题是我被引到 this post ,其中说明:

    每当使用isinstance时,控制流分叉;一种类型的对象沿着一条代码路径运行,而另一种类型的对象沿着另一条代码路径运行——即使它们实现了相同的接口!

    然而,我以前用过这样的代码,我认为这是一种面向对象的方式。如下所示:

    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”哦?

    4 回复  |  直到 7 年前
        1
  •  7
  •   Justin Ethier    14 年前

    other 使用一致的接口:

    def __iadd__(self, other):
        self.h += other.h
        self.m += other.m
        self.s += other.s
    

    尽管这看起来不太实用,但从概念上讲,它要干净得多。现在让语言来抛出异常,如果 其他 int MyTime “构造函数”使用整数的“接口”。这使得代码更干净,给下一个人留下的惊喜更少。

    其他人可能不同意,但我觉得可能有一个地方 isinstance 如果您在特殊情况下使用反射,例如在实现插件体系结构时。

        2
  •  4
  •   Alex Martelli    14 年前

    isinstance ,自Python 2.6以来,只要遵循经典的“4人帮”一书中解释的“良好设计的关键规则”,就变得相当不错: 设计是为了接口,而不是为了实现 . 具体来说,2.6的新抽象基类是您应该用于 存在 issubclass 检查, 具体的“实现”类型。

    不幸的是,在2.6的标准库中没有抽象类来概括“这个数是整数”的概念,但是您可以通过检查这个类是否有特殊的方法来创建一个这样的ABC __index__ ( 不要 使用 __int__ float str -- 是专门用来断言“这个类的实例可以成为整数而不丢失重要信息”)和使用的 而不是具体的实现 int

    你也可以做一个ABC总结“拥有m,h和s属性”的概念(可能有助于接受属性同义词以便容忍 datetime.time 或者也许 timedelta 举个例子——不确定你是在用你的语言表达一个瞬间还是一段时间的流逝 MyTime 存在 具体实施 货运财务结算系统。

        3
  •  2
  •   Ignacio Vazquez-Abrams    14 年前

    第一次使用是好的,第二次不是。把论点传给 int() 相反,这样您就可以使用类似于数字的类型。

        4
  •  2
  •   Brandon    14 年前

    为了进一步阐述我在贾斯汀的回答下所作的评论,我将保留他的代码 __iadd__ __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)
    

    我只是试着选择简短的关键字参数,但你可能想得到更多的描述性比我。