Python type()或__class __,==或is

mgi*_*nbr 47 python language-features

我想测试一个对象是否是一个类的实例,并且只测试这个类(没有子类).我可以这样做:

obj.__class__ == Foo
obj.__class__ is Foo
type(obj) == Foo
type(obj) is Foo
Run Code Online (Sandbox Code Playgroud)

是否有理由选择一个而不是另一个?(业绩差异,陷阱等)

换句话说:a)使用__class__type(x)?之间有什么实际区别吗?b)类对象总是可以安全地进行比较is吗?


更新:感谢大家的反馈.我仍然对类对象是否是单身人士感到困惑,我的常识说他们是,但是很难得到确认(尝试谷歌搜索"python","class"和"unique"或"singleton") .

我还要澄清一点,根据我的特殊需求,"更便宜"的解决方案才是最好的,因为我正在尝试优化一些专业课程中的大部分(几乎达到了理智的程度)要做的是删除Python并在C)中开发该特定模块.但问题背后的原因是为了更好地理解语言,因为它的某些功能对于我来说很容易找到这些信息.这就是为什么我让讨论延伸一点而不是解决__class__ is,所以我可以听到更有经验的人的意见.到目前为止,它已经非常富有成效!

我进行了一项小测试,以测试4种替代方案的性能.分析器结果是:

               Python  PyPy (4x)
type()    is   2.138   2.594
__class__ is   2.185   2.437
type()    ==   2.213   2.625
__class__ ==   2.271   2.453
Run Code Online (Sandbox Code Playgroud)

不出所料,is表现优于==所有情况.type()在Python中表现更好(2%更快)并且__class__在PyPy中表现更好(快6%).有趣的是,__class__ ==在PyPy 中表现更好type() is.


更新2:很多人似乎并不理解我所说的"一个类是一个单身人士",所以我将用一个例子说明:

>>> class Foo(object): pass
...
>>> X = Foo
>>> class Foo(object): pass
...
>>> X == Foo
False
>>> isinstance(X(), Foo)
False
>>> isinstance(Foo(), X)
False

>>> x = type('Foo', (object,), dict())
>>> y = type('Foo', (object,), dict())
>>> x == y
False
>>> isinstance(x(), y)
False

>>> y = copy.copy(x)
>>> x == y
True
>>> x is y
True
>>> isinstance(x(), y)
True
>>> y = copy.deepcopy(x)
>>> x == y
True
>>> x is y
True
>>> isinstance(x(), y)
True
Run Code Online (Sandbox Code Playgroud)

如果存在N个类型type的对象并不重要,给定一个对象,只有一个将是它的类,因此在这种情况下比较引用是安全的.由于参考比较总是比价值比较便宜,我想知道我的断言是否成立.我得出的结论是,除非有人提出相反的证据.

Mic*_*man 32

对于旧式课程,有一个区别:

>>> class X: pass
... 
>>> type(X)
<type 'classobj'>
>>> X.__class__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class X has no attribute '__class__'
>>> x = X()
>>> x.__class__
<class __main__.X at 0x171b5d50>
>>> type(x)
<type 'instance'>
Run Code Online (Sandbox Code Playgroud)

新式课程的重点是统一课程和类型.从技术上讲,__class__它是唯一适用于新旧类实例的解决方案,但它也会在旧式类对象本身上引发异常.您可以调用type()任何对象,但不是每个对象都有__class__.此外,你可以用你不能捣乱__class__的方式捣乱type().

>>> class Z(object):
...     def __getattribute__(self, name):
...             return "ham"
... 
>>> z = Z()
>>> z.__class__
'ham'
>>> type(z)
<class '__main__.Z'>
Run Code Online (Sandbox Code Playgroud)

就个人而言,我通常只有一个新风格的环境,并且type()由于我喜欢使用内置函数,因此我喜欢使用魔术属性.举例来说,我也宁愿bool(x)x.__nonzero__().

  • 那将使__class__更可取,对吧?因为它适用于新旧样式类。 (2认同)
  • 我已经扩展了我的答案以尽可能地回答这个问题.我不认为有一个通用的答案,但如果你可以确定你不需要在旧式类上使用它,`type()`应该更好. (2认同)

And*_*ark 12

结果type()等同obj.__class__于新样式类,并且类对象不能安全地进行比较使用is,==而是使用.

对于新风格的类,这里更好的方式是type(obj) == Foo.

正如Michael Hoffman在他的回答中指出的那样,新旧类型之间存在差异,因此对于可能需要使用的向后兼容代码obj.__class__ == Foo.

对于声称isinstance(obj, Foo)更可取的人,请考虑以下情形:

class Foo(object):
    pass

class Bar(Foo):
    pass

>>> obj = Bar()
>>> isinstance(obj, Foo)
True
>>> type(obj) == Foo
False
Run Code Online (Sandbox Code Playgroud)

OP想要行为type(obj) == Foo,即使它Foo是基类,它也会是假的Bar.

  • 你能给出一个相等但不相同的类对象的例子吗? (3认同)
  • @FJ是的,这正是我的观点.Python的[样式指南](http://www.python.org/dev/peps/pep-0008/)说"比较单身像无,应该总是用'是'或'不是'来做,永远不是平等的运营商".好吧,一个类对象就像一个单例,在某种意义上说,另一个类永远不会是对象的"类"...或者至少我的常识是这样说的.但是,我想找到确认(或反驳)的参考. (3认同)
  • "type()的结果相当于新样式类中的obj .__ class__"对于理智的代码是一个合理的假设,但Python无法保证.如果类主体定义包含`__class__ = 123`,则内置`type`返回正确的结果(即,它返回有效控制行为的对象).人们也可能用`getattribute`做一些讨厌的事情. (3认同)
  • @MichaelHoffman:如果您使用元类,您可以重写类本身的“__eq__”方法吗?我看不出它有任何用处,但这是我能想到的唯一例外。 (2认同)

wim*_*wim 12

is应该只用于身份检查,而不是类型检查(规则中有一个例外情况,您可以并且应该用它is来检查单身人士).

注意:我通常也不会使用type==进行类型检查.类型检查的首选方法是isinstance(obj, Foo).如果你有理由检查某些东西是不是一个子类实例,它对我来说就像一个腥味的设计.什么时候class Foo(Bar):,然后Bar Foo,你应该避免任何情况,你的代码的某些部分必须在一个Foo实例上工作,但在一个Bar实例上中断.

  • 如果您正在优化空间,请考虑使用[`__slots__`](http://docs.python.org/reference/datamodel.html#slots).谨慎行事!:) (3认同)