2008-11-26 37 views
10

Ich weiß, dass Python-Funktionen standardmäßig virtuell sind. Sagen wir, ich habe dies:Funktionen nicht außer Kraft setzen

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

ich sie nicht wollen in der Lage sein, dies zu tun:

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

Gibt es eine Möglichkeit, damit die Benutzer roo Überlastung()?

+2

Warum wollen Sie so etwas? Hast du Angst, dass jemand es außer Kraft setzt und es nicht für sie funktioniert? es ist ihr Problem. Aber manchmal wissen sie, was sie tun und sie müssen es einfach tun. Ich habe diese Art der Beschränkung in Java drei Tage lang umgangen, in Python waren es 20 Sekunden. – Pablo

Antwort

32

Sie eine Metaklasse verwenden können:

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 
    ... 

Die Metatyp des neue wird aufgerufen, wenn eine Unterklasse erstellt wird; Dies wird zu einem Fehler führen. Es akzeptiert nur dann eine Definition von roo, wenn es keine Basisklassen gibt.

Sie können die Annäherung mehr Phantasie machen, indem Sie Annotationen verwenden, um zu erklären, welche Methoden endgültig sind; Sie müssen dann alle Basen inspizieren und alle endgültigen Methoden berechnen, um zu sehen, ob einer von ihnen außer Kraft gesetzt wird.

Dies hindert immer noch nicht jemand Affe-patchen eine Methode in eine Klasse, nachdem es definiert ist; Sie können versuchen, diese zu finden, indem Sie ein benutzerdefiniertes Wörterbuch als das Klassenwörterbuch verwenden (das möglicherweise nicht in allen Python-Versionen funktioniert, da Klassen das Klassenwörterbuch möglicherweise vom genauen Diktattyp benötigen).

+0

+1 für ein gutes Beispiel für eine Metaklasse __new__ –

+2

Ich würde dich hassen, wenn ich deine Klasse verwenden müsste – hop

+0

Konntest du deine Methode nicht als Deskriptor angeben, der verhindert hat, dass sie entfernt oder überschrieben wird? Vermutlich werden Deskriptoren nur beim Attribut-Lookup ausgelöst und nicht, wenn sie direkt im Klassenwörterbuch nachgeschlagen werden. – fuzzyman

8

Da Python Affenflicken hat, können Sie nicht nur nichts "privates" machen. Selbst wenn Sie könnten, könnte jemand immer noch in einer neuen Version der Methodenfunktion monkeypatch.

Sie können diese Art von Namen als Warnung "nicht in der Nähe gehen" verwenden.

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

Das ist der übliche Ansatz. Jeder kann deine Quelle lesen. Sie wurden gewarnt. Wenn sie mutig dorthin gehen, wo sie gewarnt wurden, nicht zu gehen, bekommen sie, was sie verdienen. Es funktioniert nicht und du kannst ihnen nicht helfen.

Sie können versuchen, dies bewusst mit inneren Klassen und "versteckten" Implementierungsmodulen, die von den "privaten" Methoden aufgerufen werden, zu machen. Aber ... jeder hat deine Quelle. Sie können nicht alles verhindern. Sie können die Menschen nur auf die Konsequenzen ihrer Handlungen hinweisen.

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

spät zur Party, aber nicht alle Python Methoden sind "virtuelle" standardmäßig - prüfen:

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() 

Blows aufgrund Namen Mangeln:

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' 

Also, wenn Sie wollen Holen Sie sich Hilfe von der Sprache, um zu verhindern, dass Benutzer ein wenig Funktionalität außer Kraft setzen, die Sie in Ihrer Klasse benötigen. Dies ist der richtige Weg. Wenn die Methode, die Sie final machen möchten, Teil Ihrer Klassen-API ist, bleiben Sie bei der Kommentarmethode (oder Metaklassen-Hacks) hängen.Meine persönliche Meinung ist, dass ein abschließendes Schlüsselwort sehr nützlich für die Vererbung ist - da man vermeiden kann, dass die Klasse auf heimtückische Weise beim Überschreiben bricht (z. B. die "finale" Methode in Superimplementierung und dann jemand außer Kraft setzt - boom, super broken) - und zu Dokumentationszwecken (keine Dokumente sind besser als eine Kompilierung Syntaxfehler) - aber Python dynamische Natur würde nicht zulassen, dass sie und Hacks sind zerbrechlich - so fügen Sie ein docstring:

"""DON'T OVERRIDE THIS METHOD""" 
Verwandte Themen