提问人:readonly 提问时间:9/16/2008 最后编辑:smcireadonly 更新时间:11/7/2021 访问量:107311
为什么需要在 Python 方法中显式使用“self”参数?[复制]
Why do you need explicitly have the "self" argument in a Python method? [duplicate]
问:
在 Python 中定义类的方法时,它看起来像这样:
class MyClass(object):
def __init__(self, x, y):
self.x = x
self.y = y
但在其他一些语言(如 C#)中,您使用“this”关键字引用了方法绑定到的对象,而无需在方法原型中将其声明为参数。
这是 Python 中故意的语言设计决策,还是有一些实现细节需要传递“self”作为参数?
答:
这是为了最小化方法和函数之间的差异。它允许您轻松地在元类中生成方法,或在运行时将方法添加到预先存在的类中。
例如:
>>> 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 运行时的实现更容易。
评论
self
this
我喜欢引用 Peters 的 Zen of Python。“显性总比隐性好。”
在 Java 和 C++ 中,可以推导 '',除非您的变量名称无法推导。所以你有时需要它,有时不需要它。this.
Python 选择明确地表达这样的事情,而不是基于规则。
此外,由于没有任何暗示或假设,因此会公开部分实现。,以及其他“内部”结构以明显的方式可用。self.__class__
self.__dict__
评论
还有另一个非常简单的答案:根据 Python 的禅宗,“显式胜于隐式”。
Python 不会强迫你使用“self”。你可以给它起任何你想要的名字。您只需要记住,方法定义标头中的第一个参数是对对象的引用。
评论
@staticmethod
我建议人们应该阅读 Guido van Rossum 关于这个主题的博客——为什么明确的自我必须留下来。
当一个方法定义被修饰时,我们不知道是否要自动给它一个“self”参数:修饰器可以将函数变成一个静态方法(它没有“self”),或者一个类方法(它有一种有趣的self,指的是一个类而不是一个实例),或者它可以做一些完全不同的事情(在纯Python中编写一个实现“@classmethod”或“@staticmethod”的装饰器是微不足道的)。如果不知道装饰者做了什么,就没有办法是否赋予被定义的方法一个隐含的“自我”参数。
我拒绝像特殊大小写的“@classmethod”和“@staticmethod”这样的黑客。
我认为这与 PEP 227 有关:
无法访问类范围内的名称。名称在 最内层的封闭函数范围。如果类定义出现在 嵌套范围链,解析过程跳过类 定义。此规则可防止类之间的奇怪交互 属性和局部变量访问。如果名称绑定操作 在类定义中发生,它会在生成的 类对象。在方法或函数中访问此变量 嵌套在方法中,必须使用属性引用 通过 self 或通过类名。
还允许您执行此操作:(简而言之,调用将返回 12,但会以最疯狂的方式这样做。Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5)
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()
而现在:将按预期行事。该方法将与参数绑定到 ,并使用 绑定到 MagicMethod 实例。很奇怪吧?这就像有 2 个关键字和 Java 和 C# 等语言。像这样的魔术允许框架做一些本来会更冗长的事情。InnocentClass().magic_method()
innocent_self
InnocentClass
magic_self
this1
this2
再说一次,我不想评论这些东西的道德规范。我只是想展示一些没有明确的自我参照就很难做到的事情。
评论
OuterClass.this
this
Something
Something
this
this
Object self1 = this;
self1
self2
selfn
我认为除了“Python 的禅”之外,真正的原因是函数是 Python 中的一等公民。
这实质上使它们成为对象。现在,最根本的问题是,如果你的函数也是对象,那么在面向对象的范式中,当消息本身是对象时,你将如何向对象发送消息?
看起来像一个鸡蛋问题,为了减少这个悖论,唯一可能的方法是将执行的上下文传递给方法或检测它。但是由于 python 可以有嵌套函数,所以不可能这样做,因为内部函数的执行上下文会发生变化。
这意味着唯一可能的解决方案是显式传递“self”(执行的上下文)。
所以我相信这是一个实现问题,禅宗来得晚得多。
评论
正如 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中的一样。这将消除方法中形式参数列表中显式自我的冗余使用。虽然这个想法看起来很有希望,但它不会发生。至少在不久的将来不会。主要原因是向后兼容性。这是 Python 创建者本人的博客,解释了为什么显式自我必须保留。
评论
“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
评论
self