2010-03-25 11 views
7

Sono molto nuovo in Python e sto cercando di capire come creare un oggetto con valori accessibili sia per nome attributo, sia per indice. Ad esempio, il modo in cui os.stat() restituisce un stat_result o pwd.getpwnam() restituisce struct_passwd.Struttura accessibile per nome attributo o opzioni indice

Nel tentativo di capirlo, ho incontrato solo implementazioni C dei tipi di cui sopra. Niente specificamente in Python. Qual è il modo nativo di Python per creare questo tipo di oggetto?

Mi scuso se questo è già stato ampiamente trattato. Nella ricerca di una risposta, mi manca il concetto fondamentale che mi impedisce di trovare una risposta.

risposta

3

Non è possibile utilizzare la stessa implementazione come oggetto risultato di os.stat() e altri. Comunque Python 2.6 ha una nuova funzione di fabbrica che crea un tipo di dati simile chiamato tupla. Una tupla con nome è una tupla i cui slot possono anche essere indirizzati per nome. La tupla chiamata non dovrebbe richiedere più memoria, secondo la documentazione, rispetto a una tupla normale, poiché non hanno un dizionario per istanza. La firma funzione di fabbrica è:

collections.namedtuple(typename, field_names[, verbose]) 

Il primo argomento specifica il nome del nuovo tipo, il secondo argomento è una stringa (spazio o separati da virgola) che contiene i nomi dei campi e, infine, se verbose è vero, il la funzione factory stamperà anche la classe generata.

Esempio

Supponiamo di avere una tupla contenente un nome utente e una password.Per accedere al nome utente che si ottiene l'elemento in posizione zero e la password si accede alla posizione uno:

credential = ('joeuser', 'secret123') 
print 'Username:', credential[0] 
print 'Password:', credential[1] 

Non c'è niente di sbagliato in questo codice, ma la tupla non è auto-documentazione. Devi trovare e leggere la documentazione sul posizionamento dei campi nella tupla. Qui è dove tuple può venire in soccorso. Siamo in grado di ricodificare l'esempio precedente come segue:

import collections 
# Create a new sub-tuple named Credential 
Credential = collections.namedtuple('Credential', 'username, password') 

credential = Credential(username='joeuser', password='secret123') 

print 'Username:', credential.username 
print 'Password:', credential.password 

Se sei interessato di ciò che il codice è simile per la credenziale di tipo appena creata è possibile aggiungere verbose = True alla lista degli argomenti durante la creazione del tipo, in questo caso particolare si ottiene il seguente risultato:

import collections 
Credential = collections.namedtuple('Credential', 'username, password', verbose=True) 

class Credential(tuple):          
    'Credential(username, password)'      

    __slots__ =() 

    _fields = ('username', 'password') 

    def __new__(_cls, username, password): 
     return _tuple.__new__(_cls, (username, password)) 

    @classmethod 
    def _make(cls, iterable, new=tuple.__new__, len=len): 
     'Make a new Credential object from a sequence or iterable' 
     result = new(cls, iterable)         
     if len(result) != 2:           
      raise TypeError('Expected 2 arguments, got %d' % len(result)) 
     return result 

    def __repr__(self): 
     return 'Credential(username=%r, password=%r)' % self 

    def _asdict(t): 
     'Return a new dict which maps field names to their values' 
     return {'username': t[0], 'password': t[1]} 

    def _replace(_self, **kwds): 
     'Return a new Credential object replacing specified fields with new values' 
     result = _self._make(map(kwds.pop, ('username', 'password'), _self)) 
     if kwds: 
      raise ValueError('Got unexpected field names: %r' % kwds.keys()) 
     return result 

    def __getnewargs__(self): 
     return tuple(self) 

    username = _property(_itemgetter(0)) 
    password = _property(_itemgetter(1)) 

la tupla denominato non si limita a fornire accesso ai campi di nome, ma contiene anche funzioni di supporto come la funzione _make() che aiuta a creare un'istanza credenziali da una sequenza o iterabile. Per esempio:

cred_tuple = ('joeuser', 'secret123') 
credential = Credential._make(cred_tuple) 

La documentazione della libreria Python per namedtuple ha più informazioni e esempi di codice, quindi suggerisco che si take a peek.

5

Python 2.6 ha introdotto collections.namedtuple per semplificare l'operazione. Con le versioni precedenti di Python puoi usare lo named tuple recipe.

Citando direttamente dalla documentazione:

>>> Point = namedtuple('Point', 'x y') 
>>> p = Point(11, y=22)  # instantiate with positional or keyword arguments 
>>> p[0] + p[1]    # indexable like the plain tuple (11, 22) 
33 
>>> x, y = p    # unpack like a regular tuple 
>>> x, y 
(11, 22) 
>>> p.x + p.y    # fields also accessible by name 
33 
>>> p      # readable __repr__ with a name=value style 
Point(x=11, y=22) 
0

un oggetto che ha valori che sono accessibili sia dal nome di attributo, o per indice

io non sono sicuro di quello che sei trovando difficile su questo.

Una raccolta accessibile tramite gli strumenti indice __getitem__.

Una raccolta accessibile con nomi __getattr__ (o __getattribute__).

È possibile implementare entrambi senza alcun problema. Oppure, è possibile utilizzare namedtuple.

per rendere la vita più semplice, è possibile estendere la classe tuple in modo da non dover implementare il proprio __getitem__. Oppure puoi definire una classe ordinaria che abbia anche __getitem__ così non hai dovuto fare i conti con __getattr__.

Per esempio

>>> class Foo(object): 
...  def __init__(self, x, y, z): 
...   self.x= x 
...   self.y= y 
...   self.z= z 
...  def __getitem__(self, index): 
...   return { 0: self.x, 1: self.y, 2: self.z }[index] 
... 
>>> f= Foo(1,2,3) 
>>> f.x 
1 
>>> f[0] 
1 
>>> f[1] 
2 
>>> f[2] 
3 
>>> f.y 
2 
Problemi correlati