在 Python 中,赋值运算符在作为类方法定义中的默认值传递时是否访问类或实例变量?

Rya*_*yan 3 python scope namespaces python-3.x

对于Python 3.7

我有一个带有类属性(类范围变量)的类:

class foo:
    var="value goes here"
Run Code Online (Sandbox Code Playgroud)

以及在类的init方法中创建的实例变量:

def __init__(self, var=var):
    self.var=var
Run Code Online (Sandbox Code Playgroud)

将类变量传递给参数

类变量与init的参数具有相同的名称,这不会使解释器感到困惑,因为解释器将“=”符号左侧的任何字段视为该方法范围内的新变量。它通过为方法的范围填充一个新的命名空间(变量字典)来实现这一点,以数组的形式实现: 例如parameters[1] = "var"或关联数组:parameters['var'] = pointer_to_value。然后,解释器查看方法体内部,并用通用引用替换“=”符号右侧出现的对“var”的任何引用。事实上,这是一个谎言,但它比它真正的作用更容易理解:

解释器识别匹配的正则表达式.*= *var(,{0,1}| *) *(;{0,1}|\n*),然后将相应的pointer_to_value传递到程序的调用堆栈)。因此,解释器并不关心参数的名称,也不会注意到 var=var 的歧义。可以解决歧义这一事实是语言结构的副作用,而不是有意的设计决策。毕竟,问问自己,在定义方法时,为什么要从定义的方法内部访问变量?为什么要从方法定义中调用对父作用域中的变量的赋值操作?这些都是不合逻辑的行为,并且它们的名称空间可能性是互斥的,因此解释器永远不需要解决歧义。

相反,解释器将“=”符号的右侧视为现有值,并在类的命名空间中搜索变量定义。

在实例变量中存储参数

在方法中,实例变量也与类变量和参数具有相同的名称,这在init方法中起作用,因为实例变量是通过self引用访问的,例如

self.varname = varname
Run Code Online (Sandbox Code Playgroud)

问题

方法定义

我需要从另一个方法的方法定义中访问实例变量,并且我想对此函数的参数使用相同的名称:

def lookup(self, var=var):
    print(var)
Run Code Online (Sandbox Code Playgroud)

表达式将var = var获取类属性还是实例属性?methodname(self)到底与自我有什么关系?是self对实际对象的引用,还是仅将解释器的行为从静态方法更改为实例方法?解释器是否会自动将“=”符号的右侧上下文化为键入的任何对象的实例属性methodname(object)

方法体

在方法体内,如果我分配给var...

def lookup(self, var=var):
    var = var
Run Code Online (Sandbox Code Playgroud)
  1. 它将存储在类变量、实例变量还是同名的新变量中?
  2. 它会获取类变量、实例变量还是方法的变量?
  3. 如何显式引用这些变量?

我已经阅读了文档和一些 OOP 教程以及最近的一本书,但我仍然不清楚。

aba*_*ert 5

访问实例变量的唯一方法是作为self

\n\n

当你只是参考var,那永远不是实例变量;它始终是本地变量、封闭变量、全局变量或内置变量。

\n\n
\n\n

在您的方法定义中:

\n\n
def lookup(self, var=var):\n    print(var)\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x80\xa6 你有一个名为 的参数var。参数是局部变量。这print(var)打印该局部变量。

\n\n

那这个呢?

\n\n
def lookup(self, var=var):\n    var = var\n
Run Code Online (Sandbox Code Playgroud)\n\n

再次,var是一个局部变量\xe2\x80\x94a参数。因此,您只需将该局部变量的当前值分配给同一个变量。这没有任何有用的效果,但当然它是完全合法的。

\n\n
\n\n

参数的值从哪里来?在函数调用时,如果传递参数,则该参数将绑定到形参;如果不这样做,它将用默认值填充。

\n\n

好的,那么默认值从哪里来呢?

\n\n

在函数定义时(def执行语句时),var在当前作用域\xe2\x80\x94中查找,即定义的主体class\xe2\x80\x94,并将其值作为默认值存储在函数对象中(它应该显示为foo.lookup.__defaults__[0])。

\n\n

因此,默认值为"value goes here"

\n\n

请注意,它不是包捕获或对类属性的其他引用。class执行该语句时,它使用相同的class主体命名空间来构建类的属性,因此最终会得到foo.var与 中相同值的另一个名称foo.lookup.__defaults__[0]。但它们是该值完全独立的名称;您可以重新分配foo.var = 3,并且 \'s 参数的默认值lookup仍然是"value goes here"

\n\n
\n\n

因此,回答您的具体问题:

\n\n
\n

它将存储在类变量、实例变量还是同名的新变量中?

\n
\n\n

以上都不是。它将它存储在一个已经存在的局部变量中,因为它是一个参数。

\n\n
\n

它会获取类变量、实例变量还是方法的变量?

\n
\n\n

如果“方法的变量”指的是参数,那么它就是最后一个。

\n\n
\n

如何显式引用这些变量?

\n
\n\n

与显式引用其他任何内容的方式相同:

\n\n
    \n
  • var是一个局部封闭全局或内置变量。
  • \n
  • self.var是实例属性,如果没有实例属性,则为类属性。
  • \n
  • type(self).var是一个类属性,即使有一个实例属性。
  • \n
\n