【问题标题】:How to reference a static attribute from within a class in Python?如何从 Python 中的类中引用静态属性?
【发布时间】:2014-08-21 18:05:40
【问题描述】:

我有以下python sn-p:

class myClass:
    myVar = 'a'

    def __init__(self):
        self.myOtherVar = 'b'
        myVar = 'c'  # Gets assigned but only as a local variable.

    print myVar            # prints 'a' !
    print self.myOtherVar  # says 'self' not found

我的问题是这样的; 从myClass 中打印myVar 的内容和/或从init 重新分配它们的正确方法是什么?

【问题讨论】:

  • 对我来说,它为print myVar打印'a'
  • 你确定你的缩进是正确的吗?
  • self 没有什么特别之处;它只是方法的第一个参数的常规名称,它会自动分配给被调用的实例。在方法之外,您可以直接访问类级别的属性。
  • 你描述的不是真的。该代码打印a,然后出现错误,因为self 不存在。但是myVarnot None 并且 not 会引发错误。另外,如果带有myVar 的行引发错误,那么你怎么知道下一行也会引发错误,因为它不会被执行?

标签: python class constructor


【解决方案1】:

您面临的问题是因为您不了解类声明的作用域是如何工作的。类声明在其自己的范围内执行。执行完成后会创建一个新的类对象,并将获得的作用域作为其__dict__附加到该类。

注意:类范围从方法范围内搜索!这意味着当方法定义中时,您必须将类属性引用为MyClass.attribute

例如:

class MyClass:
    var = 1

    # we are executing this code as a single block
    # so you must reference the variable as is usual
    print(var)

    # default values are *not* inside the definition.
    # they are evaluated in the outer scope, so use plain "var" here
    def method(self, a_default=var):
        print(a_default)

    def other_method(self):

        # inside methods you are in a different scope
        print(MyClass.var)

        # equivalent *if* no "var" instance attributes exists
        print(self.var)

注意:由于该类在执行其声明时仍不存在,因此您不能MyClass 声明的“顶层”引用MyClass

class MyClass:
    var = 1
    print(MyClass.var)   # error: MyClass still doesn't exist.

这样的一个副作用,就是下面的代码:

class MyClass:
    x = 1
    results = list(i+x for i in range(10))

生产:

NameError                                 Traceback (most recent call last)
<ipython-input-6-f1d4417b2e52> in <module>()
----> 1 class MyClass:
      2     x = 1
      3     results = list(i+x for i in range(10))
      4 

<ipython-input-6-f1d4417b2e52> in MyClass()
      1 class MyClass:
      2     x = 1
----> 3     results = list(i+x for i in range(10))
      4 

<ipython-input-6-f1d4417b2e52> in <genexpr>(.0)
      1 class MyClass:
      2     x = 1
----> 3     results = list(i+x for i in range(10))
      4 

NameError: name 'x' is not defined

因为生成器表达式(以及 python3 中的列表理解)实际上被视为具有自己作用域的函数。由于未从内部函数范围搜索类范围,因此无法找到 x

您可以使用函数定义和默认值来解决这个问题:

class MyClass:
    x = 1
    def _make_results(x=x):
        return list(i+x for i in range(10))
    results = _make_results()
    del _make_results    # otherwise it would be added as a method.
    # or:
    results = (lambda x=x: list(i+x for i in range(10)))()

这通常不是问题,因为类定义很少包含除方法定义和一些常量之外的任何内容。


关于类范围已经有几个关于 SO 的问题了:

【讨论】:

  • 无论如何都是一个典型的答案。但是我需要最后一点澄清;您说我不能在 MyClass 声明的“顶层”引用 MyClass,但我可以从它的一个方法声明中进行。这只是引出了一个问题:class 究竟什么时候被认为是活着的,以便我可以开始引用它?我迭代 - 不是实例而是类。
  • class 块一结束。您可以在方法中使用它,因为函数的主体仅在您调用它时才被评估。这是在类定义解析和执行之后。
  • @Konos5 一个类在其主体被评估后是“活动的”。方法定义的评估意味着它的代码被编译。所以在方法内部引用MyClass 没有问题,因为由于方法没有被执行,所以只有在类完全创建后才会评估引用。就方法而言,MyClass 只是一个全局变量。事实上,您也可以以self.__class__ 的身份访问该类,而无需引用MyClass。但是,如果您创建一些子类,self.__class__ 可能不是MyClass
【解决方案2】:
class myClass:
    myVar = 'a'

    def __init__(self):
        self.myOtherVar = 'b'

    print myVar  # -> 'a'

    class EmptyClass: pass
    s = EmptyClass()
    __init__(s)

    myVar = s.myOtherVar
    print myVar  # -> 'b'

print myClass.myVar  # -> 'b'

【讨论】:

  • 想解释一下你在这里做了什么?我正在寻找您演示的功能,但以更“干净”的方式。同样在某个时候你写了__init__(s)... init 指的是哪个类?
  • 我们必须调用myClass.__init__ 以获取'b'。所以我创建了一个名为EmptyClass 的空类,然后我使用EmptyClass 的实例通过__init__(s) 调用myClass.__init__,所以__init__ 指的是myClass
【解决方案3】:

self.var 将:

  1. 如果有,请在self.__dict__ 中提供var
  2. self.__class__.__dict__ 中提供var(如果存在)
  3. AttributeError

如果你想访问静态变量minding继承,请使用这个或self.__class__.var。如果扩展myClass,子实例将访问子类中的静态变量。

如果您想访问myClass 中的静态变量,即使是从后代调用,请使用myClass.var

至于重新分配它们,这必须在类对象上显式完成,否则分配将只针对实例。

【讨论】:

  • 没有。在 myClass 我不能做print myClass.var。 Python 吐出一个错误,说 myClass 未定义。
  • @Konos5 你不能在一个类中print,但不能在它的方法中。能否请阅读我提供的链接的副本?它包含有关 Python 中静态变量的所有知识。
  • @BartoszKP 我们说话的时候我正在看它。但是,我似乎仍然找不到从myClass__init__ 方法中重新分配静态变量myVar 的明确方法...
  • @Konos5 与该线程中其他任何地方的语法相同:myClass.myVar。从哪里做都没有关系。它是该类中的一个变量,. 总是意味着到达某物的内部。
猜你喜欢
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多