2013-07-11 10 views
16

Ho visto due diversi oggetti Python usati per raggruppare insieme dati arbitrari: classi e funzioni vuote.Perché le classi e le funzioni vuote di Python funzionano come contenitori di dati arbitrari, ma non altri oggetti?

def struct(): 
    pass 

record = struct 
record.number = 3 
record.name = "Zoe" 


class Struct: 
    pass 

record = Struct() 
record.number = 3 
record.name = "Zoe" 

Anche se la classe non è vuota, sembra funzionare così a lungo come è definito in fase di esecuzione.

Ma quando sono diventato arrogante e ho provato a farlo con funzioni o classi incorporate, non ha funzionato.

record = set() 
record.number = 3 
AttributeError: 'set' object has no attribute 'number' 

record = pow 
pow.number = 3 
AttributeError: 'builtin_function_or_method' object has no attribute 'number' 

C'è una differenza fondamentale tra incorporato e classi e funzioni "su misura" che tenga conto di questo comportamento?

+0

C'è un bug nel primo esempio: Dovrebbe essere 'registrazione = struct' tecnico senza il'() ', altrimenti 'record' sarà' None' e la riga successiva genererà un'eccezione. Inoltre, si dovrebbe notare che l'uso delle funzioni funziona solo perché una funzione definita dall'utente è solo un altro oggetto in python, il che significa che è possibile assegnare attributi arbitrari. Se la funzione è vuota o no, non ha nulla a che fare con esso. Ma mentre probabilmente si può usare questo per qualche funky metafora/programmazione dinamica, non riesco a pensare a un motivo per cui l'uso di una funzione come contenitore di memoria sarebbe preferibile su una classe ... – l4mpi

+0

@ l4mpi Penso che sia utile se si vogliono emulare chiusure che "scrivono" nel loro ambito racchiudente in Python 2.x, prima di 'nonlocal'. Si utilizzano attributi ad-hoc dell'oggetto funzione locale invece di vars locali. – millimoose

+0

Oops, hai ragione riguardo al primo esempio. –

risposta

6

La differenza è che entrambi gli oggetti di funzione e l'oggetto struct avere un attributo __dict__, ma le istanze set e le funzioni integrate non:

>>> def struct(): 
...  pass 
... 
>>> record = struct 
>>> record.number = 2 
>>> struct.__dict__ 
{'number': 2} 
>>> class Struct: 
...  pass 
... 
>>> record = Struct() 
>>> record.number = 3 
>>> record.__dict__ 
{'number': 3} 
>>> record=set() 
>>> record.__dict__ 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'set' object has no attribute '__dict__' 
>>> pow.__dict__ 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'builtin_function_or_method' object has no attribute '__dict__' 

è possibile emulare il behavour utilizzando gli slot (anche se solo su classi di nuovo stile):

>>> class StructWithSlots(object): 
...  __slots__ = [] 
... 
>>> record = StructWithSlots() 
>>> record.number = 3 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'StructWithSlots' object has no attribute 'number' 
>>> record.__dict__ 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'StructWithSlots' object has no attribute '__dict__' 
2

Alcuni built-in possono essere più restrittivi. Inoltre, le classi implementate con gli slot non accettano attributi arbitrari.

3

I tipi predefiniti sono scritti in C e non possono essere modificati in questo modo. Ma dopo che lo type/class unification è stato introdotto in py2.2 ora puoi ereditare da un tipo predefinito e sovrascrivere o aggiungere i tuoi attributi in quella sottoclasse.

È possibile utilizzare il pacchetto forbiddenfood per aggiungere attributi a built-in tipi:

Questo progetto mira a dare il modo per trovare il cielo nei test, ma potrebbe condurvi a all'inferno se lo usi sul codice di produzione.

>>> from forbiddenfruit import curse 
>>> def words_of_wisdom(self): 
...  return self * "blah " 
>>> curse(int, "words_of_wisdom", words_of_wisdom) 
>>> assert (2).words_of_wisdom() == "blah blah " 

E naturalmente, se sei presuntuoso sufficiente quindi è possibile creare i propri tipi in C e aggiungere tali caratteristiche ad esso.

+4

Certamente si può creare un tipo in C che può essere usato così? – delnan

+0

@delnan E.g. il tipo 'object'. Potrebbe essere interessante immergersi nella sorgente di Python quale è il caso speciale - permettendo l'expando di oggetti o prevenendoli. – millimoose

+0

@delnan Ma non lo chiamerei un tipo built-in perché scrivo il tuo tipo in C (non è una libreria standard). La domanda riguarda le classi built-in e personalizzate. –

1

Se si desidera una protezione simile nella propria classe, è possibile utilizzare il metodo __setattr__().

class TestClass(object): 
    # Accept the attributes in this list 
    __valid_attributes = ["myattr1", "myattr2"] 

    def __setattr__(self, name, value): 
     if not name in TestClass.__valid_attributes: 
      raise AttributeError(
       "{0} has no attribute '{1}'".format(self.__class__.__name__, name)) 
     self.__dict__[name] = value 

ora si può fare qualcosa di simile:

t = TestClass() 
t.noattr = "test" # AttributeError: TestClass has no attribute 'noattr' 

Ma "attributi validi" possono ancora essere impostati:

t = TestClass() 
t.myattr1 = "test" 
print(t.myattr1) # test 
+2

Congratulazioni, hai appena reinventato la ruota ['__slots__'] (http://docs.python.org/2/reference/datamodel.html#slots). – RoadieRich

+0

non sapeva di __slots__, grazie per il tuo commento – joente

Problemi correlati