方法调用如何在Python中工作?我的意思是,python虚拟机如何解释它.
确实,Python中的python方法解析速度可能比Java慢.什么是后期绑定?
这两种语言的反思机制有何不同?哪里可以找到解释这些方面的好资源?
Python中的方法调用包含两个不同的可分离步骤.首先进行属性查找,然后调用该查找的结果.这意味着以下两个片段具有相同的语义:
foo.bar()
method = foo.bar
method()
Run Code Online (Sandbox Code Playgroud)
Python中的属性查找是一个相当复杂的过程.假设我们在对象obj上查找名为attr的属性,这意味着Python代码中的以下表达式:obj.attr
在第一个obj的实例字典中搜索"attr",然后以"attr"的方法解析顺序搜索obj类的实例字典及其父类的字典.
通常,如果在实例上找到值,则返回该值.但是如果对类的查找导致一个具有__get__和__set__方法的值(确切地说,如果值类和父类的字典查找具有这两个键的值)那么类属性被视为某些东西称为"数据描述符".这意味着调用该值的__get__方法,传入发生查找的对象并返回该值的结果.如果未找到class属性或不是数据描述符,则返回实例字典中的值.
如果实例字典中没有值,则返回类查找中的值.除非它碰巧是"非数据描述符",即它具有__get__方法.然后调用__get__方法并返回结果值.
还有一个特殊情况,如果obj恰好是一个类(类型类型的实例),那么如果它是描述符并且相应地调用它,则还检查实例值.
如果在实例及其类层次结构中找不到值,并且obj的类具有__getattr__方法,则调用该方法.
下面显示了用Python编码的算法,有效地执行了getattr()函数的功能.(不包括任何漏掉的错误)
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)
Run Code Online (Sandbox Code Playgroud)
你问的方法调用所有这些描述符废话有什么用?嗯,问题是,这些函数也是对象,它们碰巧实现了描述符协议.如果属性查找在类上找到一个函数对象,则调用它的__get__方法并返回一个"绑定方法"对象.绑定方法只是函数对象周围的一个小包装器,它存储查找函数的对象,并且在调用时,将该对象预先添加到参数列表中(通常用于表示self参数的方法的函数) .
这是一些说明性的代码:
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)
Run Code Online (Sandbox Code Playgroud)
对于方法解析顺序(在Python的情况下实际上意味着属性解析顺序),Python使用来自Dylan的C3算法.这里解释起来太复杂了,所以如果你有兴趣看这篇文章.除非你正在做一些非常时髦的继承层次结构(你不应该这样做),否则知道查找顺序是从左到右,深度优先,并且在搜索该类之前搜索类的所有子类就足够了.
名称(方法、函数、变量)都是通过查看名称空间来解析的。命名空间在 CPython 中实现为dicts(哈希映射)。
当在实例命名空间 ( ) 中找不到名称时dict,Python 将按照方法解析顺序 (MRO) 查找该类,然后查找基类。
所有解析都是在运行时进行的。
您可以使用该dis模块来查看字节码中是如何发生的。
简单的例子:
import dis
a = 1
class X(object):
def method1(self):
return 15
def test_namespace(b=None):
x = X()
x.method1()
print a
print b
dis.dis(test_namespace)
Run Code Online (Sandbox Code Playgroud)
打印:
9 0 LOAD_GLOBAL 0 (X)
3 CALL_FUNCTION 0
6 STORE_FAST 1 (x)
10 9 LOAD_FAST 1 (x)
12 LOAD_ATTR 1 (method1)
15 CALL_FUNCTION 0
18 POP_TOP
11 19 LOAD_GLOBAL 2 (a)
22 PRINT_ITEM
23 PRINT_NEWLINE
12 24 LOAD_FAST 0 (b)
27 PRINT_ITEM
28 PRINT_NEWLINE
29 LOAD_CONST 0 (None)
32 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
所有的LOAD都是命名空间查找。