关于python:如何扩展大类方法?

How to Extend Large Class Methods?

如果要扩展一个类及其各自的__init__方法,是否有方法处理大量参数(包括新参数)?例如:

1
2
3
4
5
6
7
8
class Thing:
    def __init__(self, arg1, arg2, arg3, arg4):
        # Initialize

class SubThing(Thing):
    def __init__(self, arg1, arg2, arg3, arg4, ex_arg1, ex_arg2):
        super().__init__(arg1, arg2, arg3, arg4):
        # Do extra initialization

是否可以缩短子类的__init__方法的参数列表?像这样的答案不描述如何向init添加参数,也不描述如何处理额外的关键字参数,如__init__(self, arg1, arg_room='Bedroom', ex_arg1, ex_position=(0, 0), ex_arg2)


除了删除特定于子类的项之外,您可以执行与引用的答案类似的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Thing:
    def __init__(self, arg1, arg2, arg3, arg4, kw1=None, kw2=None):
        print('Thing args:',arg1,arg2,arg3,arg4)
        print('Thing kw:',kw1,kw2)

class SubThing(Thing):
    def __init__(self, *args, **kw):
       """SubThing(arg1, arg2, arg3, arg4, arg5, arg6, kw1=None, kw2=None,
        kw3=None, kw4=None)
       """

        if len(args) != 6:
            raise TypeError(
               "SubThing() takes 6 positional arguments but {} were given"
                .format(len(args)))
        arg5, arg6 = args[4:]
        kw3 = kw.pop('kw3', None)
        kw4 = kw.pop('kw4', None)
        super().__init__(*args[:4], **kw)


SubThing(1,2,3,4,5,6,kw1='kw1',kw4='kw4')
print('---- but populating kwargs from positional args stops working ----')
Thing(1,2,3,4, 'kw1', 'kw2')
SubThing(1,2,3,4,5,6,'kw1','kw2')

这导致

1
2
3
4
5
6
7
8
9
10
11
Thing args: 1 2 3 4
Thing kw: kw1 None
---- but populating kwargs from positional args stops working ----
Thing args: 1 2 3 4
Thing kw: kw1 kw2
Traceback (most recent call last):
  File"n.py", line 24, in <module>
    SubThing(1,2,3,4,5,6,'kw1','kw2')
  File"n.py", line 14, in __init__
    .format(len(args)))
TypeError: SubThing() takes 6 positional arguments but 8 were given

注意,在第二个示例中,您丢失了一些功能,因为额外的位置参数不会自动填充关键字参数。您可以通过更多的编码来解决这个问题,或者只接受这些限制。很多使用*args, **kw的类都忘记了这一点,所以您处于良好的伙伴关系中。


做你想做的事没有什么好方法。一般来说,命名所有参数是主要的方法。

不过,有几种方法可能会有所帮助。一种方法是将一些参数绑定到更大的集合中,因此只需要将一个项传递给父类,而不需要将几个项传递给父类。下面是使用一个可重复序列来保存arg1--arg4,但您可能希望使用一个自定义类(参见csv模块中的Dialect类,例如一个为其他类型打包多个参数的类型的例子):

1
2
3
def __init__(self, thing_args, ex_arg1, ex_arg2):
    super().__init__(*thing_args)
    # ...

另一种方法是使用*args和/或**kwargs接收您想要传递的参数。使用位置*args是相当有限的,因为您必须在传递的所有参数之前在子类中列出您关心的参数:

1
2
3
def __init__(self, ex_arg1, ex_arg2, *args):
    super().__init__(*args)
    # ...

调用方需要首先列出ex_argsN参数的值,这可能是不自然的。子类越多,情况就越糟。

要求将所有内容作为关键字参数传递可能更好,因为调用方可以将关键字参数按最自然的顺序放置:

1
2
3
def __init__(self, *, ex_args1, ex_args2, **kwargs):
    super.__init__(self, **kwargs) # passes along the unnamed keyword args
    # ...

这种方法最大的缺点是调用会变得相当冗长,其中包含所有参数名:

1
2
st = SubThing(arg1="foo", arg2="bar", arg3="baz", arg4="quux",
              ex_arg1=1, ex_arg2=6.02e23)