2011-10-31 102 views
1

我有兩個類與方法foo玩對象創建

Foo = type('Foo', (object,), {'foo': lambda s: 'Foo method'}) 
Bar = type('Bar', (object,), {'foo': lambda s: 'Bar method'}) 

我有一些其它類,我需要根據參數子類上述類別之一。

我的解決方案:

class Subject(object): 
    def __new__(cls, key): 
     base = (Foo if key else Bar) 

     name = cls.__name__ + base.__name__ 
     dict_ = dict(cls.__dict__.items() + base.__dict__.items()) 
     bases = (base, cls) 

     t = type(name, bases, dict_) 
     return base.__new__(t) 

    def bar(self): 
     return 'Subject method' 

測試:

print(Subject(True).foo(), Subject(True).bar()) 
print(Subject(False).foo(), Subject(False).bar()) 

輸出:

('Foo method', 'Subject method') 
('Bar method', 'Subject method') 

足夠的這個解決方案安全嗎?或者我需要更多的東西來了解?是否有更多的pythonic方式來做這種不規則的東西?

+1

讓兩個班,'SubjectFoo'和'SubjectBar',繼承分別從'Foo'和'Bar',然後編寫一個函數'Subject'來檢查一個參數並返回一個正確類的實例。以意想不到的方式破壞的可能性要小得多。 –

+0

@ChrisLutz你可以在我的解決方案中看到什麼意想不到的方式? – scraplesh

回答

2

我想大多數人會看到上面的代碼,並推薦使用組合而不是繼承。 Subject將定義一個foo方法,該方法根據布爾值分派給正確的類。

或者,您可以使用工廠函數根據需要創建Foo或Bar。

def subject(selector): 
    'Factory function that chooses between Foo and Bar' 
    return Foo() if selector else Bar() 

如果需要的話,使雙方Foo和Bar從一個共同的類繼承,使本廠函數總是返回普通類的子類的實例。

+0

某種'namedtuple'函數? – scraplesh

+0

我沒有看到與指定元組的關係。 –

0

下面是可能由當前的方法會導致一些意外行爲的一個例子,即使從FooBar,在FooBar版本的方法的Subject覆蓋的東西會被稱爲:

class Subject(object): 
    # all of your current methods as they were above 
    def foo(self): 
     return 'Subject foo' 

>>> Subject(False).foo() 
'Bar method' 

儘管你可以通過在cls.__dict__.items()之前將base.__dict__.items()設置在你創建dict_的行上來解決這個問題,但我建議完全脫離這種方法,可能使用Chris Lutz的評論或Raymond's answer

如果所有你有興趣做的是有一個動態的方法,我會建議如下。創建的Subject類的內部方法專用版本,然後分配要在Subject.__init__()使用哪種方法:如果你避免元類(魔術)

class Subject(object): 
    def __init__(self, key): 
     self.foo = self._Foo_foo if key else self._Bar_foo 
    def _Foo_foo(self): 
     return 'Foo method' 
    def _Bar_foo(self): 
     return 'Bar method' 

>>> Subject(True).foo() 
'Foo method' 
>>> Subject(False).foo() 
'Bar method' 
+0

我同意最初的元類方法不是要走的路。工廠功能或動態調度是更傳統和可預測的解決方案。 –

1

代碼將更具可讀性(因此更Python) 。去與克里斯和雷蒙德建議的方法,即

使用組成:

class Subject(object): 
    def __init__(self, key): 
     self.foo = (Foo if key else Bar)().foo 
    def bar(self): 
     return 'Subject method' 

或使用工廠函數:

def Subject(key): 
    class Subject(Foo if key else Bar): 
     def bar(self): 
      return 'Subject method' 
    return Subject() 
+0

工廠函數或元類是我的選擇,謝謝 – scraplesh