Python __init__ setattr on arguments?
-
07-07-2019 - |
質問
__ init __
メソッドは次のようなものであることが多いようです:
def __init__(self, ivar1, ivar2, ivar3):
self.ivar1 = ivar1
self.ivar2 = ivar2
self.ivar3 = ivar3
引数をリストに変換して( * args
または ** kwargs
に頼らずに)、次に setattr
を使用してパラメータの名前と引数を渡して、インスタンス変数を設定しますか?そして、おそらくリストをスライスします self.self
は必要ないため、少なくとも [1:]
にスライスする必要があります。
(実際には、名前と値を保持するには辞書が必要だと思います)
このように:
def __init__(self, ivar1, ivar2, ivar3, optional=False):
for k, v in makedict(self.__class__.__init__.__args__): # made up __args__
setattr(self, k, v)
ありがとう!
不明な回答に答えて、これが機能することがわかりました:
Class A(object):
def __init__(self, length, width, x):
self.__dict__.update(dict([(k, v) for k, v in locals().iteritems() if k != 'self']))
または
Class A(object):
def __init__(self, length, width, x):
self.__dict__.update(locals())
del self.__dict__['self']
それほど悪くない..
解決
どうぞ。はい、これはい邪悪なハックです。はい、オブジェクトには__dict__変数が必要です。しかし、ちょっと、すてきなライナーです!
def __init__(self):
self.__dict__.update(locals())
コンストラクタは、任意のタイプの引数を取ることができます。
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']
これにはselfも含まれますが、それを簡単に削除したり、selfを無視する独自の更新機能を作成したりできます。
他のヒント
inspect.getargspecを使用して、デコレーターとしてカプセル化できます。オプションの引数とキーワードの引数の検索は少し注意が必要ですが、これでうまくいくはずです:
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__
次のように使用できます:
class Foo(object):
@inits_args
def __init__(self, spam, eggs=4, ham=5):
print "Foo(%r, %r, %r)" % (self.spam, self.eggs, self.ham)
引数を関数シグネチャで個別に指定した場合、引数をリストとして取得する良い方法はありません。おそらく、検査またはフレームハックを使用して何かを行うことができますが、それは単にあなたがやったようにそれを綴るよりもugいでしょう。
inspect.getargspec を試してください:
In [31]: inspect.getargspec(C.__init__)
Out[31]: ArgSpec(args=['self', 'ivar1', 'ivar2', 'ivar3', 'optional'],
varargs=None, keywords=None, defaults=(False,))
collectionsモジュールの新しいnamedtuple(Python 2.6の新機能)が機能するかどうかを確認します。
引数のイントロスペクションを使用して実行できますが、コードは置換しようとするコードよりも長くなります。特に、kwを処理する場合は、実行する必要があります。
この短いコードはほとんどの場合に機能します(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)
しかし、それは見苦しいハックであり、怠except以外の特定の理由でコードを読みにくくしているので、やらないでください。また、5〜6を超える初期化パラメータを持つクラスを本当にどのくらいの頻度で持っていますか?
その形式が最も好きで、長すぎず、コピーと貼り付けもサブクラス化も可能です:
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)
特別なクラスから派生するのはどうですか?次のようにより明示的で柔軟だと思います:
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)