自动初始化实例变量?

Ada*_*tan 75 python class initialization-list

我有一个python类,看起来像这样:

class Process:
    def __init__(self, PID, PPID, cmd, FDs, reachable, user):
Run Code Online (Sandbox Code Playgroud)

其次是:

        self.PID=PID
        self.PPID=PPID
        self.cmd=cmd
        ...
Run Code Online (Sandbox Code Playgroud)

有没有办法自动初始化这些实例变量,比如C++的初始化列表?它会节省大量冗余代码.

Nad*_*mli 89

你可以使用装饰器:

from functools import wraps
import inspect

def initializer(func):
    """
    Automatically assigns the parameters.

    >>> class process:
    ...     @initializer
    ...     def __init__(self, cmd, reachable=False, user='root'):
    ...         pass
    >>> p = process('halt', True)
    >>> p.cmd, p.reachable, p.user
    ('halt', True, 'root')
    """
    names, varargs, keywords, defaults = inspect.getargspec(func)

    @wraps(func)
    def wrapper(self, *args, **kargs):
        for name, arg in list(zip(names[1:], args)) + list(kargs.items()):
            setattr(self, name, arg)

        for name, default in zip(reversed(names), reversed(defaults)):
            if not hasattr(self, name):
                setattr(self, name, default)

        func(self, *args, **kargs)

    return wrapper
Run Code Online (Sandbox Code Playgroud)

用它来装饰__init__方法:

class process:
    @initializer
    def __init__(self, PID, PPID, cmd, FDs, reachable, user):
        pass
Run Code Online (Sandbox Code Playgroud)

输出:

