2009-09-14 12 views
5

Wydaje się, że często __init__ metody są podobne do tego:Python __init__ setattr na argumenty?

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

Czy istnieje jakiś sposób, aby włączyć argumenty do listy (bez uciekania się do *args lub **kwargs), a następnie za pomocą setattr ustawić zmienne instancji, przy czym nazwa parametru i przekazany argument? I może pokroić listę, np. musisz go przynajmniej przekroić na [1:], ponieważ nie chcesz self.self.

(w rzeczywistości myślę, że to musi być słownikiem posiadać nazwę i wartość)

tak:

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

Dzięki!

Reagowanie na odpowiedź Bezimiennego, znalazłem to do pracy:

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

lub

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

Nie jest tak źle ..

+1

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

+1

zobacz także: http://stackoverflow.com/questions/739625/setattr-with-kwargs-pythonic-or-not – Imran

Odpowiedz

7

Proszę bardzo. Tak, to jest brzydkie zło. Tak, obiekt potrzebuje zmiennej __dict__. Ale hej, to schludny, mały liniowiec!

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

Konstruktor może przyjmować dowolne argumenty.

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

Będzie zawierać także self, ale można go łatwo usunąć lub utworzyć własną funkcję aktualizacji, która ignoruje siebie.

+0

To nie robi tego, co OP chciał: nie ma żadnych argumentów! –

+1

@Ned, to robi ... Możesz określić dowolny typ argumentu, który chcesz. – Unknown

+2

To ustawi wszystkie zmienne lokalne jako atrybuty, szczególnie "self", co jest absurdem. :) Możesz oczywiście najpierw odfiltrować "self" z locals(), ale wtedy jest już mniej schludny. –

4

Nie ma dobrego sposobu, aby uzyskać argumenty jako lista, jeśli są one określone indywidualnie w sygnaturze funkcji. Prawdopodobnie możesz zrobić coś z inspekcją lub klatkami, ale będzie to brzydsze niż zwykłe pisanie, tak jak to robiłeś.

+0

Nie można można to zrobić nawet patrząc na obiekt lub obiekty kodu. Chociaż można zrobić coś nieprzyjemnego, zakładając, że pierwszym argumentem jest "co_argcount" z "co_varnames", nie będzie to prawdą w przypadku rozpakowywania sekwencji lub * args/** kwargs. – bobince

+0

Ned, moja metoda działa dla dowolnej metody podpisu bez potrzeby uciekania się do kontroli lub klatek hack. – Unknown

2

Sprawdź, czy nowa nazwa pliku (nowa w Pythonie 2.6) z modułu kolekcji może działać.

3

Spróbuj inspect.getargspec:

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

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

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

Można użyć inspect.getargspec i otaczać go jako dekorator. Odnośnika opcjonalnych i kluczowych argumentów jest nieco skomplikowane, ale to powinno wystarczyć:

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__ 

Następnie można go używać tak:

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

Można to zrobić za pomocą introspekcji argumentów, ale kod będzie dłuższy niż kod, który spróbujesz zastąpić. Zwłaszcza jeśli zajmujesz się kw, co być może będziesz musiał zrobić.

Ten krótki kod działa w większości przypadków (lepsza od np 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) 

Ale to brzydki hack, dzięki czemu kod mniej czytelny dla żadnego konkretnego powodu oprócz lenistwa, więc nie rób tego. A także, jak często masz naprawdę klas, które mają więcej niż 5-6 parametrów początkowych?

0

Co powiesz na specjalną lekcję? Myślę, że to bardziej wyraźne i bardziej elastyczne w ten sposób:

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

Lubię które tworzą najbardziej, nie jest zbyt długa i zarówno kopiowaniem pasteable i 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) 
Powiązane problemy