7

我有来自 SQLAlchemy 的各种项目的无数 Python 类(以及来自 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

4 回答 4

2

您可能可以使用元类来做到这一点。这是一个覆盖的元类示例__init__()Python Class Decorator

当然,您将需要以某种方式指定字段/参数名称 - 或者如果您愿意,可以使用命名参数。这是一种方法:

# 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']
于 2011-05-09T01:05:51.923 回答
2

对于 python >= 3.7,正确的处理方法是dataclasses

该模块提供了一个装饰器和函数,用于自动将生成的特殊方法添加到用户定义的类中__init__()__repr__()它最初在 PEP 557 中描述。

于 2020-07-02T14:58:21.983 回答
1

你可以这样做:

class Foo(Base):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

这样做的一个问题是不能保证所有实例都具有相同的成员。另一个问题是现在必须使用关键字参数调用所有构造函数。

于 2011-05-09T01:18:46.737 回答
0

查看命名元组:

>>> from collections import namedtuple
>>> Foo = namedtuple("Foo", "first last email mi")
>>> f = Foo("Alfred", "Neumann", "aen@madmagazine.com", "E")
>>> f
Foo(first='Alfred', last='Neumann', email='aen@madmagazinecom', mi='E')
于 2011-05-09T04:41:18.763 回答