classi Tipo Python() o__class__,== o è




ereditarietà python (4)

deve essere utilizzato solo per i controlli di identità, non per i controlli di tipo (esiste un'eccezione alla regola in cui è possibile e dovrebbe essere utilizzato per il controllo contro i singleton).

Nota: in genere non userei type e == per i controlli di tipo. Il modo preferibile per i controlli di tipo è isinstance(obj, Foo) . Se hai mai un motivo per controllare se qualcosa non è un'istanza di sottoclassi, per me ha un odore di pesce. Quando la class Foo(Bar): allora Bar è un Foo , e dovresti evitare situazioni in cui alcune parti del tuo codice devono lavorare su un'istanza di Foo ma si rompono su un'istanza di Bar .

Voglio verificare se un oggetto è un'istanza di una classe e solo questa classe (nessuna sottoclasse). Potrei farlo anche con:

obj.__class__ == Foo
obj.__class__ is Foo
type(obj) == Foo
type(obj) is Foo

Ci sono dei motivi per sceglierne uno rispetto all'altro? (differenze di prestazioni, insidie, ecc.)

In altre parole: a) c'è qualche differenza pratica tra l'uso di __class__ e type(x) ? b) gli oggetti di classe sono sempre sicuri per il confronto usando?

Aggiornamento: grazie a tutti per il feedback. Sono ancora sconcertato dal fatto che gli oggetti di classe siano o meno singleton, il mio buon senso dice che lo sono, ma è stato davvero difficile ottenere una conferma (prova a cercare su google "python", "class" e "unique" o "singleton") .

Vorrei anche chiarire che, per le mie particolari esigenze, la soluzione "più economica" che funziona è la migliore, dal momento che sto cercando di ottimizzare il massimo da poche classi specializzate (quasi raggiungendo il punto in cui il sensibile cosa da fare è far cadere Python e sviluppare quel particolare modulo in C). Ma la ragione alla base della domanda era capire meglio il linguaggio, dal momento che alcune delle sue caratteristiche sono un po 'troppo oscure per me per trovare facilmente quell'informazione. Ecco perché sto lasciando che la discussione si estenda un po 'invece di accontentarsi di __class__ is , quindi posso sentire l'opinione di persone più esperte. Finora è stato molto fruttuoso!

Ho eseguito un piccolo test per valutare le prestazioni delle 4 alternative. I risultati del profiler sono stati:

               Python  PyPy (4x)
type()    is   2.138   2.594
__class__ is   2.185   2.437
type()    ==   2.213   2.625
__class__ ==   2.271   2.453

Non sorprendentemente, is eseguito meglio di == per tutti i casi. type() ottenuto risultati migliori in Python (2% più veloce) e __class__ ottenuto risultati migliori in PyPy (il 6% più veloce). È interessante notare che __class__ == ottenuto risultati migliori in PyPy rispetto a type() is .

Aggiornamento 2: molte persone non sembrano capire cosa intendo con "una classe è un singleton", quindi mi dilungherò con un esempio:

>>> class Foo(object): pass
...
>>> X = Foo
>>> class Foo(object): pass
...
>>> X == Foo
False
>>> isinstance(X(), Foo)
False
>>> isinstance(Foo(), X)
False

>>> x = type('Foo', (object,), dict())
>>> y = type('Foo', (object,), dict())
>>> x == y
False
>>> isinstance(x(), y)
False

>>> y = copy.copy(x)
>>> x == y
True
>>> x is y
True
>>> isinstance(x(), y)
True
>>> y = copy.deepcopy(x)
>>> x == y
True
>>> x is y
True
>>> isinstance(x(), y)
True

Non importa se ci sono N oggetti di tipo type , dato un oggetto, solo uno sarà la sua classe, quindi in questo caso è sicuro confrontarsi per riferimento. E dal momento che il confronto dei riferimenti sarà sempre più economico rispetto al confronto dei valori, volevo sapere se la mia affermazione precedente valesse o meno. Sto arrivando alla conclusione che lo fa, a meno che qualcuno non presenti prove contrarie.


Answer #1

Il risultato di type() è equivalente a obj.__class__ nelle nuove classi di stile, e gli oggetti di classe non sono sicuri per il confronto usando is , usa == invece.

Per le nuove classi di stile il modo preferibile qui sarebbe type(obj) == Foo .

Come ha sottolineato Michael Hoffman nella sua risposta, qui c'è una differenza tra classi nuove e vecchie, quindi per il codice compatibile con le versioni precedenti potrebbe essere necessario utilizzare obj.__class__ == Foo .

Per coloro che sostengono che isinstance(obj, Foo) è preferibile, si consideri il seguente scenario:

class Foo(object):
    pass

class Bar(Foo):
    pass

>>> obj = Bar()
>>> isinstance(obj, Foo)
True
>>> type(obj) == Foo
False

L'OP vuole il comportamento di type(obj) == Foo , dove sarà falso anche se Foo è una classe base di Bar .


Answer #2

Aggiornamento: grazie a tutti per il feedback. Sono ancora sconcertato dal fatto che gli oggetti di classe siano o meno singleton, il mio buon senso dice che lo sono, ma è stato davvero difficile ottenere una conferma (prova a cercare su google "python", "class" e "unique" o "singleton") .

Posso confermare che __instance__ è un singleton. Ecco la prova.

>>> t1=File.test()
made class
>>> t2=File.test()
made class
>>> print t1.__class__()
made class
<File.test object at 0x1101bdd10>
>>> print t2.__class__()
made class
<File.test object at 0x1101bdd10>

Come puoi vedere sia t1 sia t2 stampano lo stesso valore in memoria anche se t1 e t2 hanno valori differenti in memoria. Ecco la prova di ciò.

>>> print t1
<File.test object at 0x1101bdc90>
>>> print t2
<File.test object at 0x1101bdcd0>

Il metodo __instance__ esiste solo se hai usato il class "name"(object): Se si utilizza la classica classe di stile, class "name": __instance__ metodo __instance__ non esiste.

Ciò significa che deve essere il più generico che probabilmente vorresti usare, a meno che tu non sappia che esiste un'istanza di fatto.


Answer #3

Per le classi vecchio stile, c'è una differenza:

>>> class X: pass
... 
>>> type(X)
<type 'classobj'>
>>> X.__class__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class X has no attribute '__class__'
>>> x = X()
>>> x.__class__
<class __main__.X at 0x171b5d50>
>>> type(x)
<type 'instance'>

Il punto delle classi di nuovo stile era di unificare classe e tipo. Tecnicamente parlando, __class__ è l'unica soluzione che funzionerà sia per le istanze di classi nuove che di vecchio stile, ma genererà anche un'eccezione sugli stessi oggetti di classe vecchio stile. Puoi chiamare type() su qualsiasi oggetto, ma non tutti gli oggetti hanno __class__ . Inoltre, puoi muck con __class__ in un modo che non puoi distruggere con type() .

>>> class Z(object):
...     def __getattribute__(self, name):
...             return "ham"
... 
>>> z = Z()
>>> z.__class__
'ham'
>>> type(z)
<class '__main__.Z'>

Personalmente, di solito ho un ambiente con solo classi di nuovo stile, e come questione di stile preferisco usare type() perché generalmente preferisco le funzioni built-in quando esistono per usare gli attributi magici. Ad esempio, preferirei anche bool(x) a x.__nonzero__() .





language-features