Home > front end >  Inherit List of Self, recursive definition
Inherit List of Self, recursive definition

Time:10-21

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)
  • Related