为什么需要在Python方法中明确地使用"self"参数?

Rea*_*nly 187 python oop methods self

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

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
Run Code Online (Sandbox Code Playgroud)

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

这是一个有意的语言设计决策在Python或是否有一些实现细节需要传递"自我"作为参数?

S.L*_*ott 89

我想引用Peters的Python禅."明确比隐含更好."

在Java和C++中,this.可以推导出' ',除非你有可变名称使其无法推断.所以你有时需要它,有时却不需要它.

Python选择将这样的内容显式化而不是基于规则.

此外,由于没有暗示或假设任何内容,因此公开了部分实现. self.__class__,self.__dict__和其他"内部"结构,一个明显的方式可供选择.

  • 虽然忘记它时会有一个不那么神秘的错误信息会很好. (53认同)
  • "明确比隐含更好"检测到无意义 (12认同)
  • "显式优于隐式" - Python的"风格"不是围绕着隐含的东西构建的吗?例如隐式数据类型,隐式函数边界(无{}'),隐式变量范围......如果模块中的全局变量在函数中可用...为什么不应该将相同的逻辑/推理应用于类?简化规则不会仅仅是"在较高级别宣布的任何内容都可以在较低级别获得",这是由缩进决定的吗? (10认同)
  • 让我们面对它,它只是坏事.这没有任何借口.这只是一个丑陋的遗物,但没关系. (7认同)
  • 但是,当你调用一个方法时,你不必传递对象变量,它是否违反了显性规则?如果要保留这个禅,它必须是:object.method(object,param1,param2).看起来不一致...... (6认同)
  • @ElmoVanKielmo我不这么认为,你传递两个参数来实现函数`object.method(param1,param2)`,但得到三个参数`def method(self,param1,param2)`.所以第一个参数隐式地,隐式地!=明确地传递. (5认同)
  • "在Java和C++中,`这个.可以推断出来"这是非常不精确的措辞.编译器没有推断任何东西.当它看到一个变量名时,它会从最里面的范围开始进行名称查找,然后进入全局或类范围. (5认同)
  • @Vedmant哦,来吧.这个概念非常简单易懂.我甚至不知道为什么我会进入这个纯粹的学术讨论.已经成功开发了大量的Python代码并且它可以工作但是现在有人发现方法的第一个参数将保持对调用该方法的对象的引用并不自然.还有一个原因让"自我"出现在那里.在Python中,你不能使用当前作用域中不可用的任何符号(除了赋值),所以`self`必须完全在它的位置. (4认同)
  • 你可以拼写任何东西(我的,这个,无论如何),对于某些类型的类方法,它会改变意义.不容易理解你的意图并给出"更好"的信息. (2认同)
  • @Vedmant`object.method()`足够明确. (2认同)
  • 请允许我回答我自己的评论... https://docs.python.org/3/tutorial/classes.html 很好地解释了这一点:在类中高层声明的变量可用于该类的所有实例,所以对我来说,这就是为什么我们需要“自我”(或者你决定如何称呼它)来区分。我仍然认为它可以以更简单的方式实现:为什么不只在每个类中固定并提供一个变量“__self__”(两边都有两个下划线......我无法将其显示在此处)以拯救人们把它放在每个函数中? (2认同)

Rya*_*yan 58

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

例如

>>> 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!
>>>
Run Code Online (Sandbox Code Playgroud)

它(据我所知)也使python运行时的实现更容易.

  • +1是为了最小化方法和功能之间的差异.这应该是接受的答案 (10认同)
  • 我真的没买.即使在您需要父类的情况下,您仍然可以在执行时推断它.实例方法和传递实例的类函数之间的等价是愚蠢的; 如果没有它们,Ruby就可以了. (9认同)
  • 这也表明,在 Python 中,当您执行 c.bar() 时,它首先检查实例的属性,然后检查 __class__ 属性。因此,您可以随时将数据或函数(对象)“附加”到类,并期望在其实例中进行访问(即 dir(instance) 将如何访问)。不仅仅是当您“创建” c 实例时。它非常有活力。 (2认同)
  • JavaScript允许您在运行时向对象添加方法,并且在函数声明中不需要`self`(请注意,也许这是从玻璃屋扔石头,因为JavaScript有一些非常棘手的`这个'绑定语义) (2认同)

bha*_*dra 52

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

当一个方法定义被装饰时,我们不知道是否自动给它一个'self'参数:装饰器可以将函数转换为静态方法(没有'self'),或者类方法(有一种有趣的自我,指的是一个类而不是一个实例),或者它可以做一些完全不同的事情(编写一个在纯Python中实现'@classmethod'或'@staticmethod'的装饰器是微不足道的).如果不知道装饰器是否赋予使用隐式"自"参数定义的方法的作用,就没有办法.

我拒绝像特殊套管'@classmethod'和'@staticmethod'这样的黑客攻击.


Vic*_*dji 16

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

  • 它强制将 self 作为每个方法中的第一个参数,只是对我来说没有多大意义的额外文本。其他语言可以很好地处理这个问题。 (2认同)

vla*_*ean 6

也允许你这样做:(简而言之,调用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
Run Code Online (Sandbox Code Playgroud)

当然,在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()
Run Code Online (Sandbox Code Playgroud)

而现在:InnocentClass().magic_method()将表现得像预期的那样.该方法将与innocent_self参数绑定InnocentClass,并与magic_selfMagicMethod实例绑定.怪啊?这就像有两个关键字this1this2Java和C#等语言.像这样的魔法允许框架做一些本来会更冗长的东西.

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

  • 当我考虑你的第一个例子时,我可以用Java做同样的事情:内部类需要调用`OuterClass.this`来从外部类中获取'self',但你仍然可以使用`this`作为对它自己的引用; 非常类似于你在Python中所做的.对我而言,这并不难想象.也许这取决于一个人对所用语言的熟练程度? (4认同)

dao*_*ole 5

我认为这与 PEP 227 有关:

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