迭代分配对象属性的可变类型默认值

时间:2013-11-24 20:19:16

标签: python python-3.x

我的目标是为多个对象属性设置可变类型(列表)的默认值。根据讨论here,我在None的参数中将这些属性的默认值设置为__init__,然后在[]中为它们指定值__init__如果他们采用了他们的默认值。以下是对此的表示:

class Wat(object):
    def __init__(self, arg1=None, arg2=None, arg3=None):
        for arg in arg1, arg2, arg3:
            if arg == None:
                arg = []
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3

    def give(self, thing):
        self.arg1.append(thing)
        self.arg2.append(thing)
        self.arg3.append(thing)

wat = Wat()
wat.give(5)

但是,这会引发以下异常:

AttributeError: 'NoneType' object has no attribute 'append'

我不知道wat.arg1wat.arg2wat.arg3在调用None时仍然可以有wat.give(5)类型 - 他们应该已被重新分配到[]中的__init__。它必须与我对编码进行编码的方式有关,因为这有效:

class Wat(object):
    def __init__(self, arg1=None, arg2=None, arg3=None):
        if arg1 == None:
            arg1 = []
        if arg2 == None:
            arg2 = []
        if arg3 == None:
            arg3 = []
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3

    def give(self, thing):
        self.arg1.append(thing)
        self.arg2.append(thing)
        self.arg3.append(thing)

wat = Wat()
wat.give(5)

在我看来,这两个语法在功能上是等价的,但你可以想象后一种语法如何变得非常繁琐和美学上令人不悦,因为以这种方式分配的参数数量会增加。

以前的语法有什么问题?我该如何解决?感谢。

编辑:你们当中有几个人已经发现了这个问题,但我仍然没有一个令人满意的解决方案。 @roippi提出了一种有效的方法,一次分配一个变量,但假设我有100个这些变量。如何在最少的代码行中完成此任务?这是循环的最初目的。谢谢大家。

5 个答案:

答案 0 :(得分:2)

for arg in arg1, arg2, arg3:
   if arg == None:
       arg = []

在此循环中,您将创建对arg1arg2arg3指向的对象的新引用,并在检查它们对None的值后指定这些对象变量(arg)到新对象,因此旧对象仍然不变。所以,它大致相当于:

>>> lis = [1, 2, 3]
>>> x = lis[0]  #now `x` points to lis[0]
>>> x = []      #now `x` points to a different object, this won't affect lis[0]
>>> lis
[1, 2, 3]
>>> x = lis[1]
>>> x = []
>>> lis
[1, 2, 3]
...

请注意,最好使用arg is None而不是arg == None

答案 1 :(得分:1)

这是因为你将空列表分配给循环变量,循环变量在每次迭代时都会发生变化,即argarg1不是同一个变量,但它们在开始时共享相同的引用。迭代。

答案 2 :(得分:1)

如果所有变量都具有相同的行为,那么您可以使用setattr在循环中设置属性,并使用locals()来访问您的值。我使用inspect获取所有变量名称,您可以对它们进行硬编码,或者进行评估,或者使用任何合理的方法:

import inspect

class Wat(object):
    def __init__(self, arg1=None, arg2=None, arg3=None):
        arg_spec, _v, _k, _d = inspect.getargspec(self.__init__)
        for k in arg_spec[1:]:
            value = locals()[k]
            value = [] if value is None else value
            setattr(self, k, value)

    def give(self, thing):
        self.arg1.append(thing)
        self.arg2.append(thing)
        self.arg3.append(thing)

>>> wat = Wat(arg2=[1,2])
>>> wat.give(5)
>>> wat2 = Wat()
>>> wat2.give(6)
>>> wat.__dict__
{'arg1': [5], 'arg2': [1, 2, 5], 'arg3': [5]}
>>> wat2.__dict__
{'arg1': [6], 'arg2': [6], 'arg3': [6]}

答案 3 :(得分:1)

  编辑:你们中的一些人已经发现了这个问题,但我仍然没有   有一个令人满意的解决方案@roippi提出了一种有效的方法   一次分配一个变量,但假设我有100个变量   变量。如何在最少的代码行中完成此任务?   这是循环的最初目的。谢谢大家。

每当有人问这个问题时,他们就不可避免地使用了错误的数据结构。

例如,您可以使用**kwargs方法捕获传递给Wat对象的所有关键字参数。你可以从那里做你想做的事情,将kwargs视为正常dict

class Wat(object):
    def __init__(self, **kwargs):
        self.arg1 = kwargs.get('arg1', [])
        self.arg2 = kwargs.get('arg2', [])
        self.arg3 = kwargs.get('arg3', [])

class Wat(object):
    def __init__(self, **kwargs):
        self.kwdict = kwargs

或者它们的某些组合:

class Wat(object):
    def __init__(self, **kwargs):
        self.kwdict = kwargs
        self.kwdict['arg1'] = self.kwdict.get('arg1',[])
        self.kwdict['arg2'] = self.kwdict.get('arg2',[])
        self.kwdict['arg3'] = self.kwdict.get('arg3',[])

您甚至不关心参数名称,因此您可以使用*args

class Wat(object):
    def __init__(self, *args):
        self.args = list(args)
        while len(self.args) < 3:
            self.args.append([])

答案 4 :(得分:0)

怎么样

def default(x, fn):
    return fn() if x is None else x

class Wat(object):
    def __init__(self, arg1=None, arg2=None, arg3=None):
        self.arg1 = default(arg1, list)
        self.arg2 = default(arg2, list)
        self.arg3 = default(arg3, list)