在Python中定义类的方法时,它看起来像这样:
class MyClass(object):
def __init__(self, x, y):
self.x = x
self.y = y
但在其他一些语言中,例如C#,您可以使用“this”关键字引用该方法绑定的对象,而不将其声明为方法原型中的参数。
这是Python中的故意语言设计决策还是有一些实现细节需要传递“自我”作为参数?
答案 0 :(得分:88)
我想引用彼得斯的Python之禅。 “明确比隐含更好。”
在Java和C ++中,可以推导出“this.
”,除非您有可变名称使其无法推断。所以你有时需要它,有时却不需要它。
Python选择将此类内容显式化,而不是基于规则。
此外,由于没有暗示或假设任何内容,因此公开了部分实现。 self.__class__
,self.__dict__
和其他“内部”结构以明显的方式可用。
答案 1 :(得分:57)
最小化方法和功能之间的差异。它允许您轻松地在元类中生成方法,或者在运行时将方法添加到预先存在的类中。
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!
>>>
它(据我所知)也使python运行时的实现更容易。
答案 2 :(得分:51)
我建议您在此主题上阅读Guido van Rossum's blog - Why explicit self has to stay。
当一个方法定义被装饰时,我们不知道是否自动给它一个'self'参数:装饰器可以将函数转换为静态方法(没有'self')或类方法(有一种有趣的自我引用类而不是实例),或者它可以做一些完全不同的事情(在纯Python中编写实现'@classmethod'或'@staticmethod'的装饰器是微不足道的)。如果不知道装饰器是否赋予了使用隐式“自我”参数定义的方法的作用,就没有办法。
我拒绝像特殊套管'@classmethod'和'@staticmethod'这样的黑客。
答案 3 :(得分:16)
Python不会强迫您使用“self”。你可以给它任何你想要的名字。您只需记住方法定义标头中的第一个参数是对该对象的引用。
答案 4 :(得分: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
当然,在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_self
绑定到MagicMethod实例。怪啊?这就像在Java和C#等语言中有2个关键字this1
和this2
。像这样的魔术允许框架做一些本来会更冗长的东西。
同样,我不想评论这些东西的道德规范。我只是想展示一些没有明确的自我引用会更难做的事情。
答案 5 :(得分:2)
我认为这与PEP 227有关:
无法访问类范围中的名称。名称在解决 最内在的封闭功能范围。如果类定义出现在 嵌套范围链,解析过程跳过类 定义。此规则可防止类之间的奇怪交互 属性和局部变量访问。如果是名称绑定操作 发生在类定义中,它会在结果上创建一个属性 类对象。在方法或函数中访问此变量 如果嵌套在方法中,则必须使用属性引用 通过自己或通过班级名称。
答案 6 :(得分:2)
我认为除了“Python的禅”之外的真正原因是函数是Python中的一等公民。
这基本上使它们成为一个对象。现在基本的问题是,如果你的函数也是对象,那么在面向对象的范例中,当消息本身是对象时,你如何向对象发送消息?
看起来像鸡蛋问题,为了减少这个悖论,唯一可行的方法是将执行上下文传递给方法或检测它。但是因为python可以有嵌套函数,所以不可能这样做,因为执行的上下文会因内部函数而改变。
这意味着唯一可行的解决方案是明确传递'self'(执行的上下文)。
所以我相信这是一个实施问题,禅宗来得更晚。
答案 7 :(得分:0)
类似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的创建者本人写的博客,解释了为何必须保留显式自我。
答案 8 :(得分: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
答案 9 :(得分:-4)
还有另一个非常简单的答案:根据zen of python,“明确比隐含更好”。