Python - self参数从哪里来?

3
我理解在调用方法时,“self”会隐式地作为参数传递。我正在学习描述符,并且了解函数如何“变成”方法,我至少在概念上理解属性是如何与对象“绑定”的,我已经阅读了多篇在线文章和Raymond Hettinger的描述符教程。
然而,在下面的示例中,我不明白对于函数f的实例的引用(即在__get__上的参数“self”,将隐式传递的参数)是从哪里来的。
我将这与f本身上的self参数进行对比,我理解当调用__get__时,它将作为参数传递给参数obj。
当调用__get__时,Python如何知道需要传递给MethodType的底层函数是什么?我知道这是传递给self的参数,但是这是如何/何时发生的?当我学习__get__时,我以为我理解了self的工作原理,以及函数类型上的__get__可以返回一个函数或者一个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
1个回答

1

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,它本身的作用是:您可以随意添加"print"语句以更好地理解其工作原理,如果仅通过查看代码和注释无法理解。


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接