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

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

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

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

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

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

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

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

I have been learning Python by following some pygame tutorials.

Therein I found extensive use of the keyword self, and coming from a primarily Java background, I find that I keep forgetting to type self. For example, instead of self.rect.centerx I would type rect.centerx, because, to me, rect is already a member variable of the class.

The Java parallel I can think of for this situation is having to prefix all references to member variables with this.

Am I stuck prefixing all member variables with self, or is there a way to declare them that would allow me to avoid having to do so?

Even if what I am suggesting isn’t pythonic, I’d still like to know if it is possible.

I have taken a look at these related SO questions, but they don’t quite answer what I am after:


回答 0

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

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

Python requires specifying self. The result is there’s never any confusion over what’s a member and what’s not, even without the full class definition visible. This leads to useful properties, such as: you can’t add members which accidentally shadow non-members and thereby break code.

One extreme example: you can write a class without any knowledge of what base classes it might have, and always know whether you are accessing a member or not:

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

That’s the complete code! (some_function returns the type used as a base.)

Another, where the methods of a class are dynamically composed:

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

Remember, both of these examples are extreme and you won’t see them every day, nor am I suggesting you should often write code like this, but they do clearly show aspects of self being explicitly required.


回答 1

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

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

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 )

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

Previous answers are all basically variants of “you can’t” or “you shouldn’t”. While I agree with the latter sentiment, the question is technically still unanswered.

Furthermore, there are legitimate reasons why someone might want to do something along the lines of what the actual question is asking. One thing I run into sometimes is lengthy math equations where using long names makes the equation unrecognizable. Here are a couple ways of how you could do this in a canned example:

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

The third example – i.e. using for k in self.__dict__ : exec(k+'= self.'+k) is basically what the question is actually asking for, but let me be clear that I don’t think it is generally a good idea.

For more info, and ways to iterate through class variables, or even functions, see answers and discussion to this question. For a discussion of other ways to dynamically name variables, and why this is usually not a good idea see this blog post.

UPDATE: There appears to be no way to dynamically update or change locals in a function in Python3, so calc3 and similar variants are no longer possible. The only python3 compatible solution I can think of now is to use 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 )

Which, again, would be a terrible practice in general.


回答 2

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

Actually self is not a keyword, it’s just the name conventionally given to the first parameter of instance methods in Python. And that first parameter can’t be skipped, as it’s the only mechanism a method has of knowing which instance of your class it’s being called on.


回答 3

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

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

甚至

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

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

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

You can use whatever name you want, for example

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

or even

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

but you are stuck with using a name for the scope.

I do not recommend you use something different to self unless you have a convincing reason, as it would make it alien for experienced pythonistas.


回答 4

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

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

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

yes, you must always specify self, because explicit is better than implicit, according to python philosophy.

You will also find out that the way you program in python is very different from the way you program in java, hence the use of self tends to decrease because you don’t project everything inside the object. Rather, you make larger use of module-level function, which can be better tested.

by the way. I hated it at first, now I hate the opposite. same for indented-driven flow control.


回答 5

“自身”是类的当前对象实例的常规占位符。当您要引用类中的对象的属性,字段或方法时,就好像在引用“自身”一样使用它。但是,为了使它简短一些,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)

现在哪个更易读?

The “self” is the conventional placeholder of the current object instance of a class. Its used when you want to refer to the object’s property or field or method inside a class as if you’re referring to “itself”. But to make it shorter someone in the Python programming realm started to use “self” , other realms use “this” but they make it as a keyword which cannot be replaced. I rather used “its” to increase the code readability. Its one of the good things in Python – you have a freedom to choose your own placeholder for the object’s instance other than “self”. Example for 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)

Now we replace ‘self’ with ‘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)

which is more readable now?


回答 6

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

self is part of the python syntax to access members of objects, so I’m afraid you’re stuck with it


回答 7

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

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

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

Actually you can use recipe “Implicit self” from Armin Ronacher presentation “5 years of bad ideas” ( google it).

It’s a very clever recipe, as almost everything from Armin Ronacher, but I don’t think this idea is very appealing. I think I’d prefer explicit this in C#/Java.

Update. Link to “bad idea recipe”: https://speakerdeck.com/mitsuhiko/5-years-of-bad-ideas?slide=58


回答 8

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

class Test:

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

    def run(_):
        print _.test

Yeah, self is tedious. But, is it better?

class Test:

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

    def run(_):
        print _.test

回答 9

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

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

From: Self Hell – More stateful functions.

…a hybrid approach works best. All of your class methods that actually do computation should be moved into closures, and extensions to clean up syntax should be kept in classes. Stuff the closures into classes, treating the class much like a namespace. The closures are essentially static functions, and so do not require selfs*, even in the class…


回答 10

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

I think that it would be easier and more readable if there was a statement “member” just as there is “global” so you can tell the interpreter which are the objects members of the class.