在Python类中,我想自动将成员变量分配给__init__
函数参数,如下所示:
class Foo(object):
def __init__(self, arg1, arg2 = 1):
self.arg1 = arg1
self.arg2 = arg2
是否可以使用自定义元类实现此目的?
答案 0 :(得分:1)
首先,免责声明。 Python对象的创建和初始化可能很复杂且非常动态。这意味着很难找到适合角落案例的解决方案。此外,解决方案倾向于使用更暗的魔法,所以当它们不可避免地做出错时,它们很难调试。
其次,您的类具有如此多的初始化参数这一事实可能暗示它具有太多许多参数。其中一些可能是相关的,可以在较小的类中组合在一起。例如,如果我正在制造一辆汽车,最好有:
class Car:
def __init__(self, tires, engine):
self.tires = tires
self.engine = engine
class Tire:
def __init__(self, radius, winter=False):
self.radius = radius
self.winter = winter
class Engine:
def __init__(self, big=True, loud=True):
self.big = big
self.loud = loud
而不是
class Car:
def __init__(self, tire_radius, winter_tires=False,
engine_big=True, engine_loud=True):
self.tire_radius = tire_radius
self.winter_tires winter_tires
self.engine_big = engine_big
self.engine_loud = engine_loud
所有这些都说,这是一个解决方案。我没有在我自己的代码中使用它,所以它不是“经过实战考验”。但它至少似乎适用于简单的情况。使用风险自负。
首先,这里不需要元类,我们可以在__init__
方法上使用简单的装饰器。我认为这更具可读性,因为很明显我们只修改了__init__
的行为,而不是更深层次的类创建。
import inspect
import functools
def unpack(__init__):
sig = inspect.signature(__init__)
@functools.wraps(__init__)
def __init_wrapped__(self, *args, **kwargs):
bound = sig.bind(self, *args, **kwargs)
bound.apply_defaults()
# first entry is the instance, should not be set
# discard it, taking only the rest
attrs = list(bound.arguments.items())[1:]
for attr, value in attrs:
setattr(self, attr, value)
return __init__(self, *args, **kwargs)
return __init_wrapped__
此装饰器使用inspect
模块来检索__init__
方法的签名。然后我们简单地遍历属性并使用setattr
来分配它们。
在使用中,它看起来像:
class Foo(object):
@unpack
def __init__(self, a, b=88):
print('This still runs!')
这样
>>> foo = Foo(42)
This still runs!
>>> foo.a
42
>>> foo.b
88
我不确定每个内省工具都会看到装饰__init__
的正确签名。特别是,我不确定Sphinx是否能做出“正确的事”。但至少inspect
模块的signature
函数将返回包装函数的签名,可以进行测试。
如果确实想要一个元类解决方案,那么它就足够简单了(但是可读性更差,更有魔力,IMO)。您只需要编写一个应用unpack
装饰器的类工厂:
def unpackmeta(clsname, bases, dct):
dct['__init__'] = unpack(dct['__init__'])
return type(clsname, bases, dct)
class Foo(metaclass=unpackmeta):
def __init__(self, a, b=88):
print('This still runs!')
输出与上面的例子相同。