Python methods: default parameter values are evaluated ONCE

狂风中的少年 提交于 2019-12-04 01:07:27

问题


I've found a strange issue with subclassing and dictionary updates in New-Style Classes:

Python 2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)] on
win32
>>> class a(object):
...     def __init__(self, props={}):
...             self.props = props
...
>>> class b(a):
...     def __init__(self, val = None):
...             super(b, self).__init__()
...             self.props.update({'arg': val})
...
>>> class c(b):
...     def __init__(self, val):
...             super(c, self).__init__(val)
...
>>> b_inst = b(2)
>>> b_inst.props
{'arg': 2}
>>> c_inst = c(3)
>>> c_inst.props
{'arg': 3}
>>> b_inst.props
{'arg': 3}
>>>

In debug, in second call (c(3)) you can see that within a constructor self.props is already equal to {'arg': 2}, and when b constructor is called after that, it becomes {'arg': 3} for both objects!

also, the order of constructors calling is:

  a, b    # for b(2)
  c, a, b # for c(3)

If you'll change self.props.update() with self.props = {'arg': val} in b constructor, everything will be ok, and will act as expected

But I really need to update this property, not to replace


回答1:


props should not have a default value like that. Do this instead:

class a(object):
    def __init__(self, props=None):
        if props is None:
            props = {}
        self.props = props

This is a common python "gotcha".




回答2:


Your problem is in this line:

def __init__(self, props={}):

{} is an mutable type. And in python default argument values are only evaluated once. That means all your instances are sharing the same dictionary object!

To fix this change it to:

class a(object):
    def __init__(self, props=None):
        if is None:
            props = {}
        self.props = props



回答3:


The short version: Do this:

class a(object):
    def __init__(self, props=None):
        self.props = props if props is not None else {}

class b(a):
    def __init__(self, val = None):
        super(b, self).__init__()
        self.props.update({'arg': val})

class c(b):
    def __init__(self, val):
    super(c, self).__init__(val)

The long version:

The function definition is evaluated exactly once, so every time you call it the same default argument is used. For this to work like you expected, the default arguments would have to be evaluated every time a function is called. But instead Python generates a function object once and adds the defaults to the object ( as func_obj.func_defaults )



来源:https://stackoverflow.com/questions/1367883/python-methods-default-parameter-values-are-evaluated-once

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!