147

我一直在按照一些pygame 教程学习 Python 。

其中我发现了关键字self的广泛使用,并且主要来自 Java 背景,我发现我一直忘记输入self。例如,self.rect.centerx我不会键入rect.centerx,因为对我来说,rect已经是该类的成员变量。

对于这种情况,我能想到的 Java 并行方法是必须在所有对成员变量的引用前加上this

我是否坚持使用self为所有成员变量添加前缀,或者有没有办法声明它们可以让我避免这样做?

即使我的建议不是pythonic,我仍然想知道它是否可能。

我已经查看了这些相关的 SO 问题,但它们并没有完全回答我所追求的:

4

10 回答 10

107

Python 需要指定 self. 结果是,即使没有完整的类定义可见,也不会混淆什么是成员,什么不是。这导致了有用的属性,例如:您不能添加意外影响非成员并因此破坏代码的成员。

一个极端的例子:你可以在不知道它可能有什么基类的情况下编写一个类,并且总是知道你是否正在访问一个成员:

class A(some_function()):
  def f(self):
    self.member = 42
    self.method()

这就是完整的代码!(some_function 返回用作基础的类型。)

另一个,类的方法是动态组合的:

class B(object):
  pass

print B()
# <__main__.B object at 0xb7e4082c>

def B_init(self):
  self.answer = 42
def B_str(self):
  return "<The answer is %s.>" % self.answer
# notice these functions require no knowledge of the actual class
# how hard are they to read and realize that "members" are used?

B.__init__ = B_init
B.__str__ = B_str

print B()
# <The answer is 42.>

请记住,这两个示例都是极端的,您不会每天都看到它们,我也不建议您应该经常编写这样的代码,但它们确实清楚地表明了 self 的各个方面被明确要求。

于 2009-12-31T05:57:29.593 回答
55

以前的答案基本上都是“你不能”或“你不应该”的变体。虽然我同意后一种观点,但从技术上讲,这个问题仍然没有答案。

此外,有人可能想要按照实际问题的要求做某事是有正当理由的。我有时遇到的一件事是冗长的数学方程式,其中使用长名称会使方程式无法识别。以下是在罐装示例中如何做到这一点的几种方法:

import numpy as np
class MyFunkyGaussian() :
    def __init__(self, A, x0, w, s, y0) :
        self.A = float(A)
        self.x0 = x0
        self.w = w
        self.y0 = y0
        self.s = s

    # The correct way, but subjectively less readable to some (like me) 
    def calc1(self, x) :
        return (self.A/(self.w*np.sqrt(np.pi))/(1+self.s*self.w**2/2)
                * np.exp( -(x-self.x0)**2/self.w**2)
                * (1+self.s*(x-self.x0)**2) + self.y0 )

    # The correct way if you really don't want to use 'self' in the calculations
    def calc2(self, x) :
        # Explicity copy variables
        A, x0, w, y0, s = self.A, self.x0, self.w, self.y0, self.s
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

    # Probably a bad idea...
    def calc3(self, x) :
        # Automatically copy every class vairable
        for k in self.__dict__ : exec(k+'= self.'+k)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

g = MyFunkyGaussian(2.0, 1.5, 3.0, 5.0, 0.0)
print(g.calc1(0.5))
print(g.calc2(0.5))
print(g.calc3(0.5))

第三个例子 - 即使用for k in self.__dict__ : exec(k+'= self.'+k)基本上是问题的实际要求,但让我明确一点,我认为这通常不是一个好主意。

有关更多信息以及遍历类变量甚至函数的方法,请参阅此问题的答案和讨论。有关动态命名变量的其他方法的讨论,以及为什么这通常不是一个好主意,请参阅这篇博文

更新:似乎没有办法在 Python3 中动态更新或更改函数中的局部变量,因此不再可能使用 calc3 和类似的变体。我现在能想到的唯一兼容 python3 的解决方案是使用globals

def calc4(self, x) :
        # Automatically copy every class variable in globals
        globals().update(self.__dict__)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

总的来说,这将是一种可怕的做法。

于 2018-03-15T18:55:21.063 回答
33

实际上self不是关键字,它只是 Python 中实例方法的第一个参数的常规名称。并且第一个参数不能被跳过,因为它是一个方法知道你的类的哪个实例被调用的唯一机制。

于 2009-12-31T05:56:37.977 回答
21

您可以使用任何您想要的名称,例如

class test(object):
    def function(this, variable):
        this.variable = variable

甚至

class test(object):
    def function(s, variable):
        s.variable = variable

但是您坚持使用范围名称。

除非您有令人信服的理由,否则我不建议您使用与 self 不同的东西,因为它会使经验丰富的 pythonistas 变得陌生。

于 2009-12-31T06:18:24.050 回答
9

是的,您必须始终指定self,因为根据 python 哲学,显式优于隐式。

您还会发现,您在 python 中编程的方式与您在 java 中的编程方式非常不同,因此使用self趋于减少,因为您没有将所有内容都投影到对象中。相反,您更多地使用模块级功能,可以更好地测试。

顺便一提。起初我讨厌它,现在我讨厌相反的东西。缩进驱动的流控制也是如此。

于 2009-12-31T05:54:33.333 回答
4

“self”是类的当前对象实例的常规占位符。当您想要在类中引用对象的属性或字段或方法时使用它,就好像您在引用“自身”一样。但是为了让它更短一些 Python 编程领域的人开始使用 "self" ,其他领域使用 "this" 但他们将其作为无法替换的关键字。我宁愿使用“它”来增加代码的可读性。它是 Python 中的一件好事——你可以自由地为对象的实例选择自己的占位符,而不是“self”。自我示例:

class UserAccount():    
    def __init__(self, user_type, username, password):
        self.user_type = user_type
        self.username = username            
        self.password = encrypt(password)        

    def get_password(self):
        return decrypt(self.password)

    def set_password(self, password):
        self.password = encrypt(password)

现在我们将 'self' 替换为 'its':

class UserAccount():    
    def __init__(its, user_type, username, password):
        its.user_type = user_type
        its.username = username            
        its.password = encrypt(password)        

    def get_password(its):
        return decrypt(its.password)

    def set_password(its, password):
        its.password = encrypt(password)

现在哪个更具可读性?

于 2012-12-18T19:47:05.780 回答
3

self 是访问对象成员的 python 语法的一部分,所以恐怕你会坚持下去

于 2009-12-31T05:53:27.343 回答
3

来自:Self Hell - 更多有状态的函数。

...混合方法效果最好。所有实际进行计算的类方法都应该移动到闭包中,并且清理语法的扩展应该保留在类中。将闭包塞入类中,将类视为命名空间。闭包本质上是静态函数,因此不需要 selfs*,即使在类中......

于 2015-11-12T11:22:16.287 回答
1

实际上,您可以使用 Armin Ronacher 演示文稿“5 年的坏主意”(谷歌搜索)中的“隐式自我”食谱。

这是一个非常聪明的食谱,几乎所有东西都来自 Armin Ronacher,但我不认为这个想法很吸引人。我想我更喜欢在 C#/Java 中明确这一点。

更新。链接到“坏主意食谱”:https ://speakerdeck.com/mitsuhiko/5-years-of-bad-ideas?slide=58

于 2015-11-29T22:16:39.187 回答
0

是的,自我很乏味。但是,这样更好吗?

class Test:

    def __init__(_):
        _.test = 'test'

    def run(_):
        print _.test
于 2013-04-25T02:48:21.573 回答