[Python] creazione attributi di classe da dizionario

Marco Buttu mbuttu a oa-cagliari.inaf.it
Dom 20 Ott 2013 09:36:48 CEST


On 10/19/2013 12:01 PM, Manlio Perillo wrote:
> On 19/10/2013 09:32, Riccardo mancuso wrote:
>> ciao a tutti,
>> volevo chiedere se č possibile creare degli attributi di una classe,
>> partendo da un dizionario.
>
> Si, č possibile.
> Gli attributi di una classe sono "normalmente" memorizzati del 
> dizionario __dict__.
>
>> Mi spiego:
>> supponiamo di avere un dizionario del tipo:
>>
>> dizionario={"auto":1,
>>              "casa":2,
>>              "moto":3}
>>
>> e poi di creare una classe lista_oggetti:
>>
>> class lista_oggetti(dizionario):
>>      def __init__(self,dizionario):
>>          pass
>>
>
> Ti basta fare:
>
>         self.__dict__.update(dizionario) 

Riportando la domanda dello OP:

# --- BEGIN OP
class lista_oggetti(dizionario):
     def __init__(self,dizionario):
         pass

vorrei fare in modo che vengano creati automaticamente gli attributi 
specificati in dizionario, cioč:

lista_oggetti.auto
lista_oggetti.casa
lista_oggetti.moto
# --- END OP

sembra che voglia creare degli attributi _di classe_ piuttosto che di 
istanza. Una precisazione quindi (per lui ovviamente, non per te ;) )
Se fai come ha suggerito Manlio, osserva che crei degli attributi _di 
istanza_, non di classe:

 >>> class Foo:
...     def __init__(self, d):
...         self.__dict__.update(d)
...
 >>> f = Foo({'foo': 100})
 >>> f.foo
100
 >>> Foo.foo
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute 'foo'

Se vuoi creare degli attributi _di classe_, e vuoi farlo dopo che la 
classe e' stata creata, puoi fare ad esempio:

 >>> class Foo:
...     def __init__(self, d):
...         for k, v in d.items():
...             setattr(type(self), k, v) # Vedi sotto la nota sul 
perche' ho usato setattr()
...
 >>> f = Foo({'foo': 100})
 >>> f.foo
100
 >>> Foo.foo
100

Se invece vuoi aggiungere gli attributi prima della creazione della 
class Foo, perche' vuoi farlo prima che __new__() trasformi in dizionari 
in mappingproxy, o per altri motivi, allora devi necessariamente usare 
la soluzione con metaclasse, che ti ho proposto prima.

Alcune precisazioni sul perche' ho usato setattr(). In Python 2 avresti 
potuto fare:

 >>> class Foo:
...     def __init__(self, d):
...         Foo.__dict__.update(d)
...
 >>> f = Foo({'foo': 100})
 >>> f.foo
100
 >>> Foo.foo
100

Infatti in Python 2 Foo.__dict__ e' un normale dizionario ordinario 
(tipo dict):

 >>> sys.version_info
sys.version_info(major=2, minor=7, micro=3, releaselevel='final', serial=0)
 >>> type(Foo.__dict__)
<type 'dict'>

In Python 3 pero' Foo.__dict__ non e' un dizionario ordinario, ma un 
oggetto di tipo types.MappingProxyType:

 >>> import sys
 >>> sys.version_info
sys.version_info(major=3, minor=3, micro=2, releaselevel='final', serial=0)
 >>> type(Foo.__dict__)
<class 'mappingproxy'>
 >>> import types
 >>> type(Foo.__dict__) is types.MappingProxyType
True

Questo oggetto e' un descriptor:

 >>> type(Foo.__dict__['__dict__'])
<class 'getset_descriptor'>

e il suo metodo __set__() ci impedisce di fare, nello stesso identico 
modo, quanto abbiamo fatto sopra con Python 2:

 >>> class Foo:
...     def __init__(self, d):
...         Foo.__dict__.update(d)
...
 >>> f = Foo({'foo': 100})
Traceback (most recent call last):
     ...
AttributeError: 'mappingproxy' object has no attribute 'update'

E non possiamo fare neppure un assegnamento:

 >>> class Foo:
...     def __init__(self, d):
...         for k, v in d.items():
...             Foo.__dict__[k] = v
...
 >>> f = Foo({'foo': 100})
Traceback (most recent call last):
     ...
TypeError: 'mappingproxy' object does not support item assignment

Questo e' il motivo per cui ho usato setattr(): usando setattr() il 
codice funziona sia in Python 2 che in Python 3.

-- 
Marco Buttu

INAF - Osservatorio Astronomico di Cagliari
Via della Scienza, Loc. Cuccuru Angius
09047 Selargius (CA)
Phone: 070 711 80 217
Email: mbuttu a oa-cagliari.inaf.it



Maggiori informazioni sulla lista Python