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

时间:2018-09-06 18:41:50

标签: python python-3.x scope namespaces

对于Python 3.7

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

class foo:
    var="value goes here"

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

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

将类变量传递给参数

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

  

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

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

在实例变量中存储参数

在该方法中,实例变量也具有与类变量和参数相同的名称,并且在 init 方法中有效,因为实例变量是通过{{1}访问的}参考,例如

self

问题

方法定义

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

self.varname = varname

表达式def lookup(self, var=var): print(var) 是否将获得class属性或instance属性? var = var对自我有什么作用? methodname(self)是对实际对象的引用,还是仅将解释器的行为从静态方法更改为实例方法?解释器是否自动将“ =”符号的右侧上下文作为输入到self中的任何对象的实例属性?

方法主体

在方法主体内,如果我对methodname(object)进行了分配...

var
  1. 它将存储在类变量,实例变量或具有相同名称的新变量中吗?
  2. 它将获取类变量,实例变量还是方法的变量?
  3. 如何明确引用这些变量?

我已经阅读了文档和一些OOP教程以及一本新书,但这对我来说仍然不清楚。

3 个答案:

答案 0 :(得分:3)

访问实例变量的唯一方法是将其作为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作为{{1 }}。但是,它们是该值的完全独立的名称。您可以重新分配foo.lookup.__defaults__[0],并且foo.var = 3参数的默认值仍为lookup


因此,要回答您的特定问题:

  

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

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

  

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

如果用“方法的变量”来表示参数,则它是最后一个。

  

如何明确引用这些变量?

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

  • "value goes here"是一个局部包含全局或内置变量。
  • var是实例属性,如果没有实例属性,则是类属性。
  • self.var是类属性,即使有实例属性也是如此。

答案 1 :(得分:1)

隐式方法参数self instance 。因此,尽管self.var是实例变量,但您需要在class对象前添加前缀以访问该类变量。有时,您可能不知道类对象,然后只需使用__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

答案 2 :(得分:1)

我将尽力回答您的问题,但如果我可以先将您的问题隔离开,

  

我要对该函数的参数使用相同的名称

不要这样做。这样做没有逻辑上的原因,如果让您感到困惑,现在您可以想象一下,在几个小时/天/周/月/年/年的时间内查看代码时的感受。想象一下其他人在看您代码的感觉。

对于我写的所有内容,假设我们有一个名为struct MyData { short Data1; short Data2; short Data3; }; 的实例

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

var

这将导致class foo: var="value goes here" 成为实例化期间传递给foo的任何值。如果未传递任何内容,则默认为“价值在这里”

bob.var

这将打印任何传递的内容,或者如果不传递任何内容,则显示“值在此处”

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

这无济于事,因为所讨论的def lookup(self, var=var): print(var) 是局部作用域的,并且仅存在于方法结束之前

var

我希望这会有所帮助,只是重申一下:为不同的变量使用不同的变量名