2015-03-03 22 views
2

我在Python中有一個繼承鏈,我希望每個子類都能夠添加新的自定義參數。現在我這樣做:在Python中繼承時增強類屬性

class A(object): 
    PARAM_NAMES = ['blah1'] 
    ... 

class B(A): 
    PARAM_NAMES = A.PARAM_NAMES + ['blah2'] 
    ... 

我不知道是否有一種方法雨衣,不過,沒有引用A兩次?不能使用super(),因爲它不在方法定義afaik中。我想我可以使用一個類方法,但這會很煩人(因爲我真的想要一個屬性)。

什麼是正確的方法來做到這一點?

+0

這種方式怎麼了? – 2015-03-03 00:15:18

回答

1

你描述的行爲其實很具體。你說你

希望每個子類來能夠上添加新的自定義paramters

但是你已經實現了它的方式,這將導致不可預知的行爲。試想一下:

class A(object): 
    PARAM_NAMES = ['blah1'] 

class B(A): 
    PARAM_NAMES = A.PARAM_NAMES + ['blah2'] 

class C(A):pass 

print(A.PARAM_NAMES) 
print(B.PARAM_NAMES) 
print(C.PARAM_NAMES) 
A.PARAM_NAMES.append('oops') 
print(C.PARAM_NAMES) 

我們注意的是,那些選擇添加新的參數的類有一個新的參考參數列表,而那些不增加新的參數具有相同的參考其父。除非仔細控制,否則這是不安全的行爲。

只使用常量作爲類屬性,或者每次完全重新定義列表(使它成爲元組)不是「光滑的」更加可靠。否則,我會推薦你​​的建議,並使該屬性爲實例變量的類方法

+0

是的,我同意這是我目前的方法的另一個缺點。我只是不確定什麼是「最好的」做我想要的是... – 2015-03-03 03:15:29

2

粗糙總有黑魔法你可以做...但問題只是因爲你可以... 應該你

class MyMeta(type): 
    items = [] 
    def __new__(meta, name, bases, dct): 
     return super(MyMeta, meta).__new__(meta, name, bases, dct) 
    def __init__(cls, name, bases, dct): 
     MyMeta.items.extend(cls.items) 
     cls.items = MyMeta.items[:] 
     super(MyMeta, cls).__init__(name, bases, dct) 

class MyKlass(object): 
    __metaclass__ = MyMeta 

class A(MyKlass): 
    items=["a","b","c"] 

class B(A): 
    items=["1","2","3"] 

print A.items 
print B.items 

,因爲這將創建一個副本,它不會有同樣的問題的其他解決辦法

(請注意,我真的不推薦這樣做......它只是給你看可以)

+1

沒有理由甚至沒有'__new__'方法... – kindall 2015-03-03 01:44:08

2

這可能會也可能不會很聰明,但在技術上可以爲此使用元類。與Joran的方法不同,我使用一個屬性,以便保留完整的動態性質(即,如果在定義類後修改任何類的私人_PARAM_NAMES列表,則其他派生類的相應PARAM_NAME屬性反映該更改)。基於這個原因,我在基類上放了一個add_param方法。

這裏假設Python 3,PARAM_NAMES屬性返回set以避免重複的項目。

class ParamNameBuilderMeta(type): 
    def __new__(mcl, name, bases, dct): 
     names = dct.get("PARAM_NAMES", []) 
     names = {names} if isinstance(names, str) else set(names) 
     dct["_PARAM_NAMES"] = names 
     dct["PARAM_NAMES"] = property(lambda s: type(s).PARAM_NAMES) 
     return super().__new__(mcl, name, bases, dct) 
    @property 
    def PARAM_NAMES(cls): 
     # collect unique list items ONLY from our classes in the MRO 
     return set().union(*(c._PARAM_NAMES for c in reversed(cls.__mro__) 
        if isinstance(c, ParamNameBuilderMeta))) 

用法:

class ParamNameBuilderBase(metaclass=ParamNameBuilderMeta): 
    @classmethod 
    def add_param(self, param_name): 
     self._PARAM_NAMES.add(param_name) 

class A(ParamNameBuilderBase): 
    PARAM_NAMES = 'blah1' 

class B(A): 
    PARAM_NAMES = 'blah1', 'blah2' 

class C(B): 
    pass 

檢查,以確保它可以在兩個類和實例及其:

assert C.PARAM_NAMES == {'blah1', 'blah2'} 
assert C().PARAM_NAMES == {'blah1', 'blah2'} 

檢查,以確保它仍然是動態的:

C.add_param('blah3') 
assert C.PARAM_NAMES == {'blah1', 'blah2', 'blah3'}