2013-04-17 5 views
7

mi sono imbattuto nel seguente nel python docs:In che modo Python impedisce a una classe di essere sottoclassata?

bool ([x])

convertire un valore per un valore booleano, utilizzando la procedura di prova vero standard. Se x è falso o omesso, restituisce False; in caso contrario, restituisce True. bool è anche una classe, che è una sottoclasse di int. La classe non può essere ulteriormente classificata in sottoclasse. Le sue uniche istanze sono false e vero.

non ho mai in vita mia voleva sottoclasse bool, ma naturalmente ho subito provato, e abbastanza sicuro:

>>> class Bool(bool): 
    pass 

Traceback (most recent call last): 
    File "<pyshell#2>", line 1, in <module> 
    class Bool(bool): 
TypeError: Error when calling the metaclass bases 
    type 'bool' is not an acceptable base type 

Quindi, la domanda: come è questo fatto? E posso applicare la stessa tecnica (o un'altra) per contrassegnare le mie classi come final, ad esempio, per impedire che vengano sottoclassate?

+1

perché non si vuole creare una sottoclasse 'bool'? È possibile creare sottoclassi '10' per rappresentare ogni possibile stato booleano. – jamylak

+0

Grazie a @Martijn, sembra una domanda strettamente correlata. Non è comparso durante la mia ricerca SO (avrei dovuto pensare di aggiungere "finale" ai termini di ricerca). – alexis

risposta

13

Il tipo bool è definito in C e lo slot tp_flags non include deliberatamente lo Py_TPFLAGS_BASETYPE flag.

I tipi C devono contrassegnare se stessi in modo esplicito come sottoclasse.

di fare questo per le classi Python personalizzato, utilizzare un metaclasse:

class Final(type): 
    def __new__(cls, name, bases, classdict): 
     for b in bases: 
      if isinstance(b, Final): 
       raise TypeError("type '{0}' is not an acceptable base type".format(b.__name__)) 
     return type.__new__(cls, name, bases, dict(classdict)) 

class Foo: 
    __metaclass__ = Final 

class Bar(Foo): 
    pass 

dà:

>>> class Bar(Foo): 
...  pass 
... 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in __new__ 
TypeError: type 'Foo' is not an acceptable base type 
+0

Grazie, questo lo copre! – alexis

+0

Su ulteriore riflessione, ho una domanda: C'è qualche motivo per il controllo 'isinstance (b, Final)'? Voglio dire, questo '__new__' sarà sempre chiamato da una classe che eredita da' Finale', quindi perché non aumentare immediatamente immediatamente se 'cls! = 'Finale''? – alexis

+0

@alexis: Sì, perché è necessario nominare quale baseclass si è tentato di ereditare da. 'Bar' non sottoclasse' Final', sottoclasse 'Foo', motivo per cui il test' isinstance (b, Final) 'restituisce' True' per quella classe. Se ci sono più classi base usate, vuoi dire all'utente finale quale di queste basi non è ereditabile. –

Problemi correlati