繁体   English   中英

python 创建一个元类,它为 *args 和 **kwargs 自动 __init__

[英]python create a metaclass which auto __init__ for *args and **kwargs

背景

当我创建一个 python 类并在init方法中我只写:

self.name1 = name1
self.name2 = name2
....

对于所有参数。

那么,如何优雅地为不同的*args**kwargs编写__init__

我想编写一个元类来自动设置*args**kwargs属性:

#the metaclass to be used
class LazyInit():
     # code here

然后当我创建它的子类时,子类将自动__init__为他们自己的*args**kwargs

class Hospital(metaclass = LazyInit):
  def __init__(self, *args,**kwargs): pass

class Basketball(metaclass = LazyInit):
  def __init__(self, *args, **kwargs): pass

a = 1
b = 2
c = 3
a_hospital = Hospital(a, b, kw1 =1, kw2 =2)
a_circle = Basketball(a, c, kw3 = 10, kw4 = 20)

我的尝试

我只知道我们只能使用setattr来为**kwargs做这件事:

class LazyInit():
    def __init__(self,**kwargs):
        for k,v in kwargs.items():
            setattr(self,k,v)
LazyInit(a=1).a

1

但是,这不是可以在子类中使用的元类。 我应该在我的每个子类中编写此代码片段...

首先,制作一个装饰器来进行绑定。

from functools import wraps
from inspect import signature

def binds(f):
    @wraps(f)
    def __init__(self, *args, **kwargs):
        # Update self attributes with inspected binding of arg names to values.
        vars(self).update(signature(f).bind(None, *args, **kwargs).arguments)
        # `self` is also an arg, but we didn't really want to save this one.
        del self.self
    return __init__

这是您可以使用它的方法。

class Foo:
    @binds
    def __init__(self, foo, bar, *args, baz, **kwargs):
        pass

还有一个演示:

>>> vars(Foo(1,2,3,4,baz=5,quux=10))
{'foo': 1, 'bar': 2, 'args': (3, 4), 'baz': 5, 'kwargs': {'quux': 10}}

现在剩下的就是让元类自动应用这个装饰器。

class AutoInit(type):
    def __new__(cls, name, bases, namespace):
        namespace['__init__'] = store_args(namespace['__init__'])
        return type.__new__(cls, name, bases, namespace)

AutoInit是默认元类type的子类。 在构建时,它会找到 init 方法并将其替换为包装版本。 type处理其余部分。

现在您可以像使用任何其他元类一样使用它:

class Bar(metaclass=AutoInit):
    def __init__(self, spam, eggs, *, bacon):
        pass

示范:

>>> vars(Bar(1,2,bacon=3))
{'spam': 1, 'eggs': 2, 'bacon': 3}

而且这种行为也会被继承。

class Baz(Bar):
    def __init__(self, spam, eggs, sausage, *, bacon):
        super().__init__(spam, eggs, bacon=bacon)

演示(注意'sausage' ):

>>> vars(Baz(1,2,3,bacon=4))
{'spam': 1, 'eggs': 2, 'sausage': 3, 'bacon': 4}

元类是深奥的魔法 很强大,但是很模糊。 你几乎从不需要它们(如果你不确定,假设你不需要)。 过多使用元类的代码会变得非常难以理解和维护。

在这种情况下,您可能应该在装饰器处停下来。 它几乎可以完成您想要的一切,而不会那么混乱。 显式优于隐式。 代码读的比写的多。 可读性很重要。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM