2009-09-14 13 views
5

Sembra che spesso __init__ metodi sono simili a questo:Python __init__ setattr su argomenti?

def __init__(self, ivar1, ivar2, ivar3): 
    self.ivar1 = ivar1 
    self.ivar2 = ivar2 
    self.ivar3 = ivar3 

C'è qualche modo per trasformare gli argomenti in una lista (senza ricorrere a *args o **kwargs) e quindi utilizzando setattr per impostare le variabili di istanza, con la nome del parametro e argomento passato? E magari taglia l'elenco, ad es. avresti bisogno almeno di affettarlo a [1:] perché non vuoi self.self.

(in realtà credo che avrebbe bisogno di essere un dizionario per contenere il nome e il valore)

come questo:

def __init__(self, ivar1, ivar2, ivar3, optional=False): 
    for k, v in makedict(self.__class__.__init__.__args__): # made up __args__ 
     setattr(self, k, v) 

Grazie!

In risposta alla risposta di Unknown, ho trovato questo lavoro:

Class A(object): 
    def __init__(self, length, width, x): 
     self.__dict__.update(dict([(k, v) for k, v in locals().iteritems() if k != 'self'])) 

o

Class A(object): 
    def __init__(self, length, width, x): 
     self.__dict__.update(locals()) 
     del self.__dict__['self'] 
Non

troppo male ..

+1

vittima http://stackoverflow.com/questions/1389180/python-automatically-initialize-instance-variables – SilentGhost

+1

vedere anche: http://stackoverflow.com/questions/739625/setattr-with-kwargs-pythonic-or-not – Imran

risposta

7

Ecco qui. Sì, questo è un brutto trucco. Sì, l'oggetto ha bisogno di una variabile __dict__. Ma hey, è una piccola nave elegante!

def __init__(self): 
    self.__dict__.update(locals()) 

Il costruttore può accettare qualsiasi tipo di argomento.

class test(object): 
    def __init__(self, a, b, foo, bar=5)... 

a = test(1,2,3) 
dir(a) 

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'foo', 'bar', 'self'] 

Esso comprenderà anche auto, ma si può facilmente eliminare tale o fare la propria funzione di aggiornamento che ignora sé.

+0

Questo non fa ciò che l'OP voleva: non ci vuole alcun argomento! –

+1

@Ned, lo fa ... È possibile specificare qualsiasi tipo di argomento desiderato. – Unknown

+2

Questo imposterà tutte le variabili locali come attributi, in particolare "self", il che è piuttosto assurdo. :) Puoi ovviamente filtrare "self" dai locals() per primo, ma poi è già diventato meno accurato. –

4

Non v'è alcun buon modo per ottenere gli argomenti come elencare se sono specificati singolarmente nella firma della funzione. Probabilmente puoi fare qualcosa con ispezioni o frame hack, ma ciò sarà più brutto di una semplice spiegazione come hai fatto tu.

+0

Non può essere fatto anche guardando gli oggetti funzione o codice. Mentre si potrebbe fare qualcosa di brutto come supponendo che il primo 'co_argcount' di' co_varnames' siano gli argomenti, questo non sarà vero di fronte alla decompressione della sequenza o * args/** kwargs. – bobince

+0

Ned, il mio metodo funziona per qualsiasi firma del metodo senza dover ricorrere a ispezionare o inquadrare gli hack. – Unknown

2

Vedere se il nuovo namedtuple (nuovo in Python 2.6) dal modulo delle raccolte potrebbe funzionare per voi.

3

Prova inspect.getargspec:

In [31]: inspect.getargspec(C.__init__) 

Out[31]: ArgSpec(args=['self', 'ivar1', 'ivar2', 'ivar3', 'optional'], 

       varargs=None, keywords=None, defaults=(False,)) 
6

Si potrebbe utilizzare inspect.getargspec e incapsulare come un decoratore. La ricerca di argomenti opzionali e parola chiave è un po 'complicato, ma questo dovrebbe farlo:

def inits_args(func): 
    """Initializes object attributes by the initializer signature""" 
    argspec = inspect.getargspec(func) 
    argnames = argspec.args[1:] 
    defaults = dict(zip(argnames[-len(argspec.defaults):], argspec.defaults)) 
    @functools.wraps(func) 
    def __init__(self, *args, **kwargs): 
     args_it = iter(args) 
     for key in argnames: 
      if key in kwargs: 
       value = kwargs[key] 
      else: 
       try: 
        value = args_it.next() 
       except StopIteration: 
        value = defaults[key] 
      setattr(self, key, value) 
     func(self, *args, **kwargs) 
    return __init__ 

È quindi possibile utilizzare in questo modo:

class Foo(object): 
    @inits_args 
    def __init__(self, spam, eggs=4, ham=5): 
     print "Foo(%r, %r, %r)" % (self.spam, self.eggs, self.ham) 
2

è possibile farlo utilizzando l'introspezione degli argomenti, ma il codice sarà più lungo del codice che proverai a sostituire. Soprattutto se stai gestendo kw, che potresti dover fare.

Questo codice funziona nella maggior parte dei casi (in miglioramento rispetto incognite esempio):

>>> class Foo: 
... def __init__(self, labamba, **kw): 
...  params = locals().copy() 
...  del params['self'] 
...  if 'kw' in params: 
...   params.update(params['kw']) 
...   del params['kw'] 
...  self.__dict__.update(params) 

Ma è un brutto hack, rendendo il codice meno leggibile per nessun motivo particolare, tranne la pigrizia, in modo da non farlo. E inoltre, quanto spesso hai classi che hanno più di 5-6 parametri iniziali?

0

Cosa ne pensi di una classe speciale? Penso che sia più esplicito e più flessibile questo modo:

class InitMe: 
    def __init__(self, data): 
     if 'self' in data: 
      data = data.copy() 
      del data['self'] 
     self.__dict__.update(data) 


class MyClassA(InitMe): 
    def __init__(self, ivar1, ivar2, ivar3 = 'default value'): 
     super().__init__(locals()) 


class MyClassB(InitMe): 
    def __init__(self, foo): 
     super().__init__({'xxx': foo, 'yyy': foo, 'zzz': None}) 
# or super().__init__(dict(xxx=foo, yyy=foo, zzz=None)) 

class MyClassC(InitMe): 
    def __init__(self, foo, **keywords): 
     super().__init__(keywords) 
1

mi piace che formano la maggior parte, non troppo lungo ed entrambi copia-pasteable e sub-classable:

class DynamicInitClass(object): 
     __init_defargs=('x',) 
     def __init__(self,*args,**attrs): 
     for idx,val in enumerate(args): attrs[self.__init_defargs[idx]]=val 
     for key,val in attrs.iteritems(): setattr(self,key,val) 
Problemi correlati