如何在Python中避免显式的'self'?

2024-03-29 06:28:09 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直在学习Python,学习一些pygame tutorials

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

在这种情况下,我可以想到的Java并行程序必须在所有对成员变量的引用前面加上this前缀。

我是否一直用self作为所有成员变量的前缀,或者是否有一种方法可以声明它们,从而避免这样做?

即使我的建议不是Python,我还是想知道是否可能。

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


Tags: 方法rectself声明键入情况成员关键字
3条回答

Python需要指定self。结果是,即使没有可见的完整类定义,也不会对什么是成员和什么不是成员产生任何混淆。这会导致一些有用的属性,例如:不能添加意外隐藏非成员从而破坏代码的成员。

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

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

这是完整的代码!(有些函数返回用作基的类型。)

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

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不是关键字,它只是Python中实例方法的第一个参数的名称。第一个参数不能被跳过,因为它是一个方法唯一的机制,可以知道调用它的类的哪个实例。

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

此外,有正当的理由可以解释为什么有些人会想按照实际问题的思路去做一些事情。我有时遇到的一件事是冗长的数学公式,其中使用长名称会使公式无法识别。在一个封闭的示例中,有两种方法可以做到这一点:

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)基本上就是问题真正想要的,但是让我明确一点,我认为这不是一个好主意。

有关更多信息以及遍历类变量甚至函数的方法,请参见this question的解答和讨论。有关动态命名变量的其他方法的讨论,以及为什么这通常不是一个好主意,请参见this blog post

相关问题 更多 >