自动初始化实例变量?
我有一个看起来像这样的Python类:
class Process:
def __init__(self, PID, PPID, cmd, FDs, reachable, user):
接下来是:
self.PID=PID
self.PPID=PPID
self.cmd=cmd
...
有没有什么方法可以像C++的初始化列表那样自动初始化这些实例变量?这样可以省去很多重复的代码。
17 个回答
37
如果你使用的是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
这个功能特别适合用在你的类只是用来存放一堆值的时候。
67
对于Python 3.7及以上版本,你可以使用数据类(Data Class),这是一种非常符合Python风格且易于维护的方法,可以帮助你实现想要的功能。
数据类允许你为你的类定义字段,这些字段就是你自动初始化的实例变量。
它的样子大概是这样的:
@dataclass
class Process:
PID: int
PPID: int
cmd: str
...
你的类中会自动包含__init__
方法。
需要注意的是,这里需要类型提示,所以我在例子中使用了int
和str
。如果你不知道字段的类型,可以使用来自typing
模块的Any。
与其他提议的解决方案相比,数据类有很多优点:
- 它是明确的:所有字段都是可见的,这符合Python的哲学,使代码更易读和易于维护。可以和使用
**kwargs
进行比较。 - 它可以有方法。就像其他任何类一样。
- 它允许你在自动生成的
__init__
方法之外进行扩展,使用__post_init__
方法。
编辑:避免使用命名元组(NamedTuples)的原因
有些人建议在这种情况下使用namedtuple
,但命名元组有一些与Python类不同的行为,这些行为一开始并不明显,需要了解:
1. 命名元组是不可变的
不可变性可能有用,但这可能不是你想要的实例特性。如果你使用@dataclass
装饰器中的frozen=True
参数,数据类也可以在某种程度上是不可变的。
2. 命名元组的__eq__
行为像元组一样
在Python中,SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2)
的结果是True
!命名元组的__eq__
函数在比较时只考虑值和这些值在被比较实例中的位置,而不考虑它们的类或字段名称。
127
你可以使用一个装饰器:
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
用它来装饰 __init__
方法:
class process:
@initializer
def __init__(self, PID, PPID, cmd, FDs, reachable, user):
pass
输出结果:
>>> c = process(1, 2, 3, 4, 5, 6)
>>> c.PID
1
>>> dir(c)
['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'