2008-11-26 113 views
10

我知道Python函數默認是虛擬的。比方說,我有這樣的:使功能無法覆蓋

class Foo: 
    def __init__(self, args): 
     do some stuff 
    def goo(): 
     print "You can overload me" 
    def roo(): 
     print "You cannot overload me" 

我不希望他們能夠做到這一點:

class Aoo(Foo): 
    def roo(): 
     print "I don't want you to be able to do this" 

有沒有辦法來防止用戶超載小豆()?

+2

爲什麼你想這樣的事情?你害怕有人會重寫它,它不會爲他們工作嗎?這是他們的問題。但有時候,他們知道他們在做什麼,他們只需要做。我花了三天的時間解決了Java中的這種限制,在Python中是20秒。 – Pablo 2008-11-26 16:01:59

回答

32

您可以使用元類:

class NonOverridable(type): 
    def __new__(self, name, bases, dct): 
     if bases and "roo" in dct: 
      raise SyntaxError, "Overriding roo is not allowed" 
     return type.__new__(self, name, bases, dct) 

class foo: 
    __metaclass__=NonOverridable 
    ... 

元類型的新只要創建一個子類被調用;這會導致出現錯誤。只有在沒有基類的情況下,它纔會接受roo的定義。

您可以通過使用註釋來聲明哪些方法是最終的;那麼你需要檢查所有的基礎並計算所有的最終方法,看看它們中的任何一個是否被覆蓋。

這仍然不能阻止某人在定義一個類後將某個方法修改爲類;您可以嘗試通過使用自定義字典作爲類的字典(它可能不適用於所有Python版本,因爲類可能需要類字典纔是確切的字典類型)。

+0

+1元類的好例子__new__ – 2008-11-26 16:12:09

8

由於Python有猴子補丁,你不僅不能做任何「私人」的東西。即使你可以,有人仍然可以在方法函數的新版本中使用monkeypatch。

您可以將此類名稱用作「不要靠近」警告。

class Foo(object): 
    def _roo(self): 
     """Change this at your own risk.""" 

這是通常的做法。每個人都可以閱讀你的來源他們受到警告。如果他們大膽地去他們被警告不要去的地方,他們會得到他們應得的。它不起作用,你不能幫助他們。

您可以嘗試使用由「私有」方法調用的內部類和「隱藏」實現模塊使其有意隱藏。但是......每個人都有你的來源。你不能阻止什麼。你只能告訴人們他們行爲的後果。

6
def non_overridable(f): 
    f.non_overridable = True 
    return f 

class ToughMeta(type): 
    def __new__(cls, name, bases, dct): 
     non_overridables = get_non_overridables(bases) 
     for name in dct: 
      if name in non_overridables: 
       raise Exception ("You can not override %s, it is non-overridable" % name) 
     return type.__new__(cls, name, bases, dct) 

def get_non_overridables(bases): 
    ret = [] 
    for source in bases: 
     for name, attr in source.__dict__.items(): 
      if getattr(attr, "non_overridable", False): 
       ret.append(name) 
     ret.extend(get_non_overridables(source.__bases__)) 
    return ret 

class ToughObject(object): 
    __metaclass__ = ToughMeta 
    @non_overridable 
    def test1(): 
     pass 

# Tests --------------- 
class Derived(ToughObject): 
    @non_overridable 
    def test2(self): 
     print "hello" 

class Derived2(Derived): 
    def test1(self): 
     print "derived2" 

# -------------------- 
0

遲到了,但並不是所有的Python方法都是 「虛擬」 默認 - 考慮:

class B(object): 
    def __priv(self): print '__priv:', repr(self) 

    def call_private(self): 
     print self.__class__.__name__ 
     self.__priv() 

class E(B): 
    def __priv(self): super(E, self).__priv() 

    def call_my_private(self): 
     print self.__class__.__name__ 
     self.__priv() 

B().call_private() 
E().call_private() 
E().call_my_private() 

吹燈由於名稱重整:

B 
__priv: <__main__.B object at 0x02050670> 
E 
__priv: <__main__.E object at 0x02050670> 
E 
Traceback (most recent call last): 
    File "C:/Users/MrD/.PyCharm2016.3/config/scratches/test_double__underscore", line 35, in <module> 
    E().call_my_private() 
    File "C:/Users/MrD/.PyCharm2016.3/config/scratches/test_double__underscore", line 31, in call_my_private 
    self.__priv() 
    File "C:/Users/MrD/.PyCharm2016.3/config/scratches/test_double__underscore", line 27, in __priv 
    def __priv(self): super(E, self).__priv() 
AttributeError: 'super' object has no attribute '_E__priv' 

所以,如果你想從語言中獲得一些幫助,以禁止人們重寫你在課堂中需要的一些功能,這是要走的路。如果你想做最終的方法是你的類API的一部分,但你堅持使用評論方法(或metaclass hacks)。我個人的觀點是最終的關鍵字對於繼承是非常有用的,因爲你可以避免類在被覆蓋時以陰險的方式破壞(例如考慮在超級實現中使用「最終」方法,然後有人覆蓋 - 繁榮,超級破壞) - 和文檔的目的(不文檔是不是編譯時語法錯誤更好) - 但是Python的動態特性不會允許它和黑客都是脆弱的 - 那麼添加文檔字符串:

"""DON'T OVERRIDE THIS METHOD"""