2009-09-14 19 views
5

Es scheint, dass oft __init__ Methoden dies ähnlich sind:Python __init__ setattr auf Argumenten?

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

Gibt es irgendwie, die Argumente in eine Liste zu machen (ohne zu *args oder **kwargs greifen) und dann setattr die Instanzvariablen zu setzen verwenden, mit der Name des Parameters und das übergebene Argument? Und vielleicht die Liste zerteilen, z. Sie müssten es zumindest zu [1:] in Scheiben schneiden, weil Sie self.self nicht wollen.

(eigentlich denke ich, es wäre ein Wörterbuch sein müssen, den Namen und den Wert zu halten)

wie folgt aus:

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

Dank!

Unbekannte Antwort reagiert, fand ich dies funktioniert:

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

oder

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

Nicht ..

zu schlecht
+1

Betrogene http://stackoverflow.com/questions/1389180/python-automatisch-initialize-instance-variables – SilentGhost

+1

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

Antwort

7

Hier gehen Sie. Ja, das ist ein hässlicher böser Hack. Ja, das Objekt benötigt eine __dict__ Variable. Aber hey, es ist ein nettes kleines Liner!

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

Der Konstruktor kann jede Art von Argumenten annehmen.

Es wird auch Selbst enthalten, aber Sie können das leicht löschen oder Ihre eigene Update-Funktion machen, die sich selbst ignoriert.

+0

Das macht nicht, was das OP wollte: Es braucht überhaupt keine Argumente! –

+1

@Ned, tut es ... Sie können jeden beliebigen Argumenttyp angeben. – Unknown

+2

Das wird alle lokalen Variablen als Attribute setzen, speziell "selbst", was irgendwie absurd ist. :) Man kann natürlich zuerst "selbst" aus den Einheimischen filtern(), aber dann wird es schon weniger ordentlich. –

4

Es gibt keine gute Möglichkeit ist es, die Argumente als ein zu erhalten Liste, wenn sie einzeln in der Funktionssignatur angegeben sind. Sie können wahrscheinlich etwas mit Inspect- oder Frame-Hacks machen, aber das wird hässlicher sein, als es einfach so zu schreiben, wie Sie es getan haben.

+0

Es kann nicht selbst wenn man die Funktion oder die Codeobjekte betrachtet. Während Sie etwas Fieses tun könnten, indem Sie annehmen, dass der erste 'co_argcount' von' co_varnames' die Argumente sind, wird dies angesichts von Sequenz-Entpacken oder * args/** kwargs nicht wahr sein. – bobince

+0

Ned, meine Methode funktioniert für jede Methodensignatur, ohne auf Inspect- oder Frame-Hacks zurückgreifen zu müssen. – Unknown

2

Sehen Sie, ob das neue namedtuple (neu in Python 2.6) aus dem Collections-Modul für Sie arbeiten könnte.

3

Versuchen inspect.getargspec:

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

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

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

könnten Sie verwenden inspect.getargspec und es als Dekorateur einzukapseln. Das Nachschlagen von optionalen und Keyword-Argumente ist ein bisschen schwierig, aber das sollte es tun:

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__ 

Sie dann es wie folgt verwenden:

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

Sie können es tun Selbstbeobachtung der Argumente verwenden, aber der Code wird länger sein als der Code, den Sie ersetzen möchten. Vor allem, wenn Sie kw behandeln, was Sie tun müssen.

Dieser kurze Code funktioniert in den meisten Fällen (von Unknowns Beispiel verbessert):

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

Aber es ist ein hässlicher Hack, Code machte weniger lesbar ohne besonderen Grund außer Faulheit, so tut es nicht. Und wie oft haben Sie wirklich Klassen, die mehr als 5-6 Init-Parameter haben?

0

Wie wäre es mit einer speziellen Klasse? Ich denke, es ist expliziter und flexibler auf diese Weise:

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

Ich mag, dass die meisten bilden, nicht zu lang und sowohl kopier pasteable und Unter 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) 
Verwandte Themen