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

问题描述 投票:0回答:1

背景

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

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

对于所有参数。

问题

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

我想编写一个元类以自动设置*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

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

python class initialization setattribute
1个回答
0
投票

首先,使装饰器进行绑定。

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}

元类是deep magic。功能强大,但晦涩难懂。您几乎永远不需要它们(如果不确定,假设您不需要)。过多使用元类的代码可能变得很难理解和维护。

在这种情况下,您可能应该在装饰器处停下来。它几乎可以完成您想要的所有事情,而不会造成混乱。显式胜于隐式。读取的代码多于编写的代码。可读性计数。

© www.soinside.com 2019 - 2024. All rights reserved.