>>> c = process(1, 2, 3, 4, 5, 6)
>>> c.PID
1
>>> dir(c)
['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'
Run Code Online (Sandbox Code Playgroud)

  • +1为解决我的问题的好答案.但它不应该是该语言的核心功能吗?你认为值得写一个PEP吗? (11认同)
  • 这有效并回答了问题所以我投票了.但我让Ferdidand Beyer回答:"明确比隐含更好" (3认同)
  • 这是一个非常好的答案 - 这已经直接进入我的工具箱. (3认同)
  • @NadiaAlramli我认为代码中有一个小错误.请看这里https://gist.github.com/pmav99/137dbf4681be9a58de74(original.py) (3认同)
  • 当前示例有一个错误,如果签名不包含默认参数,则无效.您需要包含检查以确保名称和默认值不是None.例如:如果名称和默认值: (2认同)

Kiv*_*Kiv 35

如果您使用的是Python 2.6或更高版本,则可以使用collections.namedtuple:

>>> from collections import namedtuple
>>> Process = namedtuple('Process', 'PID PPID cmd')
>>> proc = Process(1, 2, 3)
>>> proc.PID
1
>>> proc.PPID
2
Run Code Online (Sandbox Code Playgroud)

这是合适的,特别是当你的班级真的只是一大堆价值观.

  • "这很合适,特别是当你的班级真的只是一大堆价值观时." 在这种情况下,您也可以这样做:[https://docs.python.org/3.3/tutorial/classes.html#odds-and-ends](https://docs.python.org/3.3/tutorial) /classes.html#odds-and-ends) (2认同)

Jun*_*ius 28

对于Python 3.7+,您可以使用数据类,这是一种非常pythonic和可维护的方式来做你想要的.

它允许您为类定义字段,这些字段是自动初始化的实例变量.

看起来像这样:

@dataclass
class Process:
    PID: int
    PPID: int
    cmd: str
    ...
Run Code Online (Sandbox Code Playgroud)

__init__方法已经在您的课程中.

注意,这里类型提示是必需的,这就是为什么我已经使用intstr在本例中.如果您不知道字段的类型,可以使用模块中的Anytyping.

与提出的解决方案相比,Data Class具有许多优点:

  • 它是明确的:所有字段都是可见的,它尊重Python的Zen并使其可读和可维护.将它与使用相比较**kwargs.
  • 它可以有方法.就像任何其他类一样.缺少方法,如果你想使用它们,是一个缺点__init__.
  • 它允许您超越自动__post_init__使用该__init__方法.

  • 您需要导入它。`从数据类导入数据类` (4认同)
  • 您能解释一下为什么dataclass只应该用于存储数据的类,而不是其他行为吗?我可能会为此创建一个新的SO帖子,以便更好地理解其适当的用例.谢谢. (3认同)

Fer*_*yer 26

引用Python,

显式优于隐式.

  • @Ferdinand,我同意在语言中使用stdlib可以很好的东西是愚蠢的,但是,它应该在stdlib中,因为"美丽胜过丑陋"优先,许多重复的任务都是丑陋的(任何形式的重复). (26认同)
  • 初始化列表声明不会足够明确吗? (4认同)

Joc*_*zel 21

你可以做的另一件事:

class X(object):
    def __init__(self, a,b,c,d):
        vars = locals() # dict of local names
        self.__dict__.update(vars) # __dict__ holds and object's attributes
        del self.__dict__["self"] # don't need `self`
Run Code Online (Sandbox Code Playgroud)

但我推荐的唯一解决方案,除了拼写出来之外,还是"在你的编辑器中创建一个宏";-p


Sil*_*ost 15

您可以使用关键字参数轻松完成,例如:

>>> class D:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

>>> D(test='d').test
'd'
Run Code Online (Sandbox Code Playgroud)

位置参数的类似实现将是:

>> class C:
    def __init__(self, *args):
        self.t, self.d = args


>>> C('abc', 'def').t
'abc'
>>> C('abc', 'def').d
'def'
Run Code Online (Sandbox Code Playgroud)

对我来说似乎没有解决你的问题.

  • 我喜欢的另一个变体是`self .__ dict __.update(**kwargs)` (3认同)

And*_*gee 6

Nadia的解决方案更好,更强大,但我认为这也很有趣:

def constructor(*arg_names):
  def __init__(self, *args):
    for name, val in zip(arg_names, args):
      self.__setattr__(name, val)
  return __init__


class MyClass(object):
  __init__ = constructor("var1", "var2", "var3")


>>> c = MyClass("fish", "cheese", "beans")
>>> c.var2
"cheese"
Run Code Online (Sandbox Code Playgroud)


Ent*_*orm 5

我出于相同的目的需要一些东西,但是现有的答案都没有涵盖我测试过的所有案例。Nadia的答案最接近我想要的答案,因此我以她的代码作为基础。

下面的装饰器适用于所有有效的参数组合:

Positional                                          __init__(self, a, b                )
Keyword                                             __init__(self, a=None, b=None      )
Positional + Keyword                                __init__(self, a, b, c=None, d=None)
Variable Positional                                 __init__(self, *a                  )
Variable Positional + Keyword                       __init__(self, *a, b=None          )
Variable Positional + Variable Keyword              __init__(self, *a, **kwargs        )
Positional + Variable Positional + Keyword          __init__(self, a, *b, c=None       )
Positional + Variable Positional + Variable Keyword __init__(self, a, *b, **kwargs     )
Keyword Only                                        __init__(self, *, a=None           )
Positional + Keyword Only                           __init__(self, a, *, b=None        )
Run Code Online (Sandbox Code Playgroud)

它还实现了标准的_-prefix约定,以允许__init__-private变量不会分配给类实例。


Positional                                          __init__(self, a, b                )
Keyword                                             __init__(self, a=None, b=None      )
Positional + Keyword                                __init__(self, a, b, c=None, d=None)
Variable Positional                                 __init__(self, *a                  )
Variable Positional + Keyword                       __init__(self, *a, b=None          )
Variable Positional + Variable Keyword              __init__(self, *a, **kwargs        )
Positional + Variable Positional + Keyword          __init__(self, a, *b, c=None       )
Positional + Variable Positional + Variable Keyword __init__(self, a, *b, **kwargs     )
Keyword Only                                        __init__(self, *, a=None           )
Positional + Keyword Only                           __init__(self, a, *, b=None        )
Run Code Online (Sandbox Code Playgroud)

注意:

我包括了测试,但是为了简洁起见,将它们折叠到最后一行(58)。您可以通过用换行符为find/replace所有$字符- 展开详细描述所有潜在用例的测试。