方法解析和调用如何在Python内部工作? [英] How the method resolution and invocation works internally in Python?

查看:77
本文介绍了方法解析和调用如何在Python内部工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

方法调用如何在Python中运行?
我的意思是,python虚拟机如何解释它。

How the methods invocation works in Python? I mean, how the python virtual machine interpret it.

在Java中,Python中的python方法解析可能比较慢。
什么是后期绑定?

It's true that the python method resolution could be slower in Python that in Java. What is late binding?

这两种语言的反射机制有何不同?
哪里可以找到解释这些方面的好资源?

What are the differences on the reflection mechanism in these two languages? Where to find good resources explaining these aspects?

推荐答案

Python中的方法调用包含两个截然不同的可分离步骤。首先完成属性查找,然后调用该查找的结果。这意味着以下两个片段具有相同的语义:

Method invocation in Python consists of two distinct separable steps. First an attribute lookup is done, then the result of that lookup is invoked. This means that the following two snippets have the same semantics:

foo.bar()

method = foo.bar
method()

Python中的属性查找是一个相当复杂的过程。假设我们在对象 obj 上查找名为 attr 的属性,这意味着Python代码中的以下表达式: obj.attr

Attribute lookup in Python is a rather complex process. Say we are looking up attribute named attr on object obj, meaning the following expression in Python code: obj.attr

首先在 obj 的实例字典中搜索attr,然后搜索 obj 类的实例字典及其字典以attr的方法解析顺序搜索父类。

First obj's instance dictionary is searched for "attr", then the instance dictionary of the class of obj and the dictionaries of its parent classes are searched in method resolution order for "attr".

通常,如果在实例上找到一个值,则返回该值。但是如果对类的查找产生的值同时包含__get__和__set__方法(确切地说,如果对值类和父类的字典查找具有这两个键的值),那么类属性被视为某些东西称为数据描述符。这意味着调用该值的__get__方法,传入发生查找的对象并返回该值的结果。如果未找到class属性或不是数据描述符,则返回实例字典中的值。

Normally if a value is found on the instance, that is returned. But if the lookup on the class results in a value that has both the __get__ and __set__ methods (to be exact, if a dictionary lookup on the values class and parent classes has values for both those keys) then the class attribute is regarded as something called a "data descriptor". This means that the __get__ method on that value is called, passing in the object on which the lookup occurred and the result of that value is returned. If the class attribute isn't found or isn't a data descriptor the value from the instances dictionary is returned.

如果实例字典中没有值,则返回类查找中的值。除非它碰巧是非数据描述符,即它具有__get__方法。然后调用__get__方法并返回结果值。

If there is no value in the instance dictionary, then the value from the class lookup is returned. Unless it happens to be a "non-data descriptor", i.e. it has the __get__ method. Then the __get__ method is invoked and the resulting value returned.

如果 obj 碰巧是一个类,还有一个特殊情况, ( type 类型的实例),如果它是描述符并且相应地调用,则还检查实例值。

There is one more special case, if the obj happens to be a class, (an instance of the type type), then the instance value is also checked if it's a descriptor and invoked accordingly.

如果没有值则是在实例及其类层次结构中找到, obj 的类有一个__getattr__方法,该方法被调用。

If no value is found on the instance nor its class hierarchy, and the obj's class has a __getattr__ method, that method is called.

以下显示用Python编码的算法,有效地执行getattr()函数的功能。 (不包括任何已经插入的错误)​​

The following shows the algorithm as encoded in Python, effectively doing what the getattr() function would do. (excluding any bugs that have slipped in)

NotFound = object() # A singleton to signify not found values

def lookup_attribute(obj, attr):
    class_attr_value = lookup_attr_on_class(obj, attr)

    if is_data_descriptor(class_attr_value):
        return invoke_descriptor(class_attr_value, obj, obj.__class__)

    if attr in obj.__dict__:
        instance_attr_value = obj.__dict__[attr]
        if isinstance(obj, type) and is_descriptor(instance_attr_value):
            return invoke_descriptor(instance_attr_value, None, obj)
        return instance_attr_value

    if class_attr_value is NotFound:
        getattr_method = lookup_attr_on_class(obj, '__getattr__')
        if getattr_method is NotFound:
            raise AttributeError()
        return getattr_method(obj, attr)

    if is_descriptor(class_attr_value):
        return invoke_descriptor(class_attr_value, obj, obj.__class__)

    return class_attr_value

def lookup_attr_on_class(obj, attr):
    for parent_class in obj.__class__.__mro__:
        if attr in parent_class.__dict__:
            return parent_class.__dict__[attr]
    return NotFound

def is_descriptor(obj):
    if lookup_attr_on_class(obj, '__get__') is NotFound:
        return False
    return True

def is_data_descriptor(obj):
    if not is_descriptor(obj) or lookup_attr_on_class(obj, '__set__') is NotFound :
        return False
    return True

def invoke_descriptor(descriptor, obj, cls):
    descriptormethod = lookup_attr_on_class(descriptor, '__get__')
    return descriptormethod(descriptor, obj, cls)

所有这些描述符废话与方法调用有什么关系你问?嗯,问题是,这些函数也是对象,它们碰巧实现了描述符协议。如果属性查找在类上找到一个函数对象,则调用它的__get__方法并返回一个绑定方法对象。绑定方法只是函数对象周围的一个小包装器,它存储查找函数的对象,并且在调用时,将该对象预先添加到参数列表中(通常用于表示 self方法的函数) 参数是)。

What does all this descriptor nonsense have to with method invocation you ask? Well the thing is, that functions are also objects, and they happen to implement the descriptor protocol. If the attribute lookup finds a function object on the class, it's __get__ methods gets called and returns a "bound method" object. A bound method is just a small wrapper around the function object that stores the object that the function was looked up on, and when invoked, prepends that object to the argument list (where usually for functions that are meant to methods the self argument is).

以下是一些说明性代码:

Here's some illustrative code:

class Function(object):
    def __get__(self, obj, cls):
        return BoundMethod(obj, cls, self.func)
    # Init and call added so that it would work as a function
    # decorator if you'd like to experiment with it yourself
    def __init__(self, the_actual_implementation):
        self.func = the_actual_implementation
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

class BoundMethod(object):
    def __init__(self, obj, cls, func):
        self.obj, self.cls, self.func = obj, cls, func
    def __call__(self, *args, **kwargs):
        if self.obj is not None:
             return self.func(self.obj, *args, **kwargs)
        elif isinstance(args[0], self.cls):
             return self.func(*args, **kwargs)
        raise TypeError("Unbound method expects an instance of %s as first arg" % self.cls)

对于方法解析顺序(在Python的情况下实际上意味着属性解析顺序),Python使用Dylan的C3算法。这里解释起来太复杂了,所以如果你有兴趣的话请看这篇文章。除非您正在做一些非常时髦的继承层次结构(并且您不应该这样做),否则只需知道查找顺序是从左到右,深度优先,并且在搜索该类之前搜索类的所有子类。

For method resolution order (which in Python's case actually means attribute resolution order) Python uses the C3 algorithm from Dylan. It is too complicated to explain here, so if you're interested see this article. Unless you are doing some really funky inheritance hierarchies (and you shouldn't), it is enough to know that the lookup order is left to right, depth first, and all subclasses of a class are searched before that class is searched.

这篇关于方法解析和调用如何在Python内部工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