Domanda

Sembra che spesso i metodi __init__ siano simili a questo:

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

Esiste un modo per trasformare gli argomenti in un elenco (senza ricorrere a * args o ** kwargs ) e quindi usare setattr in impostare le variabili di istanza, con il nome del parametro e l'argomento passato? E forse tagliare la lista, ad es. dovresti almeno tagliarlo in [1:] perché non vuoi self.self .

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

in questo modo:

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!

Rispondendo alla risposta di Unknown, ho trovato che funzionava:

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 ..

È stato utile?

Soluzione

Ecco qua. Sì, questo è un brutto trucco malvagio. Sì, l'oggetto necessita di una variabile __dict__. Ma hey, è una piccola fodera ordinata!

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']

Includerà anche self, ma puoi facilmente eliminarlo o fare la tua funzione di aggiornamento che ignora self.

Altri suggerimenti

Puoi usare inspect.getargspec e incapsularlo come decoratore. La ricerca di argomenti opzionali e parole chiave è un po 'complicata, 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__

Puoi quindi usarlo 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)

Non esiste un buon modo per ottenere gli argomenti come un elenco se sono specificati singolarmente nella firma della funzione. Probabilmente puoi fare qualcosa con gli hack di ispezione o frame, ma sarà più brutto della semplice spiegazione come hai fatto.

Prova inspect.getargspec :

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

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

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

Verifica se il nuovo namedtuple (nuovo in Python 2.6) dal modulo collezioni potrebbe funzionare per te.

Puoi farlo usando l'introspezione degli argomenti, ma il codice sarà più lungo del codice che provi a sostituire. Soprattutto se stai gestendo kw, cosa che potresti dover fare.

Questo codice funzione funziona nella maggior parte dei casi (migliorato dall'esempio di Unknowns):

>>> 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, che rende il codice meno leggibile per nessun motivo particolare tranne la pigrizia, quindi non farlo. Inoltre, con quale frequenza veramente hai classi che hanno più di 5-6 parametri init?

Mi piace di più quella forma, non troppo lunga e sia incollabile che sub-classificabile:

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)

Che ne dici di derivare da una classe speciale? Penso che sia più esplicito e più flessibile in 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)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top