2013-11-24 25 views
0

我的目标是为几个对象属性设置可变类型(列表)的默认值。根据here的讨论,我在__init__的参数中将这些属性的默认值设置为None,然后如果他们采用默认值,则将它们的值分配给[],其值为__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仍然可以在时间wat.give(5)None被称为 - 他们应该在__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个变量。我如何在最少的代码行中完成这项任务?这是循环的最初目的。谢谢你们。

+0

你可以只做'self.arg1 = arg1如果arg1 else []'等等。跳过那个中间步骤。 – roippi

+0

@roippi,考虑'如果arg1是None else []',否则条件不一样。 –

+0

@kroolik如果你的类已经定义了多个falsy值的行为,当然。否则,我认为这种区别毫无意义。 – roippi

回答

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

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

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) 
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]} 
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([]) 

e tc。