I would like to build a recursive-type class, where the class is a list of things including itself. How can this be achieved? It doesn't seem to work even with from __future__ import annotations
.
from __future__ import annotations
from typing import Union, List
class A:
pass
class AList(List[Union[A, AList]]):
pass
The intent is the take advantage of various builtin methods of list
, e.g. append
, extend
, __iter__
, etc., while also be able to add custom methods.
It's possible to simply subclass list
. Is it adding anything to specify the types elements I will have in AList via List[Union[A, AList]]
?
CodePudding user response:
The following syntax:
class AList(List[Union[A, AList]]):
I don't think it's what you're trying to do. It basically says that the class AList
is a subclass of list
.
I think something like this is what you want. This should work for Python 3.7 with the included __future__
import, though I'm not sure how mypy
would handle it (it seems fine on Pycharm at least)
from __future__ import annotations
from dataclasses import dataclass
class A:
...
@dataclass
class AList:
data: list[A | AList]
@staticmethod
def my_custom_method():
print('Hello world!')
# creating instance of A
a = A()
# does not type check
my_list = AList(a)
my_list = AList([1, 2])
# type checks
my_list1 = AList([a])
my_list2 = AList([my_list1])
# True
assert my_list1.data[0] == a
assert my_list2.data[0] == my_list1
print(my_list1)
print(my_list2)
my_list2.my_custom_method()
# Hello world!
Subclassing from list
Note that you can still access the list object from the above example via the data
attribute, which should support list operations like append
and extend
for example.
If you do intend to subclass from list
, the following approach should work and also work well in Pycharm for example.
Note that I remove the __future__
import here, since it's not needed actually; as the annotation A | AList
is already forward declared in this case, as a string.
from typing import List
class A:
...
class AList(List['A | AList']):
def first_element(self):
return self[0]
# creating instance of A
a = A()
# does not type check
my_list = AList(["testing"])
my_list = AList([1, 2])
# type checks
my_list1 = AList([a])
my_list2 = AList([my_list1])
# True
assert my_list1[0] == a
assert my_list2[0] == my_list1
print(my_list1)
print(my_list2)
elem = my_list2.first_element()
print(elem)
# [<__main__.A object at 0x103809750>]
# True
assert isinstance(elem, AList)