2017-06-12 82 views
2

編輯:我對邊緣案例的看法越多,似乎就越沒有用。我會把它打開,因爲我覺得它很有趣,但我明白這不是一件好事。如何擁有一個類或對象的「多」對象處理程序?

我想將某個類的幾個對象合併成一個同類的'多對象',它基本上獲得子類的相同屬性。我不知道怎麼解釋,所以這裏有一個例子:

#!/usr/bin/env python3 

class Foo: 
    def __init__(self, x): 
     self.x = x 

    def bar(self, y): 
     print(self.x) 
     return 2 * y 


f1 = Foo(2) 
f2 = Foo(3) 
f3 = Foo(5) 
f4 = Foo(7) 

def multi(*args): 
    if len(args) == 0: 
     raise ValueError("Requires at least one object") 
    if not all(isinstance(arg, type(args[0])) for arg in args): 
     raise TypeError("All objects must be the same type") 
    # ... magic 

multi_f = multi(f1, f2, f3, f4) 

assert multi_f.x == [2, 3, 5, 7] # attributes return a list of each of the objects in the multi_f 
assert multi_f.bar(y=5) == [10, 10, 10, 10] # prints 2 then 3 then 5 then 7 but "5" is passed to all of them 

我最初雖然覆蓋__getattr__和朋友,但所有的其他功能的思想,你不得不重寫,並想知道是否有一個更好的方法。

的酒吧功能的另一種選擇是這樣的,每個功能將獲得它自己的參數,但這些都是相互排斥的:

assert multi_f.bar([ 
    [[], dict(y=5)], 
    [[], dict(y=10)], 
    [[], dict(y=20)], 
    [[], dict(y=40)], 
]) == [10, 20, 40, 80] 

有了這樣的想法,但是,你必須圖示列表,寫入每個對象的函數調用。

回答

1

這是一個有趣的想法!因此,下面的解決方案使用一個類(Multi)(因爲我們正在創建一個'多'對象,它應該是一個實際的對象)。我只實施__getattr__,但我認爲你應該可以覆蓋其他方法,因爲你需要它們(例如,__setattr__如果你想分配,__eq__測試是否相等,等等) - 這將需要很多計劃確定每個人應該如何行事!

class Multi: 
    def __init__(self, *args): 
     self.foos = args 

    def __getattr__(self, key, *args, **kwargs): 
     ret = [] 
     # two options - accessing values, or methods to call 
     # this boolean decides which type to return (i'm sure there's a neater way, but this was quick) 
     wrap = False 

     # build a list of the attributes from foos 
     for foo in self.foos: 
      x = getattr(foo, key) 
      if callable(x): 
       wrap = True 
      ret.append(x) 

     # return an anonymous function that when called, returns a list 
     # with the result of calling each callable with whatever args/kwargs 
     if wrap: 
      return lambda *args, **kwargs: [x(*args, **kwargs) for x in ret] 

     # otherwise just return the list of values 
     return ret 

multi_f = Multi(f1, f2, f3, f4) 
assert multi_f.x == [2, 3, 5, 7] # attributes return a list of each of the objects in the multi_f 
assert multi_f.bar(y=5) == [10, 10, 10, 10] # prints 2 then 3 then 5 then 7 but "5" is passed to all of them 

我相信你認爲最後的選擇將是可能的,但是,是的,他們不能兩者在同一時間,除非你使用一個額外的參數作爲一個標誌/撥動開關實現。

+1

是的,我同意,這與我所做的類似,儘管我最終使用'type'並創建了一個class inline。然而,這看起來更乾淨。感謝您的答覆! – Goodies