LoginSignup
6
1

More than 5 years have passed since last update.

リスト内包表記内でsuper()の引数は省略できない

Posted at

リスト内包表現で、引数なしの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]
6
1
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1