简体   繁体   English

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

[英]How do I elegantly/efficiently write the __init__ function for a Python class that takes lots of instance variables?

Let's say you have a class that takes many (keyword) arguments, most of which are meant to be stored as instance variables:假设您有一个接受许多(关键字)参数的类,其中大部分是作为实例变量存储的:

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

How would you initialize them in __init__ ?你将如何在__init__初始化它们? You could do something like this:你可以这样做:

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

...but it would take a lot of typing! ......但需要大量打字! How could I get __init__ to automatically some of the arguments it takes, noting that other arguments may not need to be assigned as instance variables?我怎样才能让__init__自动获取它需要的一些参数,注意其他参数可能不需要作为实例变量分配?

I'm sure there are many other similar solutions out there on the web for this very common issue, but this is one, for example: 我确信网上有很多其他类似的解决方案可以解决这个非常常见的问题,但这是一个例子:

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

Added: I've been asked to explain this further, but, there's a lot going on here if you're not skilled at Python!-). 补充:我被要求进一步解释这一点,但是,如果你不擅长Python,那么这里有很多东西! - )。

functools.wraps is a decorator to help make better decorators, see https://docs.python.org/2/library/functools.html#functools.wraps -- not directly germane to the question but useful to support interactive help and tools based on functions' docstrings. functools.wraps是一个帮助制作更好的装饰器的装饰器,请参阅https://docs.python.org/2/library/functools.html#functools.wraps - 与问题没有直接关系,但对支持交互式help和工具很有用基于函数的docstrings。 Get into the habit of always using it when writing a function decorator that (the most common case) wraps the decorated function, and you won't regret it. 养成在编写函数装饰器时总是使用它的习惯(最常见的情况)包装装饰函数,你不会后悔。

The inspect module is the only right way to do introspection in modern Python. inspect模块是在现代Python中进行内省的唯一正确方法。 inspect.getargspec in particular gives you information on what arguments a function accepts, and what the default values for them are, if any (the two bits of info I'm ignoring, by assigning them to _ , are about *a and **k special args, which this decorator doesn't support). inspect.getargspec特别为您提供了函数接受什么参数的信息,以及它们的默认值(如果有的话)(我忽略的两个信息,通过将它们分配给_ ,大约是*a**k特殊的args,这个装饰者不支持)。 See https://docs.python.org/2/library/inspect.html?highlight=getargspec#inspect.getargspec for more. 有关详细信息,请参阅https://docs.python.org/2/library/inspect.html?highlight=getargspec#inspect.getargspec

self , by convention, is always the first arg to a method (and this decorator is meant for methods only:-). 按照惯例, self始终是方法的第一个arg(而这个装饰器仅用于方法:-)。 So, the first for loop deals with positional args (whether explicitly given in the call or defaulting to default values); 因此,第一个for循环处理位置args(无论是在调用中明确给出还是默认为默认值); then, the second for loop deals with named args (that one, I hope, is simpler to grasp:-). 然后,第二个for循环处理命名的args(我希望,这个更容易掌握:-)。 setattr of course is the precious built-in function which sets an attribute with a variable name, https://docs.python.org/2/library/functions.html?highlight=setattr#setattr for more. setattr当然是一个珍贵的内置函数,它设置一个带有变量名的属性, https: setattr for more。

Incidentally, if you only care to use this in __init__ (as you see in the example below, absorb_attrs per se has no such constraint), then write a class decorator which singles out the class's __init__ for this treatment, and apply that class decorator to the class itself. 顺便提一下,如果你只关心在__init__使用它(正如你在下面的例子中看到的那样, absorb_attrs本身没有这样的约束),那么写一个装饰器,为这个处理单挑出类的__init__ ,并将该类装饰器应用于班级本身。

Also, if your class's __init__ has no work left to do once args are "absorbed" in this way, you must still define the (decorated) __init__ , but its body can be limited to a docstring explaining the arguments (I personally prefer to always also have a pass statement in such cases, but that's a personal style issue). 另外,如果你的类的__init__没有工作要做,一旦args以这种方式被“吸收”,你仍然必须定义(装饰的) __init__ ,但它的主体可以限制为解释参数的文档字符串(我个人更喜欢总是在这种情况下也有pass声明,但这是个人风格问题)。

And now, back to the original answer, with an example...!-) 现在,回到最初的答案,举个例子......! - )

And then, eg, something like 然后,例如,像

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)

would print 会打印

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

as desired. 如预期的。

My only excuse for "reinventing the wheel" this way (rather than scouring the web for a solution) is that the other evening my wife and co-author Anna (only ever woman winner of the Frank Willison Memorial Award for contribution to the Python community, BTW:-) asked me about it (we are, slowly alas!, writing the 3rd edition of "Python in a Nutshell") -- it took me 10 minutes to code this (sorry, no tests yet:-) while in the same 10 minutes she (despite being a very skilled web-searcher:-) could not locate an existing solution on the web. 我唯一能够以这种方式“重新发明轮子”(而不是在网上寻找解决方案)的借口是另一个晚上我的妻子和共同作者安娜(只有弗兰克威利森纪念奖的女性获奖者才能为Python社区做出贡献) ,BTW :-)问我这件事(我们是,慢慢地唉!,写第三版“Python in a Nutshell”) - 我花了10分钟来编写代码(对不起,还没有测试:-)而在同样的10分钟她(尽管是一个非常熟练的网络搜索者:-)无法在网络上找到现有的解决方案。 And, this way I need not worry about copyright issues if I want to post it here, include it in the next Nutshell, present about it at OSCON or Pycon, and so forth...:-) 而且,这样我就不用担心版权问题,如果我想在这里发布,将它包含在下一个Nutshell中,在OSCON或Pycon上提供它,等等...... :-)

A couple of years later, Python 3.7 introduced the dataclass , which allows you to define a class like this:几年后,Python 3.7 引入了dataclass ,它允许您像这样定义一个类:

from dataclasses import dataclass

@dataclass
class ManyInitVariables:
    a: int = 0
    b: int = 2
    ...
    z: 'typing.Any' = 9

This will automatically take care of setting the instance attributes.这将自动处理设置实例属性。

There's also make_dataclass :还有make_dataclass

from dataclasses import make_dataclass, field

attributes = [('a', int, 0), ('b', int, 2), ..., ('z', 'typing.Any', 9)]

ManyInitVariables = make_dataclass(
    'ManyInitVariables', 
    [(attr_name, attr_type, field(default=attr_default)) 
     for attr_name, attr_type, attr_default in attributes])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Python中的实例变量是否必须在__init__方法中为类初始化? - Do instance variables in Python have to be initialized in the __init__ method for a Class? 如何在 Python 3 类 __init__ 函数中使用 **kwargs? - How do I use **kwargs in Python 3 class __init__ function? 如何在__init__中获取python类实例变量? - How to get python class instance variables inside __init__? Python继承,类级变量,__ init__不带参数 - Python inheritance, class level variables, __init__ takes no arguments 如何确定python类中__init__函数内的内容? - How do I determine what to put inside the __init__ function in a python class? 我如何将可选参数传递给python类中的__init__函数 - how do i pass optional parameter to the __init__ function in python class 如何在python多重继承中编写__init__以实例化所有变量 - how to write __init__ in python multiple inheritance to instantiate all variables Python function class 和 __init__ - Python function class, and __init__ 如何访问python类中的实例变量(未在“__init__”中定义)? - How to access to instance variable(which is not defined in "__init__") in class in python? 如何在其他函数中使用函数中的变量而不将变量放入 class 的 __init__ 方法中? - how do I use variables from functions in other functions without putting the variable in the __init__ method of a class?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM