我知道“self”在调用方法时作为参数隐式传递给方法。我正在学习描述符并了解函数如何“成为”方法,我了解(至少在概念上)属性“绑定”到对象,我已经在线阅读了多篇文章以及 Raymond Hettinger 的描述符如何做到这一点。
但是,在下面的示例中,我只是不明白对 Function f 实例的引用(即
self
上的参数“__get__
”,将隐式传递参数)来自哪里。
我将其与 f 本身的
self
参数进行对比,我知道当调用 __get__
时,该参数将作为参数传递给 __get__
上的参数 obj。
Python 如何知道,当调用
__get__
时,需要将哪些底层函数传递给 MethodType?我知道这是传递给 self 的参数,但是这是如何/何时发生的?当我了解 __get__
时,我以为我理解了 self 的工作原理,以及函数 Type 上的 __get__
如何返回 Function 或 MethodType,但我感到非常沮丧。
我希望这是有道理的。谢谢你
class Function:
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
if obj is None:
return self
return MethodType(self, obj)
class D:
def f(self, x):
return x
MethodType
并不重要。 ;-)
这里的关键是“描述符”协议、
__get__
和__call__
特殊方法。
Python 确实实现了一个名为“MethodType”的东西,它可以将作为方法调用的“原始”函数、函数将接收的
self
参数、正确的表示形式(通过实现 __repr__
)结合在一起。
它是在调用方法时执行的
__call__
方法 - 在这种情况下,它只是将它所绑定的实例(当对 __get__
的调用创建它时)添加到它接收的参数中,然后传递到功能。用户创建的类可以实现与现有 MethodType
完全相同的角色,而无需创建类(尽管 Python 运行时可能包含一些优化,这些优化将使使用本机函数对象的 get
和它创建的 MethodType
实例更容易)通过创建一些快捷方式来提高效率。
所以,回顾一下:“.”的用法用于检索函数的属性引用将调用它的
__get__
方法(其代码包含在 object.__getattribute__
中 - 重写 __getattribute__
的类可以自定义此行为。
如果在类 (
__get__
) 上调用 MyClass.method
,则它的第二个参数是 None
,对于函数,它仅返回函数本身。 (在旧的 Python2 中,存在另一种对象,即“未绑定方法”。在 Python 3 中,仅返回类主体中定义的普通函数,并且需要返回显式 self
第一个参数)。如果在实例上调用 __get__
,则第二个参数是实例本身 - MyClass
的实例,而不是 Function
或其他描述符的实例。
下面的代码基于您的示例,但我添加了一个额外的类,其角色为
InstanceMethod
类本身:然后您可以添加“打印”语句
如果你不能仅仅通过查看代码和注释来随意更好地理解它的工作原理
class MyMethod:
def __init__(self, instance, function):
self.function = function
self.instance = instance
def __call__(self, *args, **kw):
# this is called by Python when a method in an instance
# is called, and is the part of the code responsible
# for injecting the `self` argument that the method receives.
# note that "self" in here means _this_ instance of MyMethod
# and the actual instance which is to be passed as "self"
# to the method was annotated here by the `__get__` method
# in the function descriptor:
return self.function(self.instance, *args, **kw)
def __repr__(self):
return f"bound method {self.function.__name__} of {self.instance}"
class Function:
def __init__(self, function):
self.function = function
def __get__(self, instance, cls):
# here "instance" refers to the instance of "MyClass"
# and "self" refers to the instance of "Function" itself
if instance is None:
return self
return MyMethod(instance, self.function)
def artificial_method(self):
return self.value
class MyClass:
def __init__(self, value):
self.value = value
test = Function(artificial_method)
m = MyClass(value=42)
m.test() # this expression first calls Function.__get__
# with the parameters "(MyClass.test, m, Myclass)"
# when Python executes the "." operator
# for retrieving the `test` attribute.
# then, when executing the call itself, indicated by the pair of
# parentheses, the `__call__` method in whatever was returned
# by `Function.__get__` is executed.
运行上面的代码后,就像在交互式解释器中一样,m.test() 返回
42