2011-07-14 43 views
9

的代碼如下,只是基本的結構:刪除從子類的具體方法,其是從父類繼承

class FooType(type): 
    def __new__(cls, name, bases, classdict): 
     instance = type.__new__(cls, name, bases, classdict) 
     # What can I do here? 
     return instance 

class FooBase(object, metaclass=FooType): 
    def __init__(self): 
     pass 

class Foo(FooBase): 
    def __init__(self, name): 
     self.name = name 

    def method1(self): 
     pass 

    def method2(self): 
     pass 

    def specialmethod(self): 
     pass 

class A(Foo): 
    pass 

class B(Foo): 
    pass 

class C(Foo): 
    _disallowed_methods = ['specialmethod'] 

我想要做的是,C類的實例,不應該有specialmethod,但該方法應該可用於實例AB

我可以在類C中覆蓋此方法並引發錯誤,但我不希望這樣做。

我知道我可以在代碼添加到您在該FooType_disallowed_methods和檢查的基礎上,如果instance有任何人在dir(instance)輸出。但我似乎無法使用我迄今嘗試過的任何方法從的C中刪除該方法。我嘗試的方法是delattr(instance, 'specialmethod')del instance.__dict__['specialmethod']

delattr方法導致「AttributeError的:specialmethod」和del方法導致「類型錯誤:‘dict_proxy’對象不支持項目刪除」

基本上許多不同類別將從Foo繼承,但有些他們不應該有他們可用的具體方法,如C,它們不應該有specialmethod可用。

我在做什麼錯?或者我該怎麼做到這一點?

+2

順便說一句:這將違反[里氏替換原則](http://en.wikipedia.org/wiki/Liskov_substitution_principle)。 – pillmuncher

回答

1

Or how else can I accomplish this?

使用multiple inheritance可以獲得類似的結果。

將您只想要某些孩子的方法從Foo移動到ExtraFoo。然後使用class A(Foo, ExtraFoo)class C(Foo)。這樣,你甚至可以將一個給定的方法「重新連接」到子層次結構的更遠處。

如果重新連接方法是不是你有興趣,那麼你可以簡單地ExtraFoo作爲Foo一個孩子(這樣:添加方法,而不是分離它們),並有class A(ExtraFoo)class C(Foo)

+0

我意識到我可以通過使用'Foo'和'FooExtra'創建額外的類和子類來爲其他類需要'specialmethod'和'Foo'的類。我只是想,當使用元類創建實例時,我可能可以從dict中移除該方法。猜猜我錯了... – skulled

+1

@skulled - Python是非常靈活的,並且很有可能你可以通過某種方式來實現內部處理。然而,最重要的一點是這將是糟糕的設計。其中一個OOP原則是一個子類從其父「繼承」,因此「刪除」一個子類就像在飛機上構建機翼,不會讓它飛起來! :)順便說一句:你應該提高你發現有用的問題,並且 - 如果任何解決了你的問題 - 你可以將它標記爲「接受」(只是提及,因爲我注意到你在網站上是新的,如果你已經知道,請原諒我那)。 – mac

+0

謝謝!我有點意識到,一旦答覆證實了我的懷疑,我可能會採取錯誤的做法。 – skulled

6

那麼,你不能這樣做,因爲你必須修改不是C類,而是Foo類,它確實包含specialmethod。但實際上你不能這樣做,因爲class是全局可變對象,任何對Foo的更改都會影響所有子類。

試着用另一種方式思考。例如。您可以修改C類的訪問屬性的邏輯:

class C(Foo): 
    def __getattribute__(self, name): 
     if name in ['specialmethod']: 
      raise AttributeError('no such method') 
     return super(C, self).__getattribute__(name) 

C('a').specialmethod()後產生回溯:

Traceback (most recent call last): 
    File "meta.py", line 37, in <module> 
    C('a').specialmethod() 
    File "meta.py", line 34, in __getattribute__ 
    raise AttributeError('no such method') 
AttributeError: no such method 
+0

我試了一下(在我的第一篇文章中沒有提及這一點),並計劃將這條路線作爲最後的手段。根據我的理解,這種方法意味着每次使用'C'類實例的屬性時,都會經歷這種方法,因此我的理解是它對速度/性能的影響。這可以忽略不計,我不必擔心嗎? – skulled

+0

@ skulled是的,它可能會影響性能。但我不能相信它會對你的情況有重大意義。如果你只是在擔心'timeit'。 –

0

如果你有一個家長,你不想被修改,和孩子有一種或多種你想要無法訪問的繼承方法,你可以用描述符來完成。 一個最簡單的方法是使用property內置:

class Parent: 
    def good_method(self): 
     print('Good one') 

    def bad_method(self): 
     print('Bad one') 

class Child(Parent): 
    bad_method = property(doc='(!) Disallowed inherited') 

one = Parent() 
one.good_method() # > 'Good one' 
one.bad_method() # > 'Bad one' 

two = Child() 
two.good_method() # > 'Good one' 
two.bad_method() # > AttributeError: unreadable attribute 
two.bad_method  # > AttributeError: unreadable attribute 
two.bad_method = 'Test' # > AttributeError: can't set attribute 

如何幫助(二)打印它:

class Child(Parent) 
| Method resolution order: 
|  Child 
|  Parent 
|  builtins.object 
| 
| Data descriptors defined here: 
| 
| bad_method 
|  (!) Disallowed inherited 
| 
| ---------------------------------------------------------------------- 
| Methods inherited from Parent: 
| 
| good_method(self) 
| 
| ---------------------------------------------------------------------- 
| Data descriptors inherited from Parent: 
| 
| __dict__ 
|  dictionary for instance variables (if defined) 
| 
| __weakref__ 
|  list of weak references to the object (if defined) 

還不錯,在我看來。但是,你要小心,不要定義繼承的方法這樣,如果其他方法都依賴於它們(這可以通過使用代理類,它從其父繼承和重新定義這些方法使用super().bad_method(),而不是僅僅self.bad_method()避免,並指出了bad_method本身不允許描述)。 如果需要

0

我已經與測試工作你可以編寫更復雜的描述邏輯並在完全相同的問題絆倒。

我發現只有一個正確的方法,從繼承類中刪除「不必要的方法」:從父將其刪除。 (這是個壞主意,因爲如果該函數被調用至少一次,它將會中斷所有父類的所有實例以及所有繼承類的所有實例)。

示例代碼:

class Base(object): 
    def excessive(self): 
     pass 

class Inher(Base): 
    def __init__(self): 
     #import pdb 
     #pdb.set_trace() 
     del Base.excessive 

b=Base() 
example = Inher() 
try: 
    example.excessive() 
except AttributeError: 
    print("Removed!") 
else: 
    raise Exception("Not removed!") 
try: 
    b.excessive() 
except AttributeError: 
    print("Unfortunately, all instances of Base no longer have .excessive() method") 

其原因是「繼承」的方法不存儲在父(如代碼或鏈路),但被保持內部母體。當有人調用方法時,python會遍歷所有父類,直到找到一個或停止。

在我來說,我能因爲我用其他人的測試我的目的,我保留了他們的「設置/拆卸」和腋窩的方法來使用這種技術,但我已經刪除了所有的測試。

任何實際生活中的應用不應該使用這種技術。