Java – how does method parsing and invocation work inside Python?

How do method calls work in Python?

Indeed, python method parsing in Python may be slower than Java What is late binding?

What are the differences between the reflective mechanisms of the two languages? Where can I find good resources to explain these aspects?

Solution

Method calls in Python consist of two separate steps First, attribute search is done, then the result of the search is called. This means that the following two fragments have the same semantics:

foo.bar()

method = foo.bar
method()

Attribute searching in Python is a rather complex process Suppose we look for an attribute named attr on the object obj, which means the following expression in Python code: obj attr

Search "attr" in the instance Dictionary of the first obj, and then search the instance Dictionary of obj class and its parent class in the parsing order of "attr"

Typically, if a value is found on an instance, it is returned But if the search for a class results in a__ get__ And__ set__ Method (specifically, if the dictionary of the value class and parent class looks up the value with these two keys), the class attribute is regarded as something called a "data descriptor" This means that the value is called__ get__ Method to pass in the object where the lookup occurs and return the result of the value If the class attribute is not found or is not a data descriptor, the value in the instance dictionary is returned

If there is no value in the instance dictionary, the value in the class lookup is returned Unless it happens to be a "non data descriptor", that is, it has__ get__ method. And then call it. get__ Method and returns the result value

Another special case is that if obj happens to be a class (instance of type), if it is a descriptor and calls it accordingly, the instance value is also checked

If no value is found in the instance and its class hierarchy, and obj's class has__ getattr__ Method, the method is called

The following shows the algorithm encoded in Python, which effectively performs the function of getattr() function (excluding any missing errors)

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.__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,cls):
    descriptormethod = lookup_attr_on_class(descriptor,'__get__')
    return descriptormethod(descriptor,cls)

What's the use of your method calling all these descriptors? Well, the problem is, these functions are also objects, and they happen to implement the descriptor protocol If the property lookup finds a function object on the class, its__ get__ Method and returns a "binding method" object Binding method is just a small wrapper around the function object. It stores the object to find the function, and adds the object to the parameter list in advance when calling (usually used to represent the function of the method with self parameter)

Here are some illustrative Codes:

class Function(object):
    def __get__(self,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,func):
        self.obj,self.cls,self.func = obj,func
    def __call__(self,**kwargs):
        if self.obj is not None:
             return self.func(self.obj,**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)

For method parsing order (in Python's case, it actually means attribute parsing order), python uses C3 algorithm from Dylan The explanation here is too complicated, so if you are interested, please see this article Unless you are doing some very fashionable inheritance hierarchy (you shouldn't), it's enough to know that the search order is from left to right, depth first, and search all subclasses of the class before searching the class

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>