2009-07-02 11 views
17

In Python, è illegale per creare nuovo attributo per un'istanza di un oggetto come questoPython Lingua Domanda: gli attributi di oggetto() vs Funzione

>>> a = object() 
>>> a.hhh = 1 

getta

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'object' object has no attribute 'hhh' 

Tuttavia, per una funzione oggetto, è OK.

>>> def f(): 
... return 1 
... 
>>> f.hhh = 1 

Qual è la logica alla base di questa differenza?

+0

+1, ho appena era circa per fare esattamente la stessa domanda, ma poi ricordati di cercarne prima uno esistente! Buone risposte anche qui. – Edmund

+0

ottima domanda, sto riscontrando un problema simile –

+0

possibile duplicato di [Impossibile impostare gli attributi della classe dell'oggetto] (http://stackoverflow.com/questions/1529002/cant-set-attributes-of-object-class) –

risposta

22

Gli oggetti funzione di motivazione supportano attributi arbitrari è che, prima di aggiungere questa funzione, diversi framework (ad esempio quelli del parser generator) stavano abusando delle docstring di funzione (e altri attributi di oggetti funzione) per eliminare informazioni per funzione cruciali per loro - la necessità di tale associazione di attributi arbitrari con nomi di oggetti funzione dimostrati dall'esempio, supportandoli direttamente nella lingua piuttosto che punting e lasciando che le docstring siano abusate, era abbastanza ovvio.

Per supportare esempio arbitrario attribuisce un tipo deve fornire ognuno dei suoi casi con un __dict__ - che è un grosso problema per le funzioni (che non sono mai oggetti molto piccoli in ogni caso), ma potrebbe anche essere per altri oggetti destinati a essere piccolo Rendendo il tipo object il più leggero possibile e fornendo anche __slots__ per consentire di evitare l'istanza __dict__ in sottotipi di object, abbiamo supportato i tipi di valore "piccolo" specializzato al meglio delle nostre possibilità.

+1

Grazie Alex, per questa vista interna di * oggetto *. Spero che il guadagno nello spazio sia valso lo sforzo. Trovo ancora contro-intuitivo che (a) la classe base del sistema oggetto non supporta una delle caratteristiche di base di OO - attributi di istanza; e (b) questa caratteristica nasce all'improvviso con qualsiasi classe derivata non sensoriale * classe Foo (oggetto): pass *, che mi ritrovo ad usare più spesso solo per ottenere attributi di istanza. – ThomasH

+2

@Thomas, sei altrettanto sorpreso che Object in Java "non supporta una delle funzionalità di base di OO" esattamente allo stesso modo? Le sue istanze dirette non hanno nemmeno attributi di istanza. C# è lo stesso, credo. Poiché (e questo è VERAMENTE una caratteristica di base di OO: Principio di Liskov!) Nessuna sottoclasse può togliere qualcosa che è nella superclasse, per soddisfare la tua intuizione sarebbe in realtà a costo di memoria infinita: un ditt dovrebbe quindi avere un __dict ... dovrebbe ovviamente avere il proprio __dict __... e non può MAI, MAI fermarsi. La tua intuizione ti induce in errore * A LOT *! -) –

+1

@Alex Ok, ho sentito. Ma forse tutto ciò sarebbe possibile se i \ _ \ _ dict \ _ \ _ di oggetti magri fossero pigri ?! In realtà, potrei immaginare di voler annotare il \ _ \ _ dict \ _ \ _ di un \ _ \ _ dict \ _ \ _ di un'istanza * object * con un piccolo attributo, e sarebbe abbastanza perfetto per me se questo \ _ \ _dict \ _ \ _ sorgerebbe solo quando lo assegnerò per la prima volta. Come è? – ThomasH

7

Alex Martelli ha inviato una risposta straordinaria alla tua domanda. Per chi è alla ricerca di un buon modo per realizzare gli attributi arbitrari su un oggetto vuoto, fare questo:

class myobject(object): 
    pass 

o = myobject() 
o.anything = 123 

O, più efficiente (e meglio documentate) se si conoscono gli attributi:

class myobject(object): 
    __slots__ = ('anything', 'anythingelse') 

o = myobject() 
o.anything = 123 
o.anythingelse = 456 
+0

Sei sicuro che la prima strada funzioni? L'ho provato e non penso che funzioni. –

1

Il la logica è che un'istanza di object() è un caso speciale degenerato. "È" un oggetto ma non è progettato per essere utile da solo.

Pensa allo object come un trucco temporaneo, collegando tipi e classi vecchio stile. In Python 3.0 svanirà nel dimenticatoio, perché non sarà più utilizzato come parte di

class Foo(object): 
    pass 

f = Foo() 
f.randomAttribute = 3.1415926 
+0

L'uso reale principale dell'oggetto è nell'idioma 'sentinel = object()'. Sarebbe modesto avere a portata di mano un "objectwithadict" piuttosto che dover creare nuove classi per lo scopo, per esempio ;-). –

+0

objectwithadict - essenzialmente 'class ObjectWithADict (oggetto): pass'? –

0

Ecco un'altra alternativa, il più breve, come ho potuto farlo:

>>> dummy = type('',(), {})() 
>>> dummy.foo = 5 
>>> dummy.foo 
5 
Problemi correlati