【问题标题】:Difference between type(obj) and obj.__class__type(obj) 和 obj.__class__ 的区别
【发布时间】:2010-11-06 19:49:13
【问题描述】:

type(obj)obj.__class__ 有什么区别?有没有type(obj) is not obj.__class__的可能性?

我想编写一个在提供的对象上通用的函数,使用与另一个参数相同类型的默认值 1。下面的 #1 或 #2 哪个变体会做正确的事情?

def f(a, b=None):
  if b is None:
    b = type(a)(1) # #1
    b = a.__class__(1) # #2

【问题讨论】:

    标签: python new-style-class


    【解决方案1】:

    这是一个老问题,但似乎没有一个答案提到这一点。在一般情况下,新样式类IS可能对type(instance)instance.__class__ 具有不同的值:

    class ClassA(object):
        def display(self):
            print("ClassA")
    
    class ClassB(object):
        __class__ = ClassA
    
        def display(self):
            print("ClassB")
    
    instance = ClassB()
    
    print(type(instance))
    print(instance.__class__)
    instance.display()
    

    输出:

    <class '__main__.ClassB'>
    <class '__main__.ClassA'>
    ClassB
    

    原因是ClassB 覆盖了__class__ 描述符,但是对象中的内部类型字段没有改变。 type(instance) 直接从该类型字段读取,因此它返回正确的值,而 instance.__class__ 指的是新描述符替换了 Python 提供的原始描述符,它读取内部类型字段。它不是读取该内部类型字段,而是返回一个硬编码值。

    【讨论】:

    • Caveat lector:这应该作为一个例子来说明为什么你应该避免覆盖__class__!您可能会导致使用__class__ 的代码行中断。
    • 同样受__getattribute__的影响,它会拦截OBJ.__class__的请求,但不会拦截type(OBJ)的请求。
    • 一些代码故意这样做是为了谎报对象的类型,例如weakref.proxy。有些人认为应该首选obj.__class__,因为它相信谎言,而有些人认为应该首选type(obj),因为它忽略了谎言。如果对象的真实类或其谎言类是第二个参数的实例,isinstance 将返回 true。
    • @BenjaminHodgson 这主要说明了为什么魔术__ 属性首先不应该被覆盖......
    • 这里需要注意的重要一点是:在 instance 中分配 __class__ 属性会有效地改变它的类型和“真正的”类(包括事后更改“type”和“isinstance”响应)
    【解决方案2】:

    旧式类是问题所在,叹气:

    >>> class old: pass
    ... 
    >>> x=old()
    >>> type(x)
    <type 'instance'>
    >>> x.__class__
    <class __main__.old at 0x6a150>
    >>> 
    

    在 Python 3 中不是问题,因为现在所有类都是新样式的;-)。

    在 Python 2 中,只有从另一个新样式类(包括 object 和各种内置类型,例如 dictlistset、. ..) 或隐式或显式将__metaclass__ 设置为type

    【讨论】:

    • 现在是 Python 3 时代,我们应该应该使用哪个?
    • 如果您的代码遵循 Alex 描述的最佳实践,type() 会更好。在 Python 3 中,它始终遵循最佳实践,因此在 Python 3 中使用 type()。
    • @AaronHall 如果我正在编写 Python 2 代码并且我知道它只会被 new - 风格的课程?
    • @kasperd 是的,如果您的代码总是从对象继承,那么使用type() 是安全的。
    • @Alex Martelli @AaronHall 内置函数type(instance) 和属性instance.__class__ 可以不同,即使使用new-style classes,正如Guido 在PEP 3119 中提到的那样:“另外,@987654336 @ 等同于 issubclass(x.__class__, B) or issubclass(type(x), B)。(type(x)x.__class__ 可能不是同一个对象,例如,当 x 是代理对象时。)“并如 @Flavien 的回答所示。
    【解决方案3】:

    type(obj)type.__class__ 对于旧样式类的行为不同:

    >>> class a(object):
    ...     pass
    ...
    >>> class b(a):
    ...     pass
    ...
    >>> class c:
    ...     pass
    ...
    >>> ai=a()
    >>> bi=b()
    >>> ci=c()
    >>> type(ai) is ai.__class__
    True
    >>> type(bi) is bi.__class__
    True
    >>> type(ci) is ci.__class__
    False
    

    【讨论】:

    • 最大的讽刺是 yairchu 的评论现在有同样的问题,因为他们切换了格式.. :P
    • 展示如何他们的行为不同,也许还有为什么。仅仅说他们的行为不同听起来是一个懒惰的答案,即使是正确的。
    • 值得一提的是,这只是 Python 2 中的问题。在 Python 3 中,所有三个表达式都是 True。
    猜你喜欢
    • 1970-01-01
    • 2020-02-03
    • 2014-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-06
    • 1970-01-01
    • 2014-09-03
    相关资源
    最近更新 更多