问题

今天学习《Python Web 开发实战》自定义转换器这一小节,书中有段代码如下:

class ListConverter(BaseConverter):

    def __init__(self, url_map, separator="+"):
        super(ListConverter, self).__init__(url_map)
        self.separator = urllib.parse.unquote(separator)

    def to_python(self, value):
        return value.split(self.separator)

    def to_url(self, values):
        return self.separator.join(super(ListConverter, self).to_url(value)
                                   for value in values)

倒没什么问题,是可以正常运行的。

然而我对 super() 的用法不是很满意,毕竟在 Python3 环境下开发,super(ListConverter, self) 有硬编码嫌疑。于是代码修改如下:

class ListConverter(BaseConverter):

    def __init__(self, url_map, separator="+"):
        super().__init__(url_map)
        self.separator = urllib.parse.unquote(separator)

    def to_python(self, value):
        return value.split(self.separator)

    def to_url(self, values):
        return self.separator.join(super().to_url(value)
                                   for value in values)

结果一运行,解释器报错如下:
TypeError: super(type, obj): obj must be an instance or subtype of type

原因

经过多次尝试,发现问题出现在 self.separator.join(super().to_url(value) for value in values) 这段代码里。于是我做了个测试。

class A(object):
    def print_what(self, what):
        print(what)

class B(A):
    def print_what(self, what):
        [super() for __ in range(5)]

if __name__ == "__main__":
    b = B()
    b.print_what("hello")

------------------------------------
TypeError: super(type, obj): obj must be an instance or subtype of type

果然异常,当我将 super() 改为 super(B, self) 又一切正常。

是因为 super() 在一个成员函数中多次调用造成的吗?那就不用列表解析式,换寻常的循环语句:

class B(A):
    def print_what(self, what):
        for __ in range(5):
            super()

结果运行正常。

事实上,在成员函数中使用 super() 时,Python 会自动传参,我猜测是解析式影响了传进去的参数。但遗憾的是,我不知道该如何调试,才能获得错误情况下,传进去的参数是什么。

解决方案

针对此次遇到的问题,解决方法有以下三种。

第一种

使用 super(ListConverter, self) 代替 super()

第二种

不用解析式,用寻常的 for 循环。

class ListConverter(BaseConverter):
    ...
    def to_url(self, values):
        tmplist = []
        for value in values:
            tmplist.append(super().to_url(value))
        return self.separator.join(tmplist)

第三种

使用解析式,但不在解析式中调用 super()。

class ListConverter(BaseConverter):
    ...
    def to_url(self, values):
        sup = super()
        return self.separator.join(sup.to_url(value)
                                   for value in values)


本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论