如何优雅/高效地为需要大量实例变量的Python类编写__init__函数?

时间:2015-01-17 21:10:33

标签: python class

假设您有一个带有许多(关键字)参数的类,其中大多数都是作为实例变量存储的:

class ManyInitVariables():
    def __init__(a=0, b=2, c=1, d=0, e=-1, ... , x=100, y=0, z=9):

如何在__init__中初始化它们?你可以这样做:

class ManyInitVariables():
    def __init__(a=0, b=2, c=1, d=0, e=-1, ... , x=100, y=0, z=9):
        self.a = a
        self.b = b
        self.c = c
        ...
        self.z = z

...但需要大量打字!我如何让__init__自动一些所需的参数,注意其他参数可能不需要作为实例变量分配?

1 个答案:

答案 0 :(得分:10)

我确信网络上还有许多其他类似的解决方案可以解决这个非常常见的问题,但这只是一个问题,例如:

import functools
import inspect

def absorb_args(f):
    args, _, _, defs = inspect.getargspec(f)
    args = args[1:]  # ignore the leading `self`
    @functools.wraps(f)
    def do_absorb(self, *a, **k):
        ndefs = len(args) - len(a) + 2
        for name, value in zip(args, a + defs[-ndefs:]):
            setattr(self, name, value)
        for name, value in k.items():
            setattr(self, name, value)
        return f(self, *a, **k)
    return do_absorb

补充说:我已经被要求进一步解释这个问题,但是,如果你不熟练使用Python,那么这里会发生很多事情! - )。

functools.wraps是帮助制作更好的装饰者的装饰工具,请参阅https://docs.python.org/2/library/functools.html#functools.wraps - 与问题没有直接关系,但对于支持基于功能的交互式help和工具非常有用。文档字符串。在写一个功能装饰器(最常见的情况)包装装饰功能时,养成总是使用它的习惯,你不会后悔。

inspect模块是在现代Python中进行内省的唯一正确方法。 inspect.getargspec特别为您提供有关函数接受哪些参数的信息,以及它们的默认值(如果有的话)(我忽略的两个信息位,通过将它们分配给_ ,约有*a**k特殊参数,这个装饰者不支持)。有关详情,请参阅https://docs.python.org/2/library/inspect.html?highlight=getargspec#inspect.getargspec

按惯例,

self始终是方法的第一个arg(此装饰器仅用于方法:-)。因此,第一个for循环处理位置args(无论是在调用中明确给出还是默认为默认值);然后,第二个for循环处理命名的args(我希望,这个更容易掌握:-)。 setattr当然是宝贵的内置函数,它为变量名https://docs.python.org/2/library/functions.html?highlight=setattr#setattr设置属性以获取更多信息。

顺便提一下,如果你只想在__init__中使用它(正如你在下面的例子中所见,absorb_attrs本身没有这样的约束),那么写一个装饰师为这种治疗挑选了班级__init__,并将该班级装饰师应用于班级本身。

另外,如果你的班级__init__没有工作可以做,一旦args被吸收"这样,你仍然必须定义(装饰的)__init__,但它的主体可以限制为解释参数的文档字符串(我个人更喜欢在这种情况下也总是有pass语句,但是这是个人风格问题。)

现在,回到最初的答案,举个例子......! - )

然后,例如,

class Struggle(object):

    @absorb_args
    def __init__(self, a, b, c, bab='bo', bip='bop'):
        self.d = a + b

    @absorb_args
    def setit(self, x, y, z, u=23, w=45):
        self.t = x + y

    def __str__(self):
        attrs = sorted(self.__dict__)
        r = ['%s: %s' % (a, getattr(self, a)) for a in attrs]
        return ', '.join(r)

s = Struggle('fee', 'fie', 'foo', bip='beeeeeep')
s.setit(1, 2, 3, w=99)
print(s)

会打印

a: fee, b: fie, bab: bo, bip: beeeeeep, c: foo, d: feefie, t: 3, u: 23, w: 99, x: 1, y: 2, z: 3

根据需要。

我唯一的借口"重新发明轮子"这种方式(而不是在网上寻找解决方案)是另一个晚上我的妻子和共同作者安娜(只有弗兰克威利森纪念奖的女性获奖者,对于Python社区的贡献,BTW :-)问我这个问题。 (我们正在慢慢地唉!在第三版" Python编写一个Nutshell") - 我花了10分钟来编写代码(对不起,还没有测试:-)同时在同一个10分钟内她(尽管是一位非常熟练的网络搜索者:-)无法在网络上找到现有的解决方案。而且,这样我就不用担心版权问题,如果我想在这里发布,将它包含在下一个Nutshell中,在OSCON或Pycon上提供它,等等......: - )