如何避免在Python中显式的“自我”?


131

我通过遵循一些pygame教程来学习Python 。

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

Java的并行的我能想到的这种情况是有前缀成员变量的所有引用与

我是否在所有成员变量前面加上self前缀,还是有一种方法可以声明它们,从而避免这样做呢?

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

我看了这些相关的SO问题,但它们并不能完全回答我的要求:


5
我来自Java背景并且很自然,但是我在每个调用中都显式地添加了“ this”,以便更清楚地表明我是在引用实例变量。
Uri

4
您是否熟悉m_某些C ++ / Java程序员观察到的所有成员名称的前缀约定?self.以类似的方式使用帮助提高可读性。另外,您应该阅读dirtsimple.org/2004/12/python-is-not-java.html
贝尼·切尔尼亚夫斯基-帕斯金

2
尽管通常通常m_仅用于非公共非静态数据成员(至少在C ++中)。

@Beni很棒的链接文章,是的,实际上mVariableName,在Java中进行编码时,我确实遵循对成员变量使用的约定。我认为@Anurag的评论很好地总结了它,这是Java开发人员在学习python时应该做的事情。
bguiz 2010年

11
因此,每个人如何告诉OP为什么使用自我是好的/必需的/等等。但是没有人说是否可以避免这种情况?即使被某种肮脏的trick俩?
einpoklum 2015年

Answers:


99

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.>

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


4
谢谢。这个答案打了个折扣,也解释了使用的好处self
bguiz 2010年

2
@Roger Pate:请停止编辑我的问题以从中删除python。我认为它属于那里。(感谢您的回答!)
bguiz 2010年

3
@bguiz:不重复标题中的标签是一种惯例。但是,当我两天前进行编辑时,我没有看到您7个月前还原了标题。

1
如果可以将self简化为单个字符,那将是很好的。
dwjohnston 2014年

5
这实际上是一个听起来很愚蠢的原因。Python可能无法接受阴影,并要求您专门声明它,即用某种“祝福”您的阴影字段__shadow__ myFieldName。那样也可以防止意外阴影,不是吗?
einpoklum 2015年

39

先前的答案基本上都是“您不能”或“您不应”的变体。我同意后一种观点,但从技术上来说,这个问题尚未得到解答。

此外,出于合理的原因,有人可能想要按照实际问题的要求去做某事。我有时遇到的一件事是冗长的数学方程式,其中使用长名称会使方程式无法识别。以下是在固定示例中如何执行此操作的几种方法:

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 )

总体而言,这将是可怕的做法。


4
辉煌!这是最(唯一的)正确答案。+1您还独特地给出了这样做的实际原因。+ 1i
David Lotts

创建一个类并在其中移动代码后:现在不再识别所有方法和变量(没有自我 ...),让我想起了python的另一个原因,我不满意。感谢您的想法。它不能整体解决/ headache /无法读取的问题,但提供了一个适当的解决方法。
–'javadba

为什么不更新locals而不是使用exec
内森

locals().update(self.__dict__)在python 2和3中尝试过,但是没有用。在python3中,即使'exec'技巧也不再是一种选择。另一方面,globals().update(self.__dict__)确实可以工作,但是总体上来说将是一个可怕的做法。
argentum2f

26

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


1
这个答案,尤其是第二句话,比我接受的答案要有用得多,因为我知道“显式自我”只是Python的局限之一,而且是无法避免的
QuestionDriven

21

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

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

甚至

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

但您仍然无法使用范围的名称。

我不建议您使用与自己不同的东西,除非您有令人信服的理由,因为这会使有经验的pythonista陌生。


26
您可以这样做,但是不能!没有任何理由会使您的代码变得奇怪。是的,您可以将其命名为任何东西,但惯例是称呼它self,因此您应该遵循惯例。对于任何有经验的Python程序员来说,这将使您的代码更易于理解。(这包括您,从现在开始六个月,试图弄清楚您的旧程序在做什么!)
steveha,2009年

3
甚至更多的外星人:def function(_, variable): _.variable = variable
鲍勃·斯坦

1
@ BobStein-VisiBone甚至更多的外星人:def funcion(*args): args[0].variable = args[1]
Aemyl

4
@steveha他不推荐这样做,此信息对于像我这样的人非常有用,他们不知道您可以使用除self之外的其他关键字,并且想知道为什么要传递自己类的对象。
Sven van den Boogaart '17

>“更奇怪”。Python 奇怪-特别是它的结构,这种对self的使用是支持反可读性的金丝雀。我知道此后会吸引许多捍卫者,但这不会改变准确性。
javadba

9

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

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

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


