如何避免在 Python 中显式地使用“自我”?

2025-03-05 09:18:00
admin
原创
67
摘要:问题描述:我一直在通过关注一些pygame 教程学习 Python 。我发现关键字self被广泛使用,由于我主要使用 Java,我发现自己总是忘记输入self。例如,self.rect.centerx我会输入而不是rect.centerx,因为对我来说,rect已经是该类的成员变量。对于这种情况我能想到的 J...

问题描述:

我一直在通过关注一些pygame 教程学习 Python 。

我发现关键字self被广泛使用,由于我主要使用 Java,我发现自己总是忘记输入self。例如,self.rect.centerx我会输入而不是rect.centerx,因为对我来说,rect已经是该类的成员变量。

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

我是否必须将所有成员变量都加上self前缀,或者有没有办法声明它们,使我避免这样做?

即使我所建议的不是Pythonic,我仍然想知道是否可行。

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

  • Python - 为什么在类中使用“self”?

  • 为什么需要在 Python 方法中明确添加“self”参数?


解决方案 1:

用 Java 术语来说:Python 没有成员函数,所有类函数都是静态的,并且在作为成员函数调用时,以对实际类实例的引用作为第一个参数进行调用。

这意味着当您的代码有一个class MyClass并且您构建一个实例时m = MyClass(),调用m.do_something()将被执行为MyClass.do_something(m)

还要注意,从技术上讲,这个第一个参数可以称为任何你想要的名字,但惯例是使用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.>

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

解决方案 2:

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

此外,有人可能想按照实际问题的要求做些事情,这是有正当理由的。我有时会遇到的一件事是冗长的数学方程式,使用长名称会使方程式无法识别。以下是您可以在一个固定示例中执行此操作的几种方法:

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 )

总的来说,这是一种糟糕的做法。

解决方案 3:

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

解决方案 4:

你可以使用任何你想要的名字,例如

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

甚至

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

但您只能使用名称作为范围。

我不建议你使用与 self 不同的东西,除非你有令人信服的理由,因为它会让经验丰富的 Python 专家感到陌生。

解决方案 5:

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

您还会发现,使用 Python 编程的方式与使用 Java 编程的方式非常不同,因此使用self会减少,因为您不会将所有内容都投影到对象内部。相反,您会更多地使用模块级函数,这样可以更好地进行测试。

顺便说一句。一开始我很讨厌它,现在我讨厌相反的东西。缩进驱动的流程控制也一样。

解决方案 6:

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

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

解决方案 7:

“self” 是类的当前对象实例的常规占位符。当您想要引用类中的对象的属性、字段或方法时,就像引用“itself”一样,就会使用它。但为了让它更简短,Python 编程领域的某个人开始使用“self”,其他领域使用“this”,但他们将其作为无法替换的关键字。我宁愿使用“its”来提高代码的可读性。这是 Python 中的优点之一 - 您可以自由选择除“self”之外的对象实例的占位符。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)

现在我们用‘its’替换‘self’:

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)

现在哪个更具可读性?

解决方案 8:

self 是 python 语法的一部分,用于访问对象的成员,所以恐怕你只能用它了

解决方案 9:

实际上,您可以使用 Armin Ronacher 的演讲“5 年的糟糕想法”中的秘诀“内隐自我”(谷歌搜索)。

这是一个非常聪明的配方,就像 Armin Ronacher 的几乎所有东西一样,但我认为这个想法不太有吸引力。我想我更喜欢在 C#/Java 中明确这一点。

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

解决方案 10:

使用 ASCII 范围之外的可区分字符:

class Stuff:
    def __init__(ж, x, y):
        ж.x = x
        ж.y = y

理由:

这并没有回答如何才能完全避免“自我”的问题,但正如其他人已经解释的那样,可能不应该这样做。

但如果你问,如何才能最大限度地减少要阅读的字符数量,同时又能保持良好的可区分性,这可能是一个可能的答案。你可能想要不同的字符选择,但我认为最好不要使用“s”,而是使用一些显而易见的东西:“这是有特殊含义的东西”

当我开始使用 python(来自 c++ 和 c#)时,我尝试了“this”,但很快发现,这是一种非常自私的处理方式,我会很快采用“self”,因为我正在阅读 SO 等网站上的示例代码。我最终发现“this”与“self”相比没有任何优势,并很快停止了它。

我还意识到,在所有语言中,拥有一个绑定并以某种方式强制访问对象成员的概念,但同时不占用超过一个字符的空间(我会将点包括在该计数中),这将是一个很好的做法。

所以,这是我在个人项目中使用的解决方案。怀着小小的希望,希望有一天我们都能意识到我们应该就一个共同的单字符达成一致。理想情况下,python 本身会启用一个单字符快捷方式,其中也包括点。

另一个解决方案比我的更复杂,但不涉及 Python,即使用字体,在特殊字符和点之间绘制连字符,这样会非常容易识别。我想象一个小圆形,右侧有一个边缘 - 指向属性名称。它仍然会占用固定宽度字体上的两个宽度,但可读性就像只有一个符号。

解决方案 11:

是的,self 很繁琐。但是,这样做更好吗?

class Test:

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

    def run(_):
        print _.test

解决方案 12:

在这里,我遵循@argentum2f 的回答中的想法来复制属性。这可以通过装饰器自动完成,并且适用于 Python 3。当然,复制属性意味着它们不能被更改,因此@const_self装饰器得名。

@const_self定义一个方法,其第一个参数具有与要使用的属性相同的名称 - 并且没有self

from cmath import sqrt

def const_self(fun):
    fun_args = fun.__code__.co_varnames[:fun.__code__.co_argcount]

    def fun_with_self(*args, **kwargs):
        self = args[0]
        other_args = list(args[1:])

        used_attributes = [arg for arg in fun_args if hasattr(self, arg)]
        self_args = [getattr(self, attr) for attr in used_attributes]

        return fun(*(self_args + other_args), **kwargs)

    return fun_with_self

class QuadraticEquation:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    @const_self
    def roots(a, b, c, dummy, lazy = False):
        print("Dummy is", dummy)
        if lazy: return # A lazy calculator does not calculate
        return (-b - sqrt(b**2 - 4*a*c)) /2/a, (-b + sqrt(b**2 - 4*a*c)) /2/a

当然,该代码还有很多需要改进的地方:至少如果你像def fun(a, dummy, b, c): print(a,b,c)这里一样定义一个方法,并且它不保留文档字符串,它就会失败。但我认为它已经足够很好地证明了这个想法。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1553  
  IPD(Integrated Product Development)流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。其中,技术评审与决策评审是IPD流程中至关重要的环节,它们既有明显的区别,又存在紧密的协同关系。深入理解这两者的区别与协同,对于企业有效实施IPD流程,提升产品开发效率与质量具有重要意义...
IPD管理流程   27  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、ClickUp、Freshdesk、GanttPRO、Planview、Smartsheet、Asana、Nifty、HubPlanner、Teamwork。在当今快速变化的商业环境中,项目管理软件已成为企业提升效率、优化资源分配和确保项目按时交付的关键工具。然而...
项目管理系统   22  
  建设工程项目质量关乎社会公众的生命财产安全,也影响着企业的声誉和可持续发展。高质量的建设工程不仅能为使用者提供舒适、安全的环境,还能提升城市形象,推动经济的健康发展。在实际的项目操作中,诸多因素会对工程质量产生影响,从规划设计到施工建设,再到后期的验收维护,每一个环节都至关重要。因此,探寻并运用有效的方法来提升建设工程...
工程项目管理制度   19  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用