为啥从类中访问类变量需要“自我”。在 Python 中? [复制]

Posted

技术标签:

【中文标题】为啥从类中访问类变量需要“自我”。在 Python 中? [复制]【英文标题】:Why accessing to class variable from within the class needs "self." in Python? [duplicate]为什么从类中访问类变量需要“自我”。在 Python 中? [复制] 【发布时间】:2012-11-19 01:47:18 【问题描述】:

可能重复:Python ‘self’ explained

我正在学习 Python,我有一个关于从这个类的方法访问类变量的问题,理论多于实践。

例如我们有:

class ExampleClass:
    x = 123
    def example_method(self):
        print(self.x)

为什么一定要准确写self.x,而不仅仅是xx 属于类的命名空间,使用它的方法也属于它。我错过了什么?这种风格背后的理由是什么?

在 C++ 中你可以这样写:

class ExampleClass 
public:
    int x;
    void example_method()
    
        x = 123;
        cout << x;
    ;
;

它会起作用的!

【问题讨论】:

我认为这与范围有关。在模块内部,您可以声明一个“全局”变量并在类声明中调用它,但如果您在类声明中声明某些内容,(显然)它已经被类范围“捕获”,然后需要 self. @senderle 这个问题似乎主要是关于明确传递self(或者至少这是公认的答案所关注的)。这个问题是关于为什么属性没有隐含的self. 附言。将其命名为“self”只是一种惯例和良好做法,它可以是任何东西,比如独角兽。 @delnan,实际上,我认为这两个问题都相当于“为什么有必要准确地写'self.x',而不仅仅是'x'?”但是,如果没有人同意我的观点,那么这不会被关闭:)——我只是想我会提出这是重复的可能性。 (重读另一个问题,我同意这是模棱两可的。) 如果你不接受 Bakuriu 的回答,我会追捕你。对设计决策问题有实际答案是多么令人耳目一新。 【参考方案1】:

来自The History of Python: Adding Support for User-defined Classes:

相反,我决定放弃隐式引用 实例变量。像 C++ 这样的语言可以让你把 this->foo 写成 显式引用实例变量 foo(如果有 单独的局部变量 foo)。因此,我决定做出这样的明确 引用引用实例变量的唯一方法。此外, 我决定不要让当前对象(“t​​his”)成为 特殊关键字,我会简单地使“this”(或其等价物) 方法的第一个命名参数。实例变量总是 被引用为该参数的属性。

使用显式引用,不需要特殊的语法 对于方法定义也不必担心复杂 关于变量查找的语义。相反,一个简单的定义 第一个参数对应于实例的函数,通过 约定被命名为“自我”。例如:

def spam(self,y):
    print self.x, y

这种方法类似于我在 Modula-3 中看到的方法,它具有 已经为我提供了导入和异常处理的语法。 Modula-3 没有类,但它允许您创建记录类型 包含已初始化的全类型函数指针成员 默认为附近定义的函数,并添加语法糖,所以 如果 x 是这样一个记录变量,而 m 是一个函数指针 该记录的成员,初始化为函数 f,然后调用 x.m(args) 等价于调用 f(x, args)。这符合 对象和方法的典型实现,并使其成为可能 将实例变量等同于第一个参数的属性。

因此,BDFL 本人表示,他决定使用显式自我而不是隐式自我的唯一真正原因是:

这是明确的 它更容易实现,因为查找必须在运行时完成(而不是像其他语言那样在编译时完成),并且具有隐式 self 可能会增加查找的复杂性(从而增加成本)。

编辑:Python中也有答案FAQ。

【讨论】:

【参考方案2】:

这似乎与 Python 中的模块与类范围处理有关:

COLOR = 'blue'

class TellColor(object):
    COLOR = 'red'

    def tell(self):
        print self.COLOR   # references class variable
        print COLOR        # references module variable

a = TellColor()
a.tell()

> red
> blue

【讨论】:

有趣的清晰代码。但它没有给出原因。请看我的回答。【参考方案3】:

这是我在ancient answer 中所做的关于此功能的内容:


你遇到的问题是因为这个:

块是作为一个单元执行的一段 Python 程序文本。 以下是块:模块、函数体和类 定义。

(...)

范围定义了名称在一个范围内的可见性 块。

(...)

