Python 为什么从类和实例获取属性的查找过程不同?

Python 为什么从类和实例获取属性的查找过程不同?,python,python-3.x,class,attributes,metaclass,Python,Python 3.x,Class,Attributes,Metaclass,简而言之,Python描述了获取属性时的查找过程。这本书区分了两种情况 从类中获取属性时的查找过程,例如cls.name 从类中获取属性 使用语法C.name引用属性时 在类对象C上,查找分两步进行: 当name是C.\uuuu dict\uuuu中的一个键时,C.name从C.\uu dict\uuuu['name']中获取值v。然后,当v为 描述符(即,type(v)提供一个名为 \uuuu get\uuuu),C.name的值是调用 键入(v)。\uu获取(v,无,C)。当v不是描述符时

简而言之,Python描述了获取属性时的查找过程。这本书区分了两种情况

  • 从类中获取属性时的查找过程,例如
    cls.name

    从类中获取属性

    使用语法
    C.name
    引用属性时 在类对象
    C
    上,查找分两步进行:

  • name
    C.\uuuu dict\uuuu
    中的一个键时,
    C.name
    C.\uu dict\uuuu['name']
    中获取值
    v
    。然后,当
    v
    为 描述符(即,
    type(v)
    提供一个名为
    \uuuu get\uuuu
    ),C.name的值是调用
    键入(v)。\uu获取(v,无,C)
    。当
    v
    不是描述符时,
    C.name的值为v

  • name
    不是
    C.中的键时,
    C.name
    将查找委托给
    C
    的基类,这意味着它在
    C
    上循环 祖先类并尝试在每个(in)方法上进行名称查找 决议顺序,如第页“方法决议顺序”所述 113)

因为在Python 3中,每个类对象实际上都是其元类的一个实例(例如,
类型
类),根据这本书,为什么从类中获取属性的查找过程和从实例中获取属性的查找过程不同呢?

它们没有太大的不同,书中的描述涵盖了两种不同的方式:

  • 在类实例上找到的描述符(在类上找不到之后)不会被调用(
    a.x=somedescriptor()
    其中
    a
    是类实例,而不是类,后跟
    a.x
    将只返回您刚刚创建的
    somedescriptor()
    的实例),虽然在元类实例上找到的描述符,即类(在元类上找不到之后)被调用时,其实例是
    None
    a.x=somedescriptor()
    ,其中
    a
    是元类实例,而不是元类,将调用
    上的
    。\u get\uuuuu(None,a)
    您刚刚制作了一个)。这允许像
    @classmethod
    这样的东西通过将方法绑定到类本身来工作,不管它是在类的实例上查找还是在类本身上查找
  • 类实例没有“父实例”的概念(类实例本身的名称空间是一个平面
    dict
    ,即使与该类实例相关联的属性是由来自多个继承级别的方法定义的),因此基于MRO的查找思想对于元类实例来说是独特的
  • 其他一切都差不多,只是这本书在这里美化了元类的概念,因为大多数类都是基本
    类型
    的实例,它没有特殊的行为。如果您的元类不是
    类型
    ,则在查找类的属性时会应用完整的实例查找规则(只是类的类是元类)


    他们可能在早期试图避免元类的复杂性,但在这个过程中,实例查找规则似乎不适用于类;确实如此,只是类在基本查找过程中添加了一些额外的规则。

    谢谢!你是说从书中的类中获取属性的查找过程只有在类是
    类型
    类的实例时才起作用吗?我还可以问一下你的想法吗?@Tim:这意味着当它是
    类型
    的实例时,规则会像写的那样适用,但是如果它有其他元类,这些规则可以调用“普通”实例的所有行为。一个真正简单的元类可能最终表现出与
    类型
    相同的行为(因为它是
    类型
    的一个子类,它不需要有任何不同),一个更复杂的元类可能会定义所有使实例更复杂的小奇怪的事情。所谓“它是
    类型
    的一个子类”,你的意思是“它是
    类型
    的一个实例”吗相反(我猜“it”的意思是“一个非常简单的元类”)?如果一个真正简单的元类与
    类型
    没有任何区别,那么使用真正简单的元类而不是直接使用
    类型
    作为元类的原因是什么?@Maggyero:啊,你说得对。我花了一点时间弄明白你在说什么(我最初把它误读为一个声明,声称在访问使用它的类上的内容时,元类上没有调用描述符协议)。您不能让常规实例在每个实例属性上调用描述符协议(比如
    a.x=property(somefunc)
    不会让
    a.x
    执行任何操作,而是返回原始的
    属性
    对象),但当元类不通过描述符提供重写时,它会在每个类属性上发生。我将调整答案以涵盖这一点。