Python __init__ setattr auf Argumente?
-
07-07-2019 - |
Frage
Es scheint, dass oft __init__
Methoden ähnlich sind dies:
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 auf *args
oder **kwargs
) und dann setattr
mit den Instanzvariablen zu setzen, mit dem Namen des Parameters und das Argumente übergeben? Und vielleicht die Liste in Scheiben schneiden, z.B. Sie müssen zumindest schneiden es [1:]
, weil Sie nicht self.self
wollen.
(eigentlich ich denke, es müßte ein Wörterbuch sein, 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)
Danke!
Als Reaktion auf Unknown Antwort, 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 schlecht ..
Lösung
Hier gehen Sie. Ja, das ist eine hässliche böse Hack. Ja das Objekt braucht eine __dict__ Variable. Aber hey, es ist ein nettes kleines Motto!
def __init__(self):
self.__dict__.update(locals())
Der Konstruktor kann jede Art von Argumenten entgegennehmen.
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']
Es wird auch selbst, aber Sie können die leicht löschen oder eigene Update-Funktion machen, die selbst ignoriert.
Andere Tipps
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 können dann 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)
Es gibt keine gute Möglichkeit, die Argumente als Liste zu bekommen, wenn sie einzeln in der Funktionssignatur festgelegt sind. Sie können sich wahrscheinlich etwas mit Hacks inspizieren oder Rahmen, aber das wird hässlicher als einfach zu buchstabieren, wie Sie getan haben.
Versuchen Sie inspect.getargspec :
In [31]: inspect.getargspec(C.__init__)
Out[31]: ArgSpec(args=['self', 'ivar1', 'ivar2', 'ivar3', 'optional'],
varargs=None, keywords=None, defaults=(False,))
, wenn die neue namedtuple (neu in Python 2.6) aus der Sammlung Modul könnte für Sie arbeitet.
Sie können tun, indem sie Selbstbeobachtung der Argumente, aber der Code wird sein, länger als der Code, den Sie versuchen, zu ersetzen. Vor allem, wenn Sie kw sind Handling, die Sie haben können, zu tun.
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 auch, wie oft Sie wirklich haben Klassen, die mehr als 5-6 init Parameter haben?
Ich mag diese Form am meisten, nicht zu lang und beide Kopie-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)
Wie wäre es aus einer Sonderklasse ableiten? Ich denke, es ist explizite 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)