2015-07-22 15 views
7

Voglio creare una classe in Python con vari attributi e metodi, ma per poter ereditare la funzionalità di una lista, in modo da poter aggiungere oggetti all'oggetto stesso, non a nessuno dei suoi attributi. Voglio poter dire "graph[3]" anziché "graph.node_list[3]". C'è un modo per fare questo?Come si crea una classe che è anche una lista?

+3

Fornire un ['__getitem__'] (https://docs.python.org/2/reference/datamodel.html#object.__getitem__) – NightShadeQueen

risposta

10

Tutto ciò che dovete fare è fornire un Addendum __getitem__

In [1]: class Foo: 
    ...:  def __init__(self, *args): 
    ...:   self.args = args 
    ...:  def __getitem__(self, i): 
    ...:   return self.args[i] 
    ...:  

In [2]: c = Foo(3,4,5) 

In [3]: c[2] 
Out[3]: 5 

In [4]: c[3] 
IndexError: tuple index out of range #traceback removed for brevity 

In [5]: for i in c: print(i) #look, ma, I can even use a for-loop! 
3 
4 
5 

: Ci sono altri metodi probabilmente avrete bisogno di fornire, anche. __len__ è sicuramente uno di questi. C'è una lista piuttosto lunga di metodi magici, io consiglierei di esaminarli e scegliere quelli che hanno senso.

+0

Uno può anche derivare da' abc.Sequence' per placare i controlli di tipo. O almeno implementa '__len__' ... – refi64

+0

@ kirbyfan64sos, Grazie alla sottoclasse virtuale, non è necessario ereditare da nessuno dei' collections' 'abc's, a meno che non si voglia ereditare i loro metodi. – Navith

+1

@Navith Mi riferivo a dama tipo Mypy. :) – refi64

5

Si può solo ereditare da list:

class Foo(list): 
    def __init__(self): 
     pass 

Ma sottoclasse tipi built non è necessariamente una buona idea.


collections.abc.Sequence (o dal 3.5, typing.Sequence[T]) è il modo in cui lo farei.

+1

"Questa non è necessariamente una buona idea." - Ti stai riferendo al primo approccio o al secondo? Inoltre, perché non è una buona idea? – alfasin

+0

@alfasin i tipi di sottoclassi di built-in non è di solito una buona idea. – o11c

+1

perché non è una buona idea? – alfasin

1

Poiché Python utilizza duck typing, la differenza principale tra un elenco e qualsiasi altro oggetto è l'insieme di metodi che espone. È possibile visualizzare facilmente gli attributi principali (metodi e campi) di un oggetto con la funzione dir().

>>> [a for a in dir([]) if callable(getattr([], a))] 
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', 
'__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', 
'__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', 
'__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
'__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', 
'__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 
'insert', 'pop', 'remove', 'reverse', 'sort'] 

Ovviamente c'è un sacco di metodi di qui, e probabilmente non si preoccupano più di loro. Ma se il tuo obiettivo era quello di replicare realmente il comportamento di un elenco, probabilmente vorresti implementarlo. La maggior parte dei metodi __...__ sono definiti in Python data model, se si desidera visualizzarli.

Da quella pagina:

sequenze mutabili dovrebbero fornire metodi append(), count(), index(), extend(), insert(), pop(), remove(), reverse() e sort(), come Python oggetti lista standard. Infine, i tipi di sequenza dovrebbero implementare l'aggiunta (che significa concatenazione) e la moltiplicazione (ovvero la ripetizione) definendo i metodi __add__(), __radd__(), __iadd__(), __mul__(), __rmul__() e __imul__() descritti di seguito; non dovrebbero definire altri operatori numerici. Si raccomanda che sia i mapping che le sequenze implementino il metodo __contains__() per consentire un uso efficiente dell'operatore in; per i mapping, dovrebbe cercare le chiavi del mapping; per le sequenze, dovrebbe cercare tra i valori. Si raccomanda inoltre che sia le mappature che le sequenze implementino il metodo __iter__() per consentire un'iterazione efficiente attraverso il contenitore; per i mapping, __iter__() dovrebbe essere uguale a keys(); per le sequenze, dovrebbe scorrere i valori.

La funzione che stai chiedendo in particolare, le ricerche dell'indice, è fornita da __getitem__. Suggerirei anche di implementare __contains__() e __iter__() (e forse __len__()), come minimo.

Problemi correlati