自动生成的Python构造函数

8 投票
4 回答
6331 浏览
提问于 2025-04-16 17:15

我有很多来自不同项目的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 中描述的。

撰写回答