为什么需要在Python方法中显式包含“ self”参数?

问题:为什么需要在Python方法中显式包含“ self”参数?

在Python中的类上定义方法时,它看起来像这样:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

但是在某些其他语言(例如C#)中,您可以使用“ this”关键字来引用该方法所绑定的对象,而无需在方法原型中将其声明为参数。

这是Python中的一种故意的语言设计决策,还是有一些实现细节需要传递“ self”作为参数?

When defining a method on a class in Python, it looks something like this:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

But in some other languages, such as C#, you have a reference to the object that the method is bound to with the “this” keyword without declaring it as an argument in the method prototype.

Was this an intentional language design decision in Python or are there some implementation details that require the passing of “self” as an argument?


回答 0

我喜欢引用Peters的Python Zen。“显式比隐式好。”

在Java和C ++中,this.可以推断出’ ‘,除非您拥有无法推断的变量名。因此,您有时需要它,有时则不需要。

Python选择使这种事情变得明确,而不是基于规则。

另外,由于没有暗示或假设,因此公开了部分实现。 self.__class__self.__dict__以及其他“内部”结构也很明显。

I like to quote Peters’ Zen of Python. “Explicit is better than implicit.”

In Java and C++, ‘this.‘ can be deduced, except when you have variable names that make it impossible to deduce. So you sometimes need it and sometimes don’t.

Python elects to make things like this explicit rather than based on a rule.

Additionally, since nothing is implied or assumed, parts of the implementation are exposed. self.__class__, self.__dict__ and other “internal” structures are available in an obvious way.


回答 1

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

例如

>>> 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运行时的实现更加容易。

It’s to minimize the difference between methods and functions. It allows you to easily generate methods in metaclasses, or add methods at runtime to pre-existing classes.

e.g.

>>> 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!
>>>

It also (as far as I know) makes the implementation of the python runtime easier.


回答 2

我建议人们应该阅读Guido van Rossum关于此主题的博客为什么必须保留显性自我

当修饰一个方法定义时,我们不知道是否要自动给它一个“自我”参数:修饰器可以将函数变成静态方法(没有“自我”)或类方法(其中有一个有趣的自我,它引用一个类而不是一个实例),或者可以做一些完全不同的事情(编写在纯Python中实现“ @classmethod”或“ @staticmethod”的装饰器是微不足道的)。没有办法不知道装饰器的作用,是否赋予被定义的方法一个隐式的“自我”参数。

我拒绝诸如特殊外壳“ @classmethod”和“ @staticmethod”之类的hack。

I suggest that one should read Guido van Rossum’s blog on this topic – Why explicit self has to stay.

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’.


回答 3

Python不会强迫您使用“自我”。您可以根据需要命名。您只需要记住,方法定义标头中的第一个参数是对该对象的引用。

Python doesn’t force you on using “self”. You can give it whatever name you want. You just have to remember that the first argument in a method definition header is a reference to the object.


回答 4

还允许您执行此操作:(简而言之,调用Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5)将返回12,但将以最疯狂的方式返回。

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可以覆盖或控制其行为的类怎么办?

好吧,这是:

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_selfMagicMethod实例的绑定。奇怪吗?就像有2个关键字this1以及this2Java和C#这样的语言一样。像这样的魔术使框架能够执行原本会更加冗长的工作。

同样,我不想评论这种东西的道德。我只是想展示在没有明确的自我参考的情况下很难做的事情。

Also allows you to do this: (in short, invoking Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5) will return 12, but will do so in the craziest of ways.

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

Of course, this is harder to imagine in languages like Java and C#. By making the self reference explicit, you’re free to refer to any object by that self reference. Also, such a way of playing with classes at runtime is harder to do in the more static languages – not that’s it’s necessarily good or bad. It’s just that the explicit self allows all this craziness to exist.

Moreover, imagine this: We’d like to customize the behavior of methods (for profiling, or some crazy black magic). This can lead us to think: what if we had a class Method whose behavior we could override or control?

Well here it is:

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()

And now: InnocentClass().magic_method() will act like expected. The method will be bound with the innocent_self parameter to InnocentClass, and with the magic_self to the MagicMethod instance. Weird huh? It’s like having 2 keywords this1 and this2 in languages like Java and C#. Magic like this allows frameworks to do stuff that would otherwise be much more verbose.

Again, I don’t want to comment on the ethics of this stuff. I just wanted to show things that would be harder to do without an explicit self reference.


回答 5

我认为,除了“ Python之禅”之外,真正的原因还在于,函数是Python中的一等公民。

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

看起来像一个鸡蛋问题,为了减少这种矛盾,唯一可能的方法是将执行上下文传递给方法或对其进行检测。但是由于python可以具有嵌套函数,因此将不可能做到这一点,因为内部函数的执行上下文将发生变化。

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

因此,我认为Zen来得晚了,这是一个实现问题。

I think the real reason besides “The Zen of Python” is that Functions are first class citizens in Python.

Which essentially makes them an Object. Now The fundamental issue is if your functions are object as well then, in Object oriented paradigm how would you send messages to Objects when the messages themselves are objects ?

Looks like a chicken egg problem, to reduce this paradox, the only possible way is to either pass a context of execution to methods or detect it. But since python can have nested functions it would be impossible to do so as the context of execution would change for inner functions.

This means the only possible solution is to explicitly pass ‘self’ (The context of execution).

So i believe it is a implementation problem the Zen came much later.


回答 6

我认为这与PEP 227有关:

类范围内的名称不可访问。名称在最里面的函数范围内解析。如果类定义出现在嵌套作用域链中,则解析过程将跳过类定义。此规则可防止类属性和局部变量访问之间发生奇怪的交互。如果在类定义中发生了名称绑定操作,它将在结果类对象上创建一个属性。要在方法或方法中嵌套的函数中访问此变量,必须通过self或通过类名使用属性引用。

I think it has to do with PEP 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.


回答 7

Python中的self所述,Demystified

像obj.meth(args)之类的东西都变成Class.meth(obj,args)。调用过程是自动的,而接收过程不是(它的显式)。这就是类中函数的第一个参数必须是对象本身的原因。

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

调用:

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

init()定义了三个参数,但我们只传递了两个(6和8)。同样,distance()要求传递一个但零个参数。

为什么Python不抱怨此参数编号不匹配

通常,当我们调用带有某些参数的方法时,通过将方法的对象放在第一个参数之前来调用相应的类函数。因此,像obj.meth(args)之类的东西都会变成Class.meth(obj,args)。调用过程是自动的,而接收过程不是(它的显式)。

这就是类中函数的第一个参数必须是对象本身的原因。将此参数写为self只是一种约定。它不是关键字,在Python中没有特殊含义。我们可以使用其他名称(例如这样),但我强烈建议您不要使用。对于大多数开发人员来说,使用除self之外的其他名称并不受欢迎,这会降低代码的可读性(“可读性计数”)。

在第一个示例中,self.x是实例属性,而x是局部变量。它们不相同,并且位于不同的命名空间中。

自我在这里停留

许多人建议将self用作Python的关键字,例如C ++和Java。这将消除方法中形式参数列表中显式自我的多余使用。尽管这个想法看起来很有希望,但它不会发生。至少在不久的将来不会。主要原因是向后兼容。这是Python的创建者本人写的博客,解释了为何必须保留显式自我。

As explained in self in Python, Demystified

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.

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:

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

init() defines three parameters but we just passed two (6 and 8). Similarly distance() requires one but zero arguments were passed.

Why is Python not complaining about this argument number mismatch?

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.


回答 8

还有一个非常简单的答案:根据python的禅定,“显式优于隐式”。

There is also another very simple answer: according to the zen of python, “explicit is better than implicit”.