我如何避免“self.x = x; self.y = y; self.z = z” pattern in __init__?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (3)
  • 关注 (0)
  • 查看 (218)

我看到类似的模式

def __init__(self, x, y, z):
    ...
    self.x = x
    self.y = y
    self.z = z
    ...

相当频繁,往往有很多的参数。有没有避免这种乏味的重复性很高好方法?该类应该继承namedtuple吗?

提问于
用户回答回答于

这是一个我自己写的解决方案。

助手功能

小助手功能使其更加方便和可重复使用:

def auto_init(local_name_space):
    """Set instance attributes from arguments.
    """
    self = local_name_space.pop('self')
    for name, value in local_name_space.items():
        setattr(self, name, value)

应用

您需要使用以下方式调用它locals()

class A:
    def __init__(self, x, y, z):
        auto_init(locals())

测试

a = A(1, 2, 3)
print(a.__dict__)

输出:

{'y': 2, 'z': 3, 'x': 1}

无需改变 locals()

如果你不想改变locals() 那就使用这个版本:

def auto_init(local_name_space):
    """Set instance attributes from arguments.
    """
    for name, value in local_name_space.items():
        if name != 'self': 
            setattr(local_name_space['self'], name, value)
用户回答回答于

显式复制参数到属性中没有任何问题。如果在ctor中有太多的参数,它有时被认为是一种代码味道,也许你应该将这些参数分组到一个较少的对象中。无论如何,明确的做法是解决问题的方法。

但是,既然你在问如何做(而不是应该怎么做),那么一个解决方案是:

class A:
    def __init__(self, **kwargs):
        for key in kwargs:
          setattr(self, key, kwargs[key])

a = A(l=1, d=2)
a.l # will return 1
a.d # will return 2
用户回答回答于

A decorator solution that keeps the signature:

import decorator
import inspect
import sys


@decorator.decorator
def simple_init(func, self, *args, **kws):
    """
    @simple_init
    def __init__(self,a,b,...,z)
        dosomething()

    behaves like

    def __init__(self,a,b,...,z)
        self.a = a
        self.b = b
        ...
        self.z = z
        dosomething()
    """

    #init_argumentnames_without_self = ['a','b',...,'z']
    if sys.version_info.major == 2:
        init_argumentnames_without_self = inspect.getargspec(func).args[1:]
    else:
        init_argumentnames_without_self = tuple(inspect.signature(func).parameters.keys())[1:]

    positional_values = args
    keyword_values_in_correct_order = tuple(kws[key] for key in init_argumentnames_without_self if key in kws)
    attribute_values = positional_values + keyword_values_in_correct_order

    for attribute_name,attribute_value in zip(init_argumentnames_without_self,attribute_values):
        setattr(self,attribute_name,attribute_value)

    # call the original __init__
    func(self, *args, **kws)


class Test():
    @simple_init
    def __init__(self,a,b,c,d=4):
        print(self.a,self.b,self.c,self.d)

#prints 1 3 2 4
t = Test(1,c=2,b=3)
#keeps signature
#prints ['self', 'a', 'b', 'c', 'd']
if sys.version_info.major == 2:
    print(inspect.getargspec(Test.__init__).args)
else:
    print(inspect.signature(Test.__init__))

扫码关注云+社区

领取腾讯云代金券