2010-09-10 6 views
5

jestem znalezieniem się pisać dużo zajęć z konstruktorów takich jak ten:Poprawa __init__ gdzie args są przypisane bezpośrednio do członków

class MyClass(object): 
    def __init__(self, foo, bar, foobar=1, anotherfoo=None): 
     self.foo = foo 
     self.bar = bar 
     self.foobar = foobar 
     self.anotherfoo = anotherfoo 

Czy to zły zapachy kodu? Czy Python oferuje bardziej elegancki sposób obsługi tego?

Moje klasy, a nawet niektórzy konstruktorzy są czymś więcej niż tylko tym, co pokazałem, ale zwykle mam listę argumentów przekazywanych do konstruktora, który właśnie kończy się przypisywanie do podobnie nazwanych członków. Zrobiłem niektóre z argumentów opcjonalnych zwrócić uwagę na problem robi coś takiego:

class MyClass(object): 
    def __init__(self, arg_dict): 
     self.__dict__ = arg_dict 
+0

O ile nie przewidujesz, że lista argumentów będzie rosła dużo dłużej lub będziesz potrzebować arbitralnego zestawu argumentów zawsze konwertowanych na atrybuty (w takim przypadku wymuszam użycie kwargs), nie widzę w tym nic złego. W większości przypadków robię to samo, dobrze się czyta i jest to zamierzony prerogatyw __init__. Nie sądzę, żeby w ogóle pachniał. –

+0

Zobacz także http://stackoverflow.com/questions/3652851. – FMc

Odpowiedz

8

Jeśli są kwargs, można zrobić coś takiego:

def __init__(self, **kwargs): 
    for kw,arg in kwargs.iteritems(): 
     setattr(self, kw, arg) 

posargs są nieco trudniejsze, ponieważ w przyjemny sposób nie dostajesz informacji o nazwach.

Jeśli chcesz podać wartości domyślne, można to zrobić tak:

def __init__(self, **kwargs): 
    arg_vals = { 
     'param1': 'default1', 
     # ... 
    } 
    arg_vals.update(kwargs) 
    for kw,arg in arg_vals.iteritems(): 
     setattr(self, kw, arg) 
0

To straszne kod.

class MyClass(object): 
    def __init__(self, foo, bar, spam, eggs): 
     for arg in self.__init__.func_code.co_varnames: 
      setattr(self, arg, locals()[arg]) 

Następnie można zrobić coś takiego:

myobj = MyClass(1, 0, "hello", "world") 
myletter = myobj.spam[myobj.bar] 
+4

Zauważ, że dla takich rzeczy, używanie kwargs jest prawdopodobnie dużo bardziej czytelne dla kogoś, kto próbuje zachować kod później - to naprawdę denerwujące, gdy czytasz kod i widzisz coś w stylu 'MyClass (1, 4, 6, 2)' – Amber

1

mógłby zrobić coś takiego:

def Struct(name): 
    def __init__(self, **fields): 
     self.__dict__.update(fields) 
    cls = type(name, (object,), {'__init__', __init__}) 
    return cls 

byłoby go używać jak:

MyClass = Struct('MyClass') 
t = MyClass(a=1, b=2) 

Jeśli chcesz argumenty pozycyjne, a następnie użyj:

def Struct(name, fields): 
    fields = fields.split() 
    def __init__(self, *args, **kwargs): 
     for field, value in zip(fields, args):  
      self.__dict__[field] = value 
     self.__dict__.update(kwargs) 
    cls = type(name, (object,), {'__init__': __init__}) 
    return cls 

To wtedy używany jak

MyClass = Struct('MyClass', 'foo bar foobar anotherfoo') 
a = MyClass(1, 2, foobar=3, anotherfoo=4) 

ta jest podobna do namedtuple od collections Pozwala to zaoszczędzić dużo więcej pisania niż zdefiniowanie zasadniczo samo__init__ metodę w kółko i nie wymagać, aby zmącić drzewo dziedziczenia tylko po to, aby uzyskać tę samą metodę bez ponownego wpisywania.

Jeśli trzeba dodać dodatkowe metody, to można po prostu stworzyć bazę

MyClassBase = Struct('MyClassBase', 'foo bar') 
class MyClass(MyClassBase): 
    def other_method(self): 
     pass 
0

Nie ma nic złego wzorca. Jest łatwy do odczytania i łatwy do zrozumienia (o wiele bardziej niż wiele innych odpowiedzi tutaj).

jestem znalezieniem się pisać dużo zajęć z konstruktorów jak ten

Jeśli nudzi się pisania takich konstruktorów, to rozwiązanie jest, aby komputer zrobi to za Ciebie.Na przykład, jeśli zdarzy ci się być kodowanie z PyDev można nacisnąć Ctrl + , do make the editor do it for you.

Jest to znacznie lepsze rozwiązanie niż spędzenie czasu na pisaniu i debugowaniu magicznego kodu, który zaciemnia to, co naprawdę chcesz zrobić, czyli przypisać wartości do niektórych zmiennych instancji.

+0

a) kod nie jest magiczny b) programowanie kopiowania i wklejania jest okropne (prawie wartość -1) c) jeśli kod, który usunie ten problem, wymaga debugowania, to jest to dobre ćwiczenie (to znaczy powinien być w stanie zrobić to dobrze za pierwszym razem). – aaronasterling

2

Osobiście trzymałbym się tego, co obecnie robisz, ponieważ jest o wiele mniej kruchy.

Rozważmy następujący kod z typo:

myobject = MyClass(foo=1,bar=2,fobar=3) 

Jeśli używasz oryginalnego podejścia dostaniesz następujące pożądane zachowanie podczas próby utworzenia obiektu:

TypeError: __init__() got an unexpected keyword argument 'fobar' 

Z kwargs podejście to się stanie:

>>> myobject.fobar 
3 

Wydaje się, że źródło takich błędów, które są bardzo trudne do znalezienia.

Możesz zweryfikować listę kwargs, aby upewnić się, że ma ona tylko spodziewane wartości, ale do czasu, kiedy to zrobisz i pracy w celu dodania wartości domyślnych, myślę, że będzie to bardziej złożone niż oryginalne podejście.