在一个类中初始化另一个类,只传递已设置的属性

1 投票
1 回答
820 浏览
提问于 2025-04-18 00:43

我有一个类,这个类里面应该包含几个其他的类作为它的属性。我觉得这叫做组合。现在,我想根据这些被包含的类在外部类中是否被设置来初始化它们的属性。在下面的例子中,我只是用None来覆盖这两个小类的默认值,但我并不想这样做:

class OneAttribute:
    def __init__(self,a=1):
        self.a = a

class TwoAttributes:
    def __init__(self,b=2,c=3):
        self.b = b
        self.c = 3

class SuperClass:
    def __init__(self,a=None,b=None,c=None):
        '''
        This would override the defaults in the
        other two classes, which is bad.
        '''
        self.one = OneAttribute(a=a)
        self.two = TwoAttributes(b=b,c=c)

我可以用下面的方式定义外部类,但这样做既不好看也不易扩展简直糟糕:

Class SuperClass:
    def __init__(self,a=None,b=None,c=None):
        if a is not None:
            self.one = OneAttribute(a=a)
        else:
            self.one = OneAttribute()

        if b is not None and c is not None:
            self.two = TwoAttributes(b=b, c=c)
        elif b is not None:
            self.two = TwoAttributes(b=b)
        elif c is not None:
            self.two = TwoAttributes(c=c)
        else:
            self.two = TwoAttributes()

我想实现的这个设计,可能是个糟糕的设计决定吗?

1 个回答

2

一种解决办法是让你的类可以接受额外的关键字参数。

class OneAttribute:
    def __init__(self,a=1,**kwargs):
        self.a = a

class TwoAttributes:
    def __init__(self,b=2,c=3,**kwargs):
        self.b = b
        self.c = c

这样,你就可以在容器类中简单地接受这些关键字参数。

class SuperClass:
    def __init__(self, **kwargs):
        self.one = OneAttribute(**kwargs)
        self.two = TwoAttributes(**kwargs)

不过,这样的缺点是,如果你给里面的两个类传了额外的参数,它们可能不会被发现。

要避免这个问题是可以的,但需要一些技巧:首先,你照常声明内部类:

class OneAttribute:
    def __init__(self,a=1):
        self.a = a

class TwoAttributes:
    def __init__(self,b=2,c=3):
        self.b = b
        self.c = c

然后你可以通过反射来发现它们期望哪些参数。

def parms_of(f):
    return f.__code__.co_varnames[:f.__code__.co_argcount]

最后,只把那些参数传给内部类的构造函数。

def filter_kwargs(kwargs, f):
    s = set(parms_of(f))
    return dict((k, v) for k, v in kwargs.items()
                if k in s)

class SuperClass:
    def __init__(self, **kwargs):
        self.one = OneAttribute(**filter_kwargs(kwargs, OneAttribute.__init__))
        self.two = TwoAttributes(**filter_kwargs(kwargs, TwoAttributes.__init__))

撰写回答