关于语言特性:python type()或 __class__,==或is

Python type() or __class__, == or is

我想测试一个对象是否是一个类的实例,并且只测试这个类(没有子类)。我可以通过以下方式来做到:

1
2
3
4
obj.__class__ == Foo
obj.__class__ is Foo
type(obj) == Foo
type(obj) is Foo

有没有理由选择一个而不是另一个?(性能差异、陷阱等)

换言之:a)使用__class__type(x)有什么实际区别吗?b)使用is比较类对象是否总是安全的?

更新:感谢所有人的反馈。我仍然对类对象是否是单例感到困惑,我的常识是这样说的,但是很难得到确认(尝试搜索"python"、"class"和"unique"或"singleton")。

我还想澄清的是,为了满足我的特殊需求,最有效的"更便宜"的解决方案是最好的,因为我正试图从一些专门的类中优化出最多的类(几乎达到了明智的做法是去掉python并在c中开发特定的模块)。但问题背后的原因是为了更好地理解语言,因为它的一些特性对我来说太模糊了,我无法轻易地找到这些信息。这就是为什么我要让讨论稍微扩展一点,而不是让我接受__class__ is,这样我就能听到更多有经验的人的意见。到目前为止,这是非常有成效的!

我做了一个小测试来测试这4个备选方案的性能。探查器结果为:

1
2
3
4
5
               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

不出所料,在所有情况下,is的表现都优于==type()在python中表现更好(快2%),__class__在pypyy中表现更好(快6%)。有趣的是,与type() is相比,__class__ ==在pypy的表现更好。

更新2:很多人似乎不理解我所说的"类是单例的",因此我将举一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
>>> 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

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> 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'>

新课程的重点是统一课程和类型。从技术上讲,__class__是唯一既适用于新样式类实例又适用于旧样式类实例的解决方案,但它也会对旧样式类对象本身抛出异常。您可以对任何对象调用type(),但并非每个对象都有__class__。另外,你也可以用一种你不能用type()的方式与__class__混在一起。

1
2
3
4
5
6
7
8
9
>>> class Z(object):
...     def __getattribute__(self, name):
...             return"ham"
...
>>> z = Z()
>>> z.__class__
'ham'
>>> type(z)
<class '__main__.Z'>

就我个人而言,我通常只有一个新样式类的环境,而且就样式而言,我更喜欢使用type(),因为我通常喜欢使用内置函数,而不喜欢使用魔法属性。例如,我更喜欢bool(x)而不是x.__nonzero__()


is只应用于身份检查,而不应用于类型检查(该规则有一个例外,您可以并且应该使用is进行单件检查)。

注:我一般不会使用type==进行类型检查。类型检查的首选方法是isinstance(obj, Foo)。如果您有理由检查某个东西是否不是子类实例,我觉得它闻起来像一个可疑的设计。当class Foo(Bar):时,那么Bar就是Foo,您应该避免任何情况,即您的代码的某些部分必须在Foo实例上工作,而在Bar实例上中断。


在新样式类中,type()的结果相当于obj.__class__,使用is比较类对象是不安全的,使用==代替。

对于新样式的类,这里最好的方法是type(obj) == Foo

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

对于那些声称isinstance(obj, Foo)是首选方案的人,考虑以下情况:

1
2
3
4
5
6
7
8
9
10
11
class Foo(object):
    pass

class Bar(Foo):
    pass

>>> obj = Bar()
>>> isinstance(obj, Foo)
True
>>> type(obj) == Foo
False

OP希望type(obj) == Foo的行为是错误的,即使FooBar的一个基本类。


Update: Thanks all for the feedback. I'm still puzzled by whether or not class objects are singletons, my common sense says they are, but it's been really hard to get a confirmation (try googling for"python","class" and"unique" or"singleton").

我可以确认,__instance__是单件的。这是证据。

1
2
3
4
5
6
7
8
9
10
>>> t1=File.test()
made class
>>> t2=File.test()
made class
>>> print t1.__class__()
made class
<File.test object at 0x1101bdd10>
>>> print t2.__class__()
made class
<File.test object at 0x1101bdd10>

正如您所看到的,t1t2在内存中打印出相同的值,即使t1t2在内存中的值不同。这是证明。

1
2
3
4
>>> print t1
<File.test object at 0x1101bdc90>
>>> print t2
<File.test object at 0x1101bdcd0>

__instance__方法仅在使用class"name"(object):时存在。如果使用经典样式类class"name":而不存在__instance__方法。

这意味着要成为最通用的,你可能想要使用type,除非你知道一个事实实例确实存在。