リスト内包表現で、引数なしのsuper()を使うとTypeErrorになる
test1.py
>>> class A:
... def f(self):
... return None
...
>>> class B(A):
... def g(self):
... return [super().f() for i in range(1)]
...
>>> B().g()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in g
File "<stdin>", line 3, in <listcomp>
TypeError: super(type, obj): obj must be an instance or
subtype of type
ジェネレータ式でも同じ
test2.py
>>> class A:
... def f(self):
... return None
...
>>> class B(A):
... def g(self):
... return (super().f() for i in range(1))
...
>>> B().g()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in g
File "<stdin>", line 3, in <listcomp>
TypeError: super(type, obj): obj must be an instance or
subtype of type
また、python2では、引数なしでsuper()
は使用できないので、そもそもこの問題は発生しない。
原因
おそらく原因は、リスト内包表記内(または、ジェネレータ式)では、関数内と別スコープになっているからと推測できる。
test3.py
>>> class A:
... def f(self):
... return None
...
>>> class B(A):
... def func_scope(self):
... return locals()
... def comprehension_scope(self):
... return [locals() for i in range(1)]
...
>>> B().func_scope()
{'self': <__main__.B object at 0x6ffffc60550>}
>>> B().comprehension_scope()
[{'i': 0, '.0': <range_iterator object at 0x6ffffcd2870>}]
蛇足だが、リスト内包表記内のスコープでは'.0'でiteratorにアクセスできるようだ。
(Pythonの仕様か、CPythonがこう実装しているだけなのかは不明)
対策
引数で対象のクラス、オブジェクトを指定すれば、関数スコープ以外でも使用可能なので、問題なくに使用できる。
test4.py
>>> class A:
... def f(self):
... return None
...
>>> class B(A):
... def g(self):
... return [super(self.__class__, self).f() for i in range(1)]
...
>>> B().g()
[None]