我想测试一个对象是否是一个类的实例,并且只测试这个类(没有子类)。我可以通过以下方式来做到:
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类型的对象并不重要,给定一个对象,只有一个对象是它的类,因此在这种情况下比较比较是安全的。由于参考比较总是比价值比较便宜,我想知道我上面的断言是否成立。我得出的结论是,除非有人提出相反的证据。
- 如果你想知道哪一个最适合你的使用,只需用timeit模块测试它。
- 类不是单例的——它们是它们的元类的实例——默认的元类是"类型",而一个普通的python程序中会有很多实例。
- @我不是这个意思。当然有很多实例,但是只有一个是给定对象的"类",对吗?换句话说,如果x.__class__ == a和a == b,那么a is b(当然,对于行为良好的__eq__),这就是我试图证实/反驳的猜想。
- a is b与__eq__在对象a和b上无关。它不再检查id(a) == id(b)。jsbueno说,由于x.__class__不是单体的,因此不能保证不会有更多的单体漂浮在周围,因此不要使用is。
- 感谢大家的反馈,我认为所有的回答(和评论)都为讨论增添了一些有用的东西。但是我只能选择一个作为"被接受的",所以我选择的是我觉得更准确的。
- 标题是对我大脑语法单元的DoS攻击:)
- 类型(obj)和对象类之间的差异可能重复__
- @ AloisMahdal,这就是为什么我认为Python比C++复杂得多的原因。
- @德清,这是一个逻辑谬论:题目是英文和python混合的,所以题目的性质[我的大脑中是ddos]并没有提到python的复杂性;它只谈到混合这两个词的特殊情况(因为python使用普通en的词作为is),以及ab的效果。记号的感觉)。(我确实喜欢这个标题——我的评论只是个笑话)。
- @mgibsonbr if x.__class__ == a and a == b then a is b参见这里->类对象在python中是单例的吗?
对于旧式课程,有一个区别:
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__()。
- 那会使__class__更可取,对吧?因为它既适用于新旧样式的类。
- 我已经尽可能地扩大了我的回答范围来回答这个问题。我不认为有一个普遍的答案,但是如果你确定你不需要在老式的类中使用这个,那么type()应该工作得更好。
is只应用于身份检查,而不应用于类型检查(该规则有一个例外,您可以并且应该使用is进行单件检查)。
注:我一般不会使用type和==进行类型检查。类型检查的首选方法是isinstance(obj, Foo)。如果您有理由检查某个东西是否不是子类实例,我觉得它闻起来像一个可疑的设计。当class Foo(Bar):时,那么Bar就是Foo,您应该避免任何情况,即您的代码的某些部分必须在Foo实例上工作,而在Bar实例上中断。
- 正确的。如果你做不到,你通常会有一个坏的设计。
- 谢谢,但我明确地说"没有子类"…
- 哎呀,我错过了!
- 顺便说一句,我同意我的设计不是最好的,但是我正在写一些非常专业的类,它们需要为空间进行优化,并且不打算在我的库外被子类化。不过,在一般情况下,我不会这么做……
- 如果要优化空间,请考虑使用__slots__。小心操作!:)
- @Jathanism非常感谢,甚至没有想到在python中存在这样的东西!并不能解决我所有的问题,但对大多数人来说,这比我所知道的其他选择要好。
- 我在代码中使用IsInstance 99%的时间,但是在一些地方我使用类型(X),在这些地方对子类执行操作是不起作用的。例如,假设我有一个槽型类,我知道它只有两个属性,它们只能是简单的对象,但是它们被复制了很多,我可以节省大量的时间来复制重写的__deepcopy__,从而简单地用这两个属性(比copy.py的代码快)创建一个新的对象,然后如果是子类,就使用copy.py代码。那么,子类不需要知道如何重写__deepcopy__本身。
在新样式类中,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的行为是错误的,即使Foo是Bar的一个基本类。
- 你能举一个相等但不相同的类对象的例子吗?
- @迈克尔霍夫曼:如果使用元类,可以为类本身重写__eq__方法吗?我看不出它有什么用处,但这是我能想到的唯一例外。
- @迈克尔霍夫曼-我不能,但这是一个值比较,不能保证等价值具有相同的特性。在这种情况下,它是一个实现细节,即使在某些情况下(这可能是其中之一),实现使得不可能有相等和不相同的值,但这并不保证它的安全。
- @F.J是的,这正是我的观点。python的风格指南说,"像none这样的单例比较应该总是用is或is来完成,而不是用相等运算符。"好吧,一个类对象就像一个单例,从这个意义上说,另一个类永远不会是一个对象的"类"…或者至少我的常识是这样说的。不过,我想找一些参考资料来证实(或反驳)这一点。
- "type()的结果相当于obj.uu class_uuu in new style classes"是关于健全代码的合理假设,但python并不能保证这一点。如果类体定义包含__class__ = 123,那么内置的type返回正确的结果(即返回有效控制行为的对象)。人们也可能会对getattribute做一些不好的事情。
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> |
正如您所看到的,t1和t2在内存中打印出相同的值,即使t1和t2在内存中的值不同。这是证明。
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,除非你知道一个事实实例确实存在。
- 谢谢你的回答!关于最后一条注释,只需粘贴整个代码,然后选择它并单击"代码示例"按钮(或者在粘贴到这里之前只需将代码缩进4个空格,以较容易的为准)。
- 这不是"证据",只是一个例子。这是另一个x, y = 7, 7; x is y将计算为真,但它并不能证明整数是单子数(它们不是)。