类块中定义的名称范围仅限于 类块;它没有扩展到方法的代码块—— 这包括生成器表达式,因为它们是使用 功能范围。这意味着以下将失败:

A 类:

   a = 42  

   b = list(a + i for i in range(10))

http://docs.python.org/reference/executionmodel.html#naming-and-binding

上面的意思是: 函数体是代码块,方法是函数,则在类定义中存在的函数体之外定义的名称不会扩展到函数体。


当我读到这篇文章时,我觉得很奇怪,但这就是 Python 的制作方式:

类块中定义的名称范围仅限于类块;它没有扩展到方法的代码块

官方文档是这么说的。

.

编辑

heltonbiker 写了一段有趣的代码:

COLOR = 'blue'

class TellColor(object):
    COLOR = 'red'

    def tell(self):
        print self.COLOR   # references class variable
        print COLOR        # references module variable

a = TellColor()
a.tell()

> red
> blue

这让我想知道写在方法 tell() 中的指令 print COLOR 是如何引发打印在类外部定义的全局对象 COLOR 的值的。 我在官方文档的这一部分找到了答案:

方法可以像普通方法一样引用全局名称 职能。与方法关联的全局范围是模块 包含它的定义。 (类从不用作全局范围。) 虽然人们很少遇到在某个领域使用全局数据的充分理由 方法,全局范围有许多合法用途:对于一个 导入到全局范围内的事物、函数和模块可以是 由方法以及其中定义的函数和类使用。 通常,包含该方法的类本身是在此定义的 全局范围 (...)

http://docs.python.org/2/tutorial/classes.html#method-objects

当解释器必须执行print self.COLOR时,因为COLOR不是实例属性(也就是说标识符'COLOR'不属于实例的命名空间),解释器进入实例类的命名空间中搜索标识符“COLOR”并找到它,因此它打印出 TellColor.COLOR

的值

当解释器必须执行print COLOR时,由于该指令中没有写入属性访问,它将在全局命名空间中搜索标识符'COLOR',官方文档说它是模块的命名空间。

【讨论】:

我见过很多特定领域的模块(例如地理空间),在模块内部全局声明了命名常量(例如,EARTH_RADIUS。如果有人真的想使用它,只需导入模块,例如 from Geostuff import EARTH_RADIUSimport Geostuff; a = Geostuff.EARTH_RADIUS【参考方案4】:

附加到对象(及其类,以及该类的祖先)的属性名称在编译时是不可确定的。所以你要么明确地查找属性,要么:

根除局部变量(在方法中)并始终使用实例变量。这没有什么好处,因为它本质上消除了具有所有优点的局部变量(至少在方法中)。 在运行时决定基础x 是指属性还是本地属性(如果没有self.x,则使用一些额外的规则来决定x = ... 何时添加新属性)。这会降低代码的可读性,因为您永远不知道名称应该是哪一个,并且本质上将 所有方法 中的每个局部变量都变成了公共接口的一部分(因为附加该名称的属性会发生变化方法的行为)。

两者都有一个额外的缺点,即它们需要特殊的方法外壳。现在,“方法”只是一个可以通过类属性访问的常规函数​​。这对于各种用例非常有用。

【讨论】:

可能会提到为什么这仍然适用于具有基本相同情况的全局变量(实际上,有一个关键字可以区分:global @JonasWielicki 哪些变量属于哪个作用域在编译时是可确定的(如果它被分配,它是本地的;如果不是,它在它被分配到的最里面的作用域中,默认为全局;globalnonlocal 覆盖)。 啊,是的,你是对的。我总是忘记从全局范围内可用的变量读取时得到的 UnboundLocalError,但稍后在函数中为其赋值。 @delnan 我不明白 在编译时可确定 是什么意思。由什么决定? @eyquem 由编译器决定。或者真的通过任何程序。或者人类,就此而言。考虑一个执行name = random_identifier(); setattr(something, name, value) 的程序。

以上是关于为啥从类中访问类变量需要“自我”。在 Python 中? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

从类 Ui_MainWindow(object) 的方法访问变量

为啥在匿名类中只能访问最终变量?

从类中的串行端口访问 WinForms 控件

如何在没有自我的情况下访问类范围变量?

从类外部访问实例变量

为啥 kv 文件变量不接受自我?