2
“您更多地使用模块级功能,可以更好地进行测试”是可疑的,我不得不强烈不同意。的确,您不必强迫所有东西都成为某个类的方法(无论是静态方法还是非静态方法),无论它是否是“逻辑模块级”,但这与自我无关,并且对自身没有任何重大影响测试一种方式或另一种方式(对我而言)。

它来自于我的经验。我并不是每次都遵循它的口头禅,但是如果您将不需要成员变量访问权限的任何内容作为单独的独立方法进行测试,则使事情变得更容易测试。是的,您将逻辑与数据分开,是的,这是针对OOP的,但是它们在一起,只是在模块级别。我不会给任何一个“最佳”商标,这只是一个品味问题。有时我发现自己指定了与类本身无关的类方法,因为它们没有self任何联系。那么让他们上课有什么意义呢?
Stefano Borini,2009年

我不同意这两个部分(似乎我使用“非方法”的频率不比其他语言高),但是“可以更好地测试”确实意味着一种方法优于另一种方法(就是我阅读吗?似乎不太可能),但根据我的经验,我没有找到对此的支持。请注意,我并不是说您应该始终使用一种方法,而只是说方法和非方法同样可以被测试。

5
是的,当然可以,但是方法不同。对象具有状态,而模块级方法则没有。如果在测试类级方法时发现测试失败,则可能有两件事是错误的:1)调用时的对象状态2)方法本身。如果您具有无状态模块级方法,则仅会发生情况2。您已将设置从对象(考虑到测试的地方是黑盒,因为它受对象内部最终复杂的逻辑支配)移动到测试套件。您正在降低复杂性并保持对设置的更严格控制。
Stefano Borini,2009年

1
“如果您有无状态模块级方法”,那么有状态模块级方法呢?您只是告诉我的是,无状态函数比有状态函数更易于测试,我会同意这一点,但这与方法与非方法无关。完全按照以下方式查看self参数:函数的另一个参数。

4

“自身”是类的当前对象实例的常规占位符。当您要引用类中的对象的属性,字段或方法时,就好像在引用“自身”一样使用它。但是,为了使它简短一些,Python编程领域中的某个人开始使用“ self”,其他领域则使用“ this”,但是它们使它成为无法替换的关键字。我宁愿使用“它”来增加代码的可读性。这是Python的优点之一-您可以自由选择对象实例的占位符,而不是“自身”。自我示例:

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)

现在我们用“其”替换“自我”:

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)

现在哪个更易读?


为什么不只是s(或其他单个字母)而不是its
javadba,

“其”具有含义,而“ s”则没有含义。
LEMUEL ADANE

s具有相同的含义:类实例的别名。我必须its在上下文中查找相同的内容
javadba,

两者都不可读。让“自己”作为外部参数仍然不合逻辑,这没有任何意义。
Cesar

3

self是python语法的一部分,用于访问对象的成员,因此恐怕您会受其束缚


2
自我是一种无需真正使用即可告诉访问修饰符的方法。+1
Perpetualcoder

1

实际上,您可以使用Armin Ronacher演讲“ 5年的坏主意”中的食谱“自卑自我”(用Google搜索)。

这是一个非常聪明的秘方,几乎所有阿明·罗纳赫(Armin Ronacher)的著作都如此,但我认为这个主意并不吸引人。我想我更愿意在C#/ Java中对此进行明确说明。

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


这个你是指你指的什么联系呢?如果是这样,请在您的回答中
注明

不,阿明的“坏主意”看起来更符合我的口味。我包括链接。
亚历克斯·于

在单击配方链接之前,请注意,这使它隐含在def method(<del> self </ del> )参数列表中,但self.variable对于这种聪明的技巧仍然是必需的。
大卫·洛茨

0

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

class Test:

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

    def run(_):
        print _.test

10
_在Python外壳中有特殊的含义,它保存最后一个返回值。这样使用是安全的,但可能会造成混淆;我会避免的。
Cairnarvon

不,这不是更好,但是为什么不使用单个字母代替,例如sor或m(模仿C ++)
javadba,

0

来自:自我地狱-更多有状态的功能。

混合方法效果最好 您所有实际进行计算的类方法都应移到闭包中,并且清理语法的扩展应保留在类中。将闭包塞入类,将类像命名空间一样对待。闭包本质上是静态函数,因此甚至在类中也不需要self *。


对于小型用例来说,闭包是很好的选择,但是对它们的扩展使用会大大增加程序的内存开销(因为您使用的是基于原型的OO,而不是基于类的OO,因此每个对象都需要有自己的一组功能而不是在一个类中保留一组通用的功能)。此外,这将使您无法进行魔术/挖洞方法(例如__str__,类似方法),因为这些方法的调用方式与普通方法不同。
沙丘

0

我认为,如果有一个“成员”语句和“全局”语句,那将更容易且更具可读性,因此您可以告诉解释器哪些是类的对象成员。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.