【问题标题】:Automatically-generated Python constructor自动生成的 Python 构造函数
【发布时间】:2011-08-21 08:22:05
【问题描述】:

我有来自 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 字节码或使用模板/宏来更改源文件本身?

【问题讨论】:

    标签: python constructor code-generation data-members


    【解决方案1】:

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

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

    【讨论】:

    • 如果你在 >=3.7 上似乎是正确的方法
    【解决方案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']
    

    【讨论】:

    • 非常感谢。您的解决方案效果很好,尽管我必须查看您回复的源代码才能获得缩进。
    • 类Base的目的是什么?为什么不直接在 Foo 上定义元类,然后定义 init_args 类变量呢?这会让 init_args 的用途更加清晰。
    • @Paul:Base 在那里,因此您可以定义任意数量的类,而您不必为每个类重新定义 __metaclass__。 OP 想要节省打字,而不是尽可能清晰。
    【解决方案3】:

    你可以这样做:

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

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

    【讨论】:

      【解决方案4】:

      查看命名元组:

      >>> 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')
      

      【讨论】:

      • 原始代码所没有的,但是,mi 字段的默认值是 None
      猜你喜欢
      • 1970-01-01
      • 2012-03-08
      • 2016-12-07
      • 2012-01-07
      • 2019-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-23
      相关资源
      最近更新 更多