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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Python中,当作为类方法定义中的默认值传递时,赋值运算符是否会访问类或实例变量?相关的知识,希望对你有一定的参考价值。

对于Python 3.7

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

class foo:
    var="value goes here"

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

def __init__(self, var=var):
    self.var=var

Passing class variable to parameter

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

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

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

Storing parameter in Instance Variable

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

self.varname = varname

The problem

方法定义

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

def lookup(self, var=var):
    print(var)

表达式var = var会获取class属性还是实例属性? methodname(self)对自己做了什么,确切地说? self是对实际对象的引用,还是仅仅将解释器的行为从静态方法更改为实例方法?解释器是否自动将“=”符号右侧的上下文作为methodname(object)中输入的任何对象的实例属性?

方法体

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

def lookup(self, var=var):
    var = var
  1. 它会将它存储在类变量,实例变量或具有相同名称的新变量中吗?
  2. 它会获得类变量,实例变量或方法的变量吗?
  3. 我如何明确引用这些变量?

我已经阅读了文档和几个OOP教程,以及最近的一本书,这对我来说仍然不太清楚。

答案

您可以访问实例变量的唯一方法是作为self的属性。

当你只是引用var时,那绝不是一个实例变量;它始终是本地的,封闭的,全局的或内置变量。


在您的方法定义中:

def lookup(self, var=var):
    print(var)

...你有一个名为var的参数。参数是局部变量。身体内的print(var)打印出局部变量。

那这个呢?

def lookup(self, var=var):
    var = var

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


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

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

在函数定义时(执行def语句时),在当前范围中查找var,即class定义的主体 - 并将其值作为默认值存储在函数对象中(它应该是可见的foo.lookup.__defaults__[0])。

所以,默认值是"value goes here"

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


那么,回答你的具体问题:

它会将它存储在类变量,实例变量或具有相同名称的新变量中吗?

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

它会获得类变量,实例变量或方法的变量吗?

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

我如何明确引用这些变量?

与您明确引用其他任何内容的方式相同:

  • var是一个本地封闭的全局或内置变量。
  • self.var是实例属性,如果没有实例属性,则为class属性。
  • type(self).var是一个类属性,即使有一个实例属性。
另一答案

隐式方法参数self是实例。因此,虽然self.var是实例变量,但您需要使用类对象作为前缀来访问类变量。有时,您可能不知道类对象,然后您可以简单地使用__class__属性,即类变量是self.__class__.var

回答你的三个问题1.你在lookup方法中的任务将创建一个名为var的新变量。 2.它会得到你传递给方法的论点(我猜你的意思是“方法的变量”3。下面的代码展示了如何显式访问这些变量(在__init__中说明了它,但它没有'真的很重要你在哪个方法。

class A(object):
    var = 'class_variable'

    def __init__(self, var=var):
        print('Argument var={}'.format(var))
        var = var
        self.var = var
        print('Instance var={}'.format(self.var))
        print('Class var={}'.format(self.__class__.var))
        print('Alternative access to Class var={}'.format(A.var))

这给了

>>> a = A()
Argument var=class_variable
Instance var=class_variable
Class var=class_variable
Alternative access to Class var=class_variable
>>> b = A('v')
Argument var=v
Instance var=v
Class var=class_variable
Alternative access to Class var=class_variable
>>> c = A()
Argument var=class_variable
Instance var=class_variable
Class var=class_variable
Alternative access to Class var=class_variable
另一答案

我会尽力回答你的问题,但如果我可以先解决你的问题:

我想为这个函数的参数使用相同的名称

不要这样做。这样做没有合理的理由,如果它让您感到困惑,现在想象一下,当您在几小时/几天/几周/几个月/几年内查看代码时,您会感觉如何。想象一下,其他人如何看待你的代码会有什么感受。

对于我写的所有内容,想象一下我们有一个名为bob的实例

这将导致bob.var在每次实例化时都是“值在这里”。每个新对象都将var设置为“值在这里”

class foo:
    var="value goes here"

这将导致bob.var成为在实例化期间传递给foo的任何值。如果没有通过,它将默认为“值到这里”

def __init__(self, var=var):
    self.var=var

如果没有传递,这将打印传递的任何内容或“值到此处”

def lookup(self, var=var):
    print(var)

这没有任何作用,因为所讨论的var是局部范围的,并且只有在方法结束时才存在

def lookup(self, var=var):
    var = var

我希望这有帮助,只需重申:为不同的变量使用不同的变量名称。

以上是关于在Python中,当作为类方法定义中的默认值传递时,赋值运算符是否会访问类或实例变量?的主要内容,如果未能解决你的问题,请参考以下文章

绑定方法与非绑定方法

python基础

Python面向对象

java中的变量

Java值传递和引用传递

函数与代码复用