Python 将类变量作为默认值分配给类方法参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15189245/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 13:35:10  来源:igfitidea点击:

assigning class variable as default value to class method argument

pythonclassvariable-assignment

提问by tomasz74

I would like to build a method inside a class with default values arguments taken from this class. In general I do filtering on some data. Inside my class I have a method where normally I pass vector of data. Sometimes I don't have the vector and I take simulated data. Every time I do not pass a particular vector I would like to take simulated data by default. I thought it should be an easy construction where inside my method definition I say a=self.vector. But for some reason I have an error NameError: name 'self' is not defined. The simplified construction is:

我想在一个类中构建一个方法,使用从这个类中获取的默认值参数。一般来说,我会过滤一些数据。在我的课程中,我有一个方法,通常我传递数据向量。有时我没有向量,而是使用模拟数据。每次我不传递特定向量时,我都希望默认采用模拟数据。我认为这应该是一个简单的构造,在我的方法定义中我说a=self.vector. 但由于某种原因,我有一个错误NameError: name 'self' is not defined。简化的构造是:

class baseClass(object):  # This class takes an initial data or simulation
    def __init__(self):
        self.x = 1
        self.y = 2

class extendedClass(baseClass): # This class does some filtering
    def __init__(self):
        baseClass.__init__(self)
        self.z = 5
    def doSomething(self, a=self.z):
        self.z = 3
        self.b = a

if __name__ == '__main__':
    a = extendedClass()
    print a.__dict__
    a.doSomething()
    print a.__dict__

An output I expected should be:

我期望的输出应该是:

{'y': 2, 'x': 1, 'z': 5}
{'y': 2, 'x': 1, 'z': 3, 'b': 5}

I tried default assignment as def doSomething(self, a=z):obviously it doesn't work ever. As far as I understand self.zis visible in this scope and should not be a problem to have it as a default value. Don't know why I have this error and how to do it. This is probably an easy question, but I try to figure it out or find the solution with no lack for sometime already. I found similarquestions only for other languages.

我尝试了默认分配,因为def doSomething(self, a=z):显然它永远不起作用。据我所知self.z,在这个范围内是可见的,将它作为默认值应该不是问题。不知道为什么我有这个错误以及如何做。这可能是一个简单的问题,但我已经尝试弄清楚或找到不缺乏的解决方案。我发现类似的问题只适用于其他语言。

采纳答案by Daniel Roseman

Your understanding is wrong. selfis itself a parameter to that function definition, so there's no way it can be in scope at that point. It's only in scope within the function itself.

你的理解是错误的。self本身就是该函数定义的一个参数,所以在那个时候它不可能在范围内。它只在函数本身的范围内。

The answer is simply to default the argument to None, and then check for that inside the method:

答案只是将参数默认为None,然后在方法内部检查它:

def doSomething(self, a=None):
    if a is None:
        a = self.z
    self.z = 3
    self.b = a

回答by uselpa

Default arguments get evaluated only once, when the definition is executed. Instead, do this:

执行定义时,默认参数仅评估一次。相反,请执行以下操作:

def doSomething(self, a=None):
    if a is None:
        a = self.z
    self.z = 3
    self.b = a

See also http://docs.python.org/release/3.3.0/tutorial/controlflow.html#more-on-defining-functions.

另请参阅http://docs.python.org/release/3.3.0/tutorial/controlflow.html#more-on-defining-functions

回答by Eryk Sun

Here's the disassembly of the code for a simple example module. A code object is a read-only container for bytecode, the constants and names it uses, and metadata about the number of local variables, required stack size, etc. Notice that all of the code objects are compiled as constants. These are created at compilation time. But the objects class Aand function testare instantiated at execution time (e.g. when the module is imported).

这是一个简单示例模块的代码的反汇编。代码对象是字节码、它使用的常量和名称以及有关局部变量数量、所需堆栈大小等的元数据的只读容器。请注意,所有代码对象都被编译为常量。这些是在编译时创建的。但是对象class Afunction test是在执行时实例化的(例如,当导入模块时)。

To make the class, BUILD_CLASStakes the name 'A', the bases tuple(object,), and a dictthat contains the attributes of the class namespace. This is like manually instantiating a type by calling type(name, bases, dict). To make the dict, a function is created from code object Aand called. Finally, the class object is stored in the module namespace via STORE_NAME.

