自动生成的Python构造函数
我有很多来自不同项目的Python类,主要是用SQLAlchemy写的,还有一些是用Pygame写的。最近我发现了一个共同点:这些类的构造函数(也就是初始化方法)通常都是这样的:
class Foo(Base):
def __init__(self, first, last, email, mi=""):
self.first = first
self.last = last
self.email = email
self.mi = mi
... 也就是说,这个构造函数的唯一作用就是把一组位置参数(传入的值)转移到一组同名的数据成员里,根本没有进行任何计算或者其他函数调用。
我觉得这种重复的写法是没必要的,而且在修改的时候容易出错。
这让我想问:有没有办法自动生成这样的__init__(self, ...)
函数,最好是不用去搞什么CPython字节码,也不想用模板或宏来改动源文件呢?
4 个回答
1
你可以这样做:
class Foo(Base):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
不过,这样做有一个问题,就是不能保证所有的实例都有相同的成员。还有一个问题是,现在所有的构造函数都必须使用关键字参数来调用。
2
你可以通过元类来实现这个功能。下面是一个元类的例子,它重写了 __init__()
方法:
当然,你需要以某种方式指定字段或参数的名称,或者如果你喜欢的话,可以使用命名参数。这里有一种方法可以做到这一点:
# This is the mataclass-defined __init__
def auto_init(self, *args, **kwargs):
for arg_val, arg_name in zip(args, self.init_args):
setattr(self, arg_name, arg_val)
# This would allow the user to explicitly specify field values with named arguments
self.__dict__.update(kwargs)
class MetaBase(type):
def __new__(cls, name, bases, attrs):
attrs['__init__'] = auto_init
return super(MetaBase, cls).__new__(cls, name, bases, attrs)
class Base(object):
__metaclass__ = MetaBase
# No need to define __init__
class Foo(Base):
init_args = ['first', 'last', 'email', 'mi']
5
对于 Python 3.7 及以上版本,处理这个问题的正确方法是使用 dataclasses
:
这个模块提供了一种装饰器和一些函数,可以自动为用户定义的类添加一些特殊的方法,比如
__init__()
和__repr__()
。这个功能最初是在 PEP 557 中描述的。