2017-08-12 51 views
2

假設我有的,這將產生O對象輸入列表,以下形式的:啓動子從父類

inps = [['A', 5], ['B', 2]] 

和O具有亞類A和B A和B各自與單個啓動整數 - 上述示例中的5或2 - 並且有一個方法update(self, t),所以我認爲將它們歸入O超類下是有意義的。我可以用一個循環完成程序:

Os = [] 
for inp in inps: 
    if inp[0] == 'A': 
     Os.append(A(inp[1])) 
    elif inp[0] == 'B': 
     Os.append(B(inp[1])) 

,然後在運行時,

for O in Os: O.update(t) 

我不知道,但是,如果有更多的面向對象的方式來做到這一點。一種方式,我想,可能是做一個假的「O構造」外將O類:

def initO(inp): 
    if inp[0] == 'A': 
     return A(inp[1]) 
    elif inp[0] == 'B': 
     return B(inp[1]) 

Os = [initO(inp) for inp in inps] 

這是更優雅,在我看來,和所有密集的目的給我我想要的結果;但感覺就像是Python中完全濫用類系統。有沒有更好的方法來做到這一點,也許通過O構造函數啓動A和B?

編輯:理想的做法是能夠使用

Os = [O(inp) for inp in inps] 

,同時保持O爲A的超類和B

回答

2

你可以使用一個dict的名字實際類映射:

dct = {'A': A, 'B': B} 

[dct[name](argument) for name, argument in inps] 

或者,如果你不想讓列表理解:

dct = {'A': A, 'B': B} 
Os = [] 
for inp in inps: 
    cls = dct[inp[0]] 
    Os.append(cls(inp[1])) 
1

儘管在技術上有可能在Python中執行名爲的調用,但強烈建議您不要這樣做。最徹底的方法可能是使用字典:

trans = { 'A' : A, 'B' : B } 

def initO(inp): 
    cons = trans.get(inp[0]) 
    if cons is not None: 
     return cons(*inp[1:])

所以這裏trans是在映射類(以及因此相應的構造函數)名稱的字典。

initO我們執行查找,如果查找成功,我們調用構造函數cons,其餘參數爲inp

0

不知道更多關於您的AB,這很難說。但是這看起來像使用C這樣的語言的switch的經典案例。Python沒有switch語句,因此改爲使用類似於dictdict的構造。

如果你確定你輸入的是乾淨的,你可以直接得到使用類globals()功能:

Os = [globals()[f](x) for (f, x) in inps] 

如果要清理,你可以做這樣的事情:

allowed = {'A', 'B'} 
Os = [globals()[f](x) for (f, x) in inps if f in allowed] 

如果您希望擁有固定字典和消毒輸入,也可以更改此解決方案:

allowed = {'A', 'B'} 
classname_to_class = {k: v for (k, v) in globals().iteritems() if k in allowed} 
# Now, you can have a dict mapping class names to classes without writing 'A': A, 'B': B ... 

或者,如果你能前綴所有的類定義,你甚至可以做這樣的事情:

classname_to_class = {k[13:]: v for (k, v) in globals().iteritems() if k.startswith('SpecialPrefix'} # 13: is the length of 'SpecialPrefix' 

該解決方案可以讓你只用一個前綴命名類和有字典自動填充(剝出後如果你這麼選擇的特殊前綴)。這些字典在這裏發佈的其他解決方案中相當於transdct,除了無需手動生成字典。

不像其他的解決方案,迄今爲止公佈,這些減少抄寫錯誤的情況下,可能性(和樣板代碼所需要的量),你必須比A和B.

+0

對於低調的選民,你能解釋一下投票背後的推理嗎?這個解決方案不起作用(除了在本地定義類時)還是我說錯了什麼?我沒有回答這個問題嗎?我在這裏沒有充分警告地採用一些不好的做法嗎? – lungj

+0

感謝您的回答。您使用的是globals()@Willem提到的「名稱呼叫」功能嗎? – biffletsbq

+0

有幾種方法可以在Python中按名稱調用事物(例如,https://stackoverflow.com/questions/3061/calling-a-function-of-a-module-from-a-string-with-the-functions -python中的名稱顯示幾個wyas),所以我不確定哪一個被引用。而且,根據您的情況,通過名稱呼叫沒有任何問題。我將用另一種完成任務的方式更新我的答案,與上面的更類似。 – lungj

0

多了很多類在風險繪製更多負面消息......我們可以使用元類。這可能會或可能不適合您的特定應用程序。每當您定義類O的子類時,您總是會獲得O的子類的最新列表(以及dict)。哦,這是爲Python 2編寫的(但可以移植到Python 3)。

class OMetaclass(type): 
    '''This metaclass adds a 'subclasses' attribute to its classes that 
    maps subclass name to the class object.''' 
    def __init__(cls, name, bases, dct): 
     if not hasattr(cls, 'subclasses'): 
      cls.subclasses = {} 
     else: 
      cls.subclasses[name] = cls 

     super(OMetaclass, cls).__init__(name, bases, dct) 

class O(object): 
    __metaclass__ = OMetaclass 

### Now, define the rest of your subclasses of O as usual. 

class A(O): 
    def __init__(self, x): pass 

class B(O): 
    def __init__(self, x): pass 

現在,你有一本字典,O.subclasses,包含的O所有子類。現在,您可以只是這樣做:

Os = [O.subclasses[cls](arg) for (cls, arg) in inps] 

現在,您不必擔心怪異的前綴你的類,你將不再需要,如果你繼承O已經改變你的代碼,但你」已經引入了魔術(元類),可能會讓你的程序更難以理解。

1

如果你真的想創建一個父類中的一個(直接)的子類,你可以使用特殊__subclasses__方法:

class O(object): 
    def __init__(self, integer): 
     self.value = integer 

    @classmethod 
    def get_subclass(cls, subclassname, value): 
     # probably not a really good name for that method - I'm out of creativity... 
     subcls = next(sub for sub in cls.__subclasses__() if sub.__name__ == subclassname) 
     return subcls(value) 

    def __repr__(self): 
     return '{self.__class__.__name__}({self.value})'.format(self=self) 

class A(O): 
    pass 

class B(O): 
    pass 

這就像一個工廠:

>>> O.get_subclass('A', 1) 
A(1) 

或者作爲列表理解:

>>> [O.get_subclass(*inp) for inp in inps] 

如果你想優化它, d。您知道你會不會增加子類在程序進步,你可以把在子類映射從__name__子類的字典:

class O(object): 
    __subs = {} 
    def __init__(self, integer): 
     self.value = integer 

    @classmethod 
    def get_subclass(cls, subclassname, value): 
     if not cls.__subs: 
      cls.__subs = {sub.__name__: sub for sub in cls.__subclasses__()} 
     return cls.__subs[subclassname](value) 

你也許還可以使用__new__實施該行爲或元類但我認爲classmethod在這裏可能更合適,因爲它很容易理解,並允許更多的靈活性。

如果您不僅想要直接子類,您可能需要檢查this recipe以查找您的子類的甚至子類(我也在我的第三方擴展包中實現了它:iteration_utilities.itersubclasses)。