要创建类,BUILD_CLASS需要使用 name 'A'、 basestuple(object,)dict包含类命名空间属性的a 。这就像通过调用手动实例化一个类型type(name, bases, dict)。为了使dict,从代码对象创建一个函数A并调用。最后,类对象通过STORE_NAME.

In code object A, self.zis loaded on the stack as the argument to MAKE_FUNCTION. The bytecode op LOAD_NAMEwill search for selfin the current locals (i.e. the class namespace being defined), the module globals, and builtins. This will obviously fail if selfisn't defined in the global or builtins scope; it clearly isn't defined in the local scope.

在代码对象中Aself.z作为参数加载到堆栈上MAKE_FUNCTION。字节码操作LOAD_NAMEself在当前局部变量(即定义的类命名空间)、模块全局变量和内置变量中搜索。如果self没有在全局或内置作用域中定义,这显然会失败;它显然没有在本地范围内定义。

If it did succeed, however, the function would be created with (self.z,)as its __defaults__attribute, and then stored to the local name test.

但是,如果它确实成功,则将创建该函数(self.z,)作为其__defaults__属性,然后将其存储到本地 name test

>>> code = compile('''
... class A(object):
...   def test(self, a=self.z): pass
... ''', '<input>', 'exec')

>>> dis.dis(code)
  2           0 LOAD_CONST               0 ('A')
              3 LOAD_NAME                0 (object)
              6 BUILD_TUPLE              1
              9 LOAD_CONST               1 (<code object A ...>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_NAME               1 (A)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE

>>> dis.dis(code.co_consts[1]) # code object A
  2           0 LOAD_NAME                0 (__name__)
              3 STORE_NAME               1 (__module__)

  3           6 LOAD_NAME                2 (self)
              9 LOAD_ATTR                3 (z)
             12 LOAD_CONST               0 (<code object test ...>)
             15 MAKE_FUNCTION            1
             18 STORE_NAME               4 (test)
             21 LOAD_LOCALS         
             22 RETURN_VALUE       


@uselpa:Your pastebin example (rewritten for 2.x):

@uselpa:您的 pastebin 示例(为 2.x 重写):

>>> code = compile('''
... default = 1
... class Cl(object):
...     def __init__(self, a=default):
...         print a
... Cl()
... default = 2
... Cl()
... ''', '<input>', 'exec')
>>> dis.dis(code)
  2           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (default)

  3           6 LOAD_CONST               1 ('Cl')
              9 LOAD_NAME                1 (object)
             12 BUILD_TUPLE              1
             15 LOAD_CONST               2 (<code object Cl ...>)
             18 MAKE_FUNCTION            0
             21 CALL_FUNCTION            0
             24 BUILD_CLASS         
             25 STORE_NAME               2 (Cl)

  6          28 LOAD_NAME                2 (Cl)
             31 CALL_FUNCTION            0
             34 POP_TOP             

  7          35 LOAD_CONST               3 (2)
             38 STORE_NAME               0 (default)

  8          41 LOAD_NAME                2 (Cl)
             44 CALL_FUNCTION            0
             47 POP_TOP             
             48 LOAD_CONST               4 (None)
             51 RETURN_VALUE        

As you can see, class object Cl(and function object __init__) is only instantiated and stored to the local name 'Cl'once. The module executes sequentially at run time, so subsequently rebinding the name defaultwill have no effect on the default value in __init__.

如您所见,类对象Cl(和函数对象__init__)仅被实例化并存储到本地名称'Cl'一次。该模块在运行时按顺序执行,因此随后重新绑定名称default将不会影响 中的默认值__init__

You could dynamically instantiate a new function using the previously compiled code and a new default value:

您可以使用先前编译的代码和新的默认值动态实例化一个新函数:

>>> default = 1
>>> class Cl(object):
...     def __init__(self, a=default):
...         print a
... 

>>> from types import FunctionType
>>> default = 2
>>> Cl.__init__ = FunctionType(
...   Cl.__init__.__code__, globals(), '__init__', (default,), None)
>>> c = Cl()
2

This reuses the already compiled code object from __init__.__code__to create a function with a new __defaults__tuple:

这将重用已编译的代码对象__init__.__code__来创建一个带有__defaults__新元组的函数:

>>> Cl.__init__.__defaults__
(2,)

回答by Alex

This will insert self.zif ais None/False/empty_value:

这将插入self.z如果aNone/ False/ empty_value

def doSomething(self, a=None):
        self.z = 3
        self.b = (a or self.z)