我有来自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字节码或使用模板/宏来改变源文件本身?
答案 0 :(得分:3)
你可以用Metaclasses做到这一点。以下是覆盖__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']
答案 1 :(得分:1)
查看namedtuple:
>>> 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')
答案 2 :(得分:1)
对于python> = 3.7,处理此问题的正确方法是通过dataclasses:
此模块提供装饰器和功能,用于将生成的特殊方法(例如 init ()和 repr ())自动添加到用户定义的类。它最初在PEP 557中进行了描述。
答案 3 :(得分:0)
您可以这样做:
class Foo(Base):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
执行此操作的一个问题是无法保证所有实例都具有相同的成员。另一个问题是现在必须使用关键字参数调用所有构造函数。