扩展类的__init__以接受更多关键字参数

时间:2019-02-25 22:28:24

标签: python python-3.x

我想扩展更多的类集以接受其他关键字参数。所有的类集合共享一个我不能更改的公共接口。

我已经这样尝试过了:

class Base:
    def __init__(self, a=0, b=1, c=2):
        self.a = a
        self.b = b
        self.c = c



def extended_base ( Kls ):

    additional_fields = [ 'a', 'b' ]

    def my_init( self, *args, **kwargs ):
        for field in additional_fields:
            try:
                setattr(self, '{}2'.format(field), kwargs.pop(field))
            except KeyError:
                setattr(self, '{}2'.format(field), None)

        # Does not work as I expect
        # super().__init__(*args, **kwargs)

        # Does not work as expected 
        super(Kls, self).__init__(*args, **kwargs)


    # I would like to define a function that returns a function that does
    # what the following functions do, automatically but for every value
    # in `additional_fields` 
    def get_a2( self ):
        return self.a2 or self.a
    def get_b2( self ):
        return self.b2 or self.b

    return type( 'My{}'.format(Kls.__name__), (Kls, ), {
        '__init__' : my_init,
        'get_a2' : get_a2,
        'get_b2' : get_b2,

    })


test = extended_base(Base)(a=3, a2=9)
test.a
test.a2
test.get_a2()

test.get_b2()

如您所见,我只想扩展基类的某些属性,并且我希望能够通过简单地指定应在函数additional_fields中扩展的属性来做到这一点。

我有两个问题:我不知道如何调用双亲__init__方法,上面显示的两种方法都给出错误第二个是,我不知道如何定义一个匿名函数(?)例如,如果我将c添加到c列表中,将对属性additional_fields进行同样的操作。

1 个答案:

答案 0 :(得分:0)

一种方法是为其使用元类:

class LibraryBase:  
    # imagine it is placed it third-party library

    def __init__(self, a=0, b=1, c=2):
        self.a = a
        self.b = b
        self.c = c

class Meta(type):
    def __new__(cls, name, bases, attrs):
        for name in attrs['additional_fields']:
            name2 = '{}2'.format(name)
            get_name2 = 'get_{}'.format(name2)

            def getter_name2(name2, name):
                def _get_name2(self):
                    return getattr(self, name2, None) or getattr(self, name)
                return _get_name2

            attrs[get_name2] = getter_name2(name2, name)
        return type.__new__(cls, name, bases, attrs)


class Base(LibraryBase, metaclass=Meta):  
    # we still can inherit from library class with our metaclass

    additional_fields = []

    def __init__(self, a=0, b=1, c=2, **kwargs):
        self.a = a
        self.b = b
        self.c = c
        for key, value in kwargs.items():
            setattr(self, key, value)
        for name in self.additional_fields:
            name2 = '{}2'.format(name)
            setattr(self, name2, kwargs.get(name2))


class ExtendBase(Base):
    additional_fields = ['a', 'b']

class ExtendBase2(Base):
    additional_fields = ['a', 'b', 'c']


test = ExtendBase(a=3, a2=9)

print(test.a)  # 3
print(test.a2)  # 9
print(test.get_a2())  # 9

print(test.b)  # 1
print(test.b2)  # None
print(test.get_b2())  # 1

print(test.c)  # 2
print(test.c2)  # AttributeError
print(test.get_c2())  # AttributeError