2013-11-24 27 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。