为什么需要将”self”参数显式地放入Python方法中?

为什么需要将”self”参数显式地放入Python方法中?

Why do you need explicitly have the “self” argument into a Python method?

在Python中对类定义方法时,它看起来如下所示:

1
2
3
4
class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

但是在一些其他语言中,例如C,您有一个对对象的引用,该对象与"this"关键字绑定,而不在方法原型中将其声明为参数。

在Python中,这是一个有意的语言设计决策,还是存在一些需要将"self"作为参数传递的实现细节?


我喜欢引用彼得斯的《Python禅》。显式优于隐式。"

在爪哇和C++中,EDCOX1〔0〕是可以推断出来的,除非你有不可推论的变量名。所以你有时需要它,有时不需要。

python选择将类似这样的事情显式化,而不是基于规则。

此外,由于没有任何暗示或假设,因此实现的部分内容是公开的。self.__class__self.__dict__和其他"内部"结构都有明显的可用性。


它是为了最小化方法和函数之间的差异。它允许您轻松地在元类中生成方法,或者在运行时将方法添加到预先存在的类中。

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> class C(object):
...     def foo(self):
...         print"Hi!"
...
>>>
>>> def bar(self):
...     print"Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

它还(据我所知)使Python运行时的实现更加容易。


我建议你应该读一下吉多·范·罗森关于这个话题的博客——为什么要保持明确的自我。

When a method definition is decorated, we don't know whether to automatically give it a 'self' parameter or not: the decorator could turn the function into a static method (which has no 'self'), or a class method (which has a funny kind of self that refers to a class instead of an instance), or it could do something completely different (it's trivial to write a decorator that implements '@classmethod' or '@staticmethod' in pure Python). There's no way without knowing what the decorator does whether to endow the method being defined with an implicit 'self' argument or not.

I reject hacks like special-casing '@classmethod' and '@staticmethod'.


python不会强迫你使用"self"。你想叫什么名字都行。您只需记住,方法定义头中的第一个参数是对对象的引用。


也可以这样做:(简而言之,调用Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5)将返回12,但会以最疯狂的方式进行。

1
2
3
4
5
6
7
8
9
10
class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

当然,在Java和C语言这样的语言中,这是难以想象的。通过使自引用显式化,您可以通过自引用自由地引用任何对象。同样,在运行时使用类的这种方式在更静态的语言中也很难做到——这不一定是好的或坏的。只是外在的自我允许所有这些疯狂的存在。

此外,想象一下:我们想要定制方法的行为(用于分析,或者一些疯狂的黑色魔法)。这可以让我们思考:如果我们有一个类Method,我们可以覆盖或控制它的行为,会怎么样?

好吧,这是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from functools import partial

class MagicMethod(object):
   """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

现在:InnocentClass().magic_method()将像预期的那样行动。方法将与innocent_self参数绑定到InnocentClass,并与magic_self绑定到magicMethod实例。奇怪吗?就像在Java和C语言中有2个关键字EDOCX1,9和EDCOX1,10。这样的魔力使得框架可以做一些原本更冗长的事情。

再说一次,我不想评论这些东西的伦理道德。我只是想展示一些如果没有明确的自我参照会很难做到的事情。


我认为除了"Python的禅"之外,真正的原因是函数是Python中的头等公民。

它本质上使它们成为一个物体。现在的基本问题是,如果您的函数也是对象的,那么在面向对象的范式中,当消息本身是对象时,您将如何向对象发送消息?

看起来像是一个鸡蛋问题,为了减少这个悖论,唯一可能的方法是要么将执行上下文传递给方法,要么检测它。但是,由于Python可以有嵌套函数,因此不可能这样做,因为内部函数的执行上下文会发生变化。

这意味着唯一可能的解决方案是显式传递"self"(执行上下文)。

所以我认为这是一个实现问题,禅宗来得晚了很多。


我认为这与政治公众人物227有关:

Names in class scope are not accessible. Names are resolved in the
innermost enclosing function scope. If a class definition occurs in a
chain of nested scopes, the resolution process skips class
definitions. This rule prevents odd interactions between class
attributes and local variable access. If a name binding operation
occurs in a class definition, it creates an attribute on the resulting
class object. To access this variable in a method, or in a function
nested within a method, an attribute reference must be used, either
via self or via the class name.


正如在Python中自我解释的,去神秘化

anything like obj.meth(args) becomes Class.meth(obj, args). The calling process is automatic while the receiving process is not (its explicit). This is the reason the first parameter of a function in class must be the object itself.

1
2
3
4
5
6
7
8
class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
       """Find distance from origin"""
        return (self.x**2 + self.y**2) ** 0.5

Invocations:

1
2
3
>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init()定义了三个参数,但我们刚刚传递了两个(6和8)。同样,Distance()需要一个参数,但传递的参数为零。

为什么python不抱怨这个参数号不匹配?

Generally, when we call a method with some arguments, the corresponding class function is called by placing the method's object before the first argument. So, anything like obj.meth(args) becomes Class.meth(obj, args). The calling process is automatic while the receiving process is not (its explicit).
This is the reason the first parameter of a function in class must be the object itself. Writing this parameter as self is merely a convention. It is not a keyword and has no special meaning in Python. We could use other names (like this) but I strongly suggest you not to. Using names other than self is frowned upon by most developers and degrades the readability of the code ("Readability counts").
...
In, the first example self.x is an instance attribute whereas x is a local variable. They are not the same and lie in different namespaces.

Self Is Here To Stay

Many have proposed to make self a keyword in Python, like this in C++ and Java. This would eliminate the redundant use of explicit self from the formal parameter list in methods. While this idea seems promising, it's not going to happen. At least not in the near future. The main reason is backward compatibility. Here is a blog from the creator of Python himself explaining why the explicit self has to stay.


还有一个非常简单的答案:根据python的禅宗,"显胜于隐"。


推荐阅读