如何在Python中避免明确的"自我"?

bgu*_*uiz 112 python self

我通过一些pygame教程学习Python .

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

对于这种情况,我可以想到的Java并行是必须使用前缀对成员变量的所有引用作为前缀.

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

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

我已经看过这些相关的SO问题,但他们并没有完全回答我的想法:

小智 90

Python需要指定self. 结果是,即使没有可见的完整类定义,也不会对成员和不成员的内容产生任何混淆.这会产生有用的属性,例如:您无法添加意外影响非成员的成员,从而破坏代码.

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

class A(some_function()):
  def f(self):
    self.member = 42
    self.method()
Run Code Online (Sandbox Code Playgroud)

这是完整的代码!(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.>
Run Code Online (Sandbox Code Playgroud)

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

  • 谢谢.这个答案可以解决这个问题,因为它也解释了使用"自我"的好处. (4认同)
  • 这实际上是一个相当愚蠢的理由.Python可能无法接受阴影,并要求您专门声明它,即用某种`__shadow__ myFieldName`'祝福'你的阴影场.这样可以防止意外阴影,不是吗? (4认同)
  • @Roger Pate:请停止编辑我的问题以从中删除 python。我认为它属于那里。(并感谢您的回答!) (3认同)
  • @bguiz:不重复标题中的标签就是惯例.但是,当我2天前编辑时,我没有看到你在7个月前恢复了这个标题. (3认同)
  • @dwjohnston 好吧,我参加聚会晚了好几年,但我刚刚开始学习 Python(我的背景是 C++),我讨厌输入“self”。但是我确实喜欢明确区分本地人和实例数据。语法扩展是否会生成 '.' =='自我。' 允许以下代码是可行的: (2认同)

Mic*_*zyk 25

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

  • 这个答案,尤其是第二句话,对我来说比公认的答案有用得多,因为我知道“显式自我”只是Python的限制之一,并且是不可避免的 (3认同)

arg*_*m2f 22

以前的答案基本上都是"你不能"或"你不应该"的变体.虽然我同意后一种观点,但问题在技术上仍然没有答案.

此外,有正当理由可以解释为什么某人可能想要按照实际问题的要求做某事.有时我遇到的一件事是冗长的数学方程,其中使用长名称使方程变得无法识别.以下是一些如何在固定示例中执行此操作的方法:

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))
Run Code Online (Sandbox Code Playgroud)

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

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

  • @WestCoastProjects 我完全同意。我相信鲍勃叔叔在《干净的代码》中概述的原则。并且有“自我”。我的代码中的任何地方都没有使其变得更具可读性或更简单。 (4认同)
  • 辉煌!这是最(唯一?)正确答案.+1你也唯一给出了这样做的实际理由.+ 1I (2认同)
  • 创建一个类并将代码移动到其中后:现在所有的方法和变量都不再被识别(没有_self._ ..),我想起了 python 和我相处不好的另一个原因。感谢您提供这个想法。它并没有解决总体问题/头痛/不可读性,但提供了一个适度的解决方法。 (2认同)

Est*_*ber 21

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

class test(object):
    def function(this, variable):
        this.variable = variable
Run Code Online (Sandbox Code Playgroud)

甚至

class test(object):
    def function(s, variable):
        s.variable = variable
Run Code Online (Sandbox Code Playgroud)

但你仍然坚持使用范围的名称.

除非你有一个令人信服的理由,否则我不建议你使用与自我不同的东西,因为它会使它对于有经验的pythonistas而言更加陌生.

  • 你可以做到这一点,但*不要*!没有理由让你的代码更加怪异.是的,你可以称之为任何东西,但惯例是称它为"自我",你应该遵循惯例.对于任何看过它的有经验的Python程序员来说,它将使您的代码更容易理解.(这包括你,六个月后,试图找出你的旧程序做了什么!) (24认同)
  • @steveha他不推荐它,这些信息对于像我这样的人来说非常有帮助,他们不知道你可以使用不同于自己的关键词,并想知道为什么自己的类的对象被传递. (4认同)
  • 甚至更陌生:`def function(_,variable):_.variable = variable` (3认同)
  • &gt;“更奇怪”。Python _is_ 很奇怪 - 尤其是它的 _classes_ 结构,而 _self_ 的这种使用是支持反可读性的金丝雀。我知道此后会有很多防守者前来,但这并不会改变真实性。 (2认同)

Ste*_*ini 9

是的,你必须总是指定self,因为根据python哲学,explicit比hidden更好.

您还会发现在python中编程的方式与您在java中编程的方式有很大不同,因此使用self往往会减少,因为您不会在对象内部投影所有内容.相反,您可以更多地使用模块级功能,这可以更好地进行测试.

顺便说说.我起初讨厌它,现在我讨厌相反的事情.对于缩进驱动的流量控制也是如此.

  • 是的,当然可以,但方法不同.一个对象有一个状态,一个模块级别的方法没有.如果在测试类级方法时发现测试失败,则可能出现两个错误:1)调用时的对象状态2)方法本身.如果您有无状态模块级方法,则只能发生案例2.您将设置从对象(其中涉及测试的黑盒子,因为它由对象内部最终复杂的逻辑控制)移动到了测试套件.您正在降低复杂性并保持对设置的更严格控制. (5认同)
  • “你更多地使用模块级函数,可以更好地测试”是可疑的,我不得不强烈反对。确实,无论是否“逻辑模块级”,您都不会被迫将所有方法都设为某个类的方法(静态或非静态),但这与 self 无关并且对测试一种或另一种方式(对我来说)。 (2认同)

Cha*_* Ma 5

self是访问对象成员的python语法的一部分,所以我担心你会被它困住

  • self是一种在不使用访问修饰符的情况下告诉访问修饰符的方法.+1 (2认同)

LEM*_*ANE 5

"self"是类的当前对象实例的传统占位符.当你想在类中引用对象的属性或字段或方法时使用它,就好像你指的是"它自己"一样.但是为了缩短Python编程领域的某些人开始使用"自我",其他领域使用"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)
Run Code Online (Sandbox Code Playgroud)

现在我们将'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)
Run Code Online (Sandbox Code Playgroud)

现在哪个更具可读性?