213

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

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

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

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

4

10 回答 10

95

我喜欢引用 Peters 的 Python Zen。“显式胜于隐式。”

在 Java 和 C++ 中,this.可以推导出 ' ',除非您的变量名称无法进行推导。所以你有时需要它,有时不需要。

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

此外,由于没有任何暗示或假设,因此公开了部分实现。 self.__class__self.__dict__并且其他“内部”结构以明显的方式可用。

于 2008-09-16T00:47:55.133 回答
69

这是为了尽量减少方法和功能之间的差异。它允许您轻松地在元类中生成方法,或在运行时将方法添加到预先存在的类中。

例如

>>> class C:
...     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 运行时的实现更容易。

于 2008-09-16T00:47:18.487 回答
57

我建议人们应该阅读Guido van Rossum关于这个主题的博客 -为什么明确的自我必须留下

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

我拒绝像特殊外壳'@classmethod'和'@staticmethod'这样的黑客攻击。

于 2008-11-21T06:28:26.760 回答
16

Python 不会强迫你使用“self”。你可以给它任何你想要的名字。您只需要记住方法定义标头中的第一个参数是对对象的引用。

于 2008-09-16T01:15:16.377 回答
8

还允许您这样做:(简而言之,调用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 个关键字this1this2Java 和 C# 等语言。像这样的魔法允许框架做一些原本会更冗长的事情。

同样,我不想评论这些东西的道德规范。我只是想展示如果没有明确的自我引用会更难做的事情。

于 2015-07-12T11:18:06.863 回答
4

我认为这与 PEP 227 有关:

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

于 2015-03-31T13:29:09.163 回答
3

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

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

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

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

所以我相信这是一个实现问题,Zen 来得太晚了。

于 2016-05-01T16:01:06.157 回答
1

正如Python 中的 self 所解释的那样,揭秘

像 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 是局部变量。它们不相同并且位于不同的命名空间中。

自我在这里留下

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

于 2019-02-04T05:52:03.703 回答
0

'self' 参数保留当前调用对象。

class class_name:
    class_variable
    def method_name(self,arg):
        self.var=arg 
obj=class_name()
obj.method_name()

这里,self 参数包含对象 obj。因此,语句 self.var 表示 obj.var

于 2020-08-19T07:01:27.260 回答
-5

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

于 2008-09-16T00:49:26.970 回答