You can also take a look at llist python package, which provides some useful features that deque does not. There are not only doubly linked lists, but also single linked lists data structure in that package. IMHO, one of the biggest advantages of this package is the ability to store a reference to the llist elements.

Answer from Alexander Zhukov on Stack Overflow
🌐
GeeksforGeeks
geeksforgeeks.org › python › python-library-for-linked-list
Python Library for Linked List - GeeksforGeeks
July 15, 2025 - Storage is preferred over performance and not all elements get a separate node of their own · Method 2: Using llist package. The llist is an extension module for CPython providing basic linked list data structures.
🌐
Medium
stephenagrice.medium.com › how-to-implement-a-linked-list-in-python-7b9ce28d9c1e
How to Implement a Linked List in Python | by Steve Grice | Medium
June 21, 2023 - Typing python3 on the command prompt ... import it. If your linked list is stored in linked_list.py, then simply type from linked_list import LinkedList....
🌐
CodingNomads
codingnomads.com › data-structure-linked-list-python
Linked Lists in Python
Of course, in the real world, you don't have to manipulate and manage Node objects directly. You create a LinkedList class, which will manage the Node objects. Here's what a linked list might look like implemented in Python: import Node class LinkedList: def __init__(self, node=None): """ Construct a new Linked List """ self.head = node
🌐
pytz
pythonhosted.org › llist
llist — Linked list datatypes for Python — llist 0.4 documentation
... >>> from llist import sllist, sllistnode >>> empty_lst = sllist() # create an empty list >>> print(empty_lst) sllist() >>> print(len(empty_lst)) # display length of the list 0 >>> print(empty_lst.size) 0 >>> print(empty_lst.first) # display the first node (nonexistent) None >>> ...
🌐
DEV Community
dev.to › hspedro › implementing-linked-lists-in-python-3iff
Implementing Linked Lists in Python - DEV Community
September 30, 2024 - Pretty straightforward, as it is a Singly-Linked list, we only need to define a field for the next, pointing to the successor, and a field for data, which can receive any type of data. from __future__ import annotations from typing import Any, ...
🌐
DEV Community
dev.to › georgeoffley › linked-lists-in-python
Linked Lists in Python - DEV Community
July 28, 2017 - So each of our nodes now has a reference to the next node and we have ourselves a singularly-linked list. There’s another method we can add in here as well to print out our list in full. def print_list(node): while node: print(node), node = node.next print · So then we import our print_list ...
🌐
structlinks
eeshannarula29.github.io › structlinks › linked_lists.html
LinkedLists | structlinks
from structlinks.DataStructures import LinkedList lst = LinkedList([1, 10, 3, 5]) # Map function f(x) = x^2 new_lst = lst.map(lambda x: x ** 2) print(new_lst) # Output: # [25 -> 9 -> 100 -> 1] from structlinks.DataStructures import LinkedList lst = LinkedList([1.1, 10.5, -3.7, 5.2]) #abs abs_lst = lst.abs() print(abs_lst) # Output: # [1.1 -> 10.5 -> 3.7 -> 5.2] # floor floor_lst = lst.floor() print(floor_lst) # Output: # [1.0 -> 10.0 -> 3.0 -> 5.0] # ceil ceil_lst = lst.ceil() print(ceil_lst) # Output: # [2.0 -> 11.0 -> 4.0 -> 6.0]
Find elsewhere
🌐
PyPI
pypi.org › project › LinkedList-575
LinkedList-575 · PyPI
August 6, 2023 - Access and update elements using index (Just like noraml list in python). Perform element-wise division with a numeric value (for numeric linked list). Perform element-wise multiplication with a numeric value (for numeric linked list). ... The linked list is present in linked_list.py file of LinkedList package. To use linked list in your project, you needed to import.
🌐
W3Schools
w3schools.com › python › python_dsa_linkedlists.asp
Linked Lists with Python
A Linked List is, as the word implies, a list where the nodes are linked together. Each node contains data and a pointer.
🌐
PyPI
pypi.org › project › py-linkedlist
Client Challenge
JavaScript is disabled in your browser · Please enable JavaScript to proceed · A required part of this site couldn’t load. This may be due to a browser extension, network issues, or browser settings. Please check your connection, disable any ad blockers, or try using a different browser
🌐
PyPI
pypi.org › project › llist
llist · PyPI
This extension requires CPython 2.5 or newer (3.x is supported). If you are looking for an implementation of linked lists in pure Python, visit http://github.com/rgsoda/pypy-llist/ The pypy-llist module has the same API as this extension, but is significantly slower in CPython.
🌐
Stack Abuse
stackabuse.com › python-linked-lists
Python Linked Lists
August 25, 2023 - The underlying data structure of deque is a Python list which is double-linked. The first list node has the index 0. Using deque leads to a significant simplification of the ListNode class. The only thing we keep is the class variable data to store the node value: from collections import deque class ListNode: def __init__(self, data): "constructor class to initiate this object" # store data self.data = data
🌐
Real Python
realpython.com › linked-lists-python
Linked Lists in Python: An Introduction – Real Python
June 24, 2026 - In this article, you'll learn what linked lists are and when to use them, such as when you want to implement queues, stacks, or graphs. You'll also learn how to use collections.deque to improve the performance of your linked lists and how to implement linked lists in your own projects.
🌐
GeeksforGeeks
geeksforgeeks.org › python › python-linked-list
Python Linked List - GeeksforGeeks
December 11, 2025 - A linked list is a type of linear data structure individual items are not necessarily at contiguous locations.
🌐
Python.org
discuss.python.org › python help
Inbuilt module implementing doubly linked list - Python Help - Discussions on Python.org
October 6, 2023 - Why doesn’t Python have an inbuilt module for linked lists? There must be one for it, just like a list in C++ STL.
Top answer
1 of 4
7

Summary

I won't dwell on what has already been cited by users toolic and J_H, so I just have a few comments:

Type Hinting

I would suggest that you include type hinting, especially if your functions do not contain docstrings that describe the type of arguments being passed to functions (J_H has suggested this, so pardon if this is too repetitive).

Be More Tolerant of Errors in User Input

If the user does not enter a valid integer in function run_and_add, you essentially quit. You should instead put out the prompt again and give the user as many chances needed to enter valid input. The user can always terminate by entering Ctrl-C if they get stuck.

Strive for Encapsulation and Reusability

I can't stress too strongly that your code is crying out for you to create a LinkedList abstract data type that abstracts the notion of a linked list while encapsulating the actual implementation. To that end, I would use attribute names that begin with '_' where appropriate to suggest that they are "private" and not to be either updated nor depended on existing in the future (such as the next instance attribute of the ListNode class.

The following classes are just one possibility. Note:

  1. There is no print method implemented since printing the entire list is trivial given that the class implements the iterator protocol. Besides, what if you wanted to print to a file? Then a print method would require one or more additional arguments.
  2. The client never explicitly creates ListNode instances.
  3. The linked list keeps explicit track of the final (last) node in the list to provide efficient appending of a node or an entire linked list to the end.

I can envision your using this as a starting point and potentially adding other methods (for example, __eq__ methods to compare nodes and linked lists).

"""A module for creating and manipulating linked lists."""

from abc import ABC, abstractmethod
from typing import TypeVar, Any

LinkedListInstance = TypeVar('LinedListInstance', bound='LinkedList')

class NodeType(ABC):
    @property
    @abstractmethod
    def val(self):
        pass

    @val.setter
    @abstractmethod
    def val(self, val):
        pass

class LinkedList:

    class _ListNode(NodeType):
        """Initialize a new node with some value, val."""

        def __init__(self, val):
            self._val = val
            self._next = None

        @property
        def val(self):
            return self._val

        @val.setter
        def val(self, val):
            self._val = val

        def __repr__(self):
            return f'_ListNode({repr(self._val)})'

        def __str__(self):
            return str(self._val)

    def __init__(self):
        """Create a new, empty linked list."""

        self._head = None
        self._tail = None

    def append_node(self, val: Any) -> LinkedListInstance:
        """Append a new node to the list initialized with val."""

        new_node = LinkedList._ListNode(val)
        if self._head is None:
            self._head = new_node
        else:
            self._tail._next = new_node
        self._tail = new_node

        return self

    def insert_node(self, at_node: NodeType, val: Any) -> LinkedListInstance:
        """Create and insert a new node after the specified at_node node initialized
        with val."""

        if at_node is self._tail:  # special case
            return self.append_node(val)
        node_to_insert = LinkedList._ListNode(val)
        node_to_insert._next = at_node._next
        at_node._next = node_to_insert

        return self

    def append_list(self, linked_list: LinkedListInstance) -> LinkedListInstance:
        """Append a linked list to the current list."""

        if self._head is None:
            self._head = linked_list._head
        else:
            self._tail._next = self._head
        self._tail = linked_list._tail

        return self

    def __iter__(self) -> NodeType:
        """Iterate the list."""

        current = self._head
        while current is not None:
            yield current
            current = current._next

if __name__ == '__main__':
    def insert_node_at_position(linked_list: LinkedList, position: int) -> None:
        for counter, current_node in enumerate(linked_list, start=1):
            print(f"Node at position {counter}: {current_node}")
            if counter == position:
                while True:
                    try:
                        number = int(input("Please insert an Integer: "))
                    except ValueError:
                        print("Not an Integer")
                    else:
                        break
                linked_list.insert_node(current_node, number)
                print("Node added at position:", position)

        print("Updated linked list:")
        for node in linked_list:
            print(node)

    linked_list = LinkedList().append_node(1).append_node(2).append_node(3)
    insert_node_at_position(linked_list, 2)
2 of 4
6

names

class ListNode:

This is a perfectly fine identifier, as-is.

There's no adjacent code that uses other node types. Consider shortening to just Node.

design of Public API

OO

def print_linked_list(head):
...
def add_node(prev_node, node_to_add):
...

These are somewhat unexpected signatures, the sort of thing I might expect in Fortran code. ListNode turned out to be just a very brief @dataclass, with no OO aspect to it. Given a ListNode, we find no methods to call on it for list operations. This works, but makes it a little harder for developers and maintenance engineers to discover your API. For example if I hit a breakpoint() I cannot p dir(node) to find plausible things I might do with a node -- I instead have to scour the codebase for such operations.

Also, your signatures lack ListNode type annotations, so I can't just grep for that or use type-aware IDE features to narrow my search.

I propose some more natural implementations.

    def print_linked_list(self):
        head = self
        while head:
            print(head.val)
            head = head.next

    def add_node(self, node_to_add):
        assert node_to_add.next is None
        node_to_add.next = self.next
        self.next = node_to_add

Consider renaming these to simply .print() and .insert().

interactive input vs parameter

(I am paraphrasing, renaming the vague number to new_val.)

def run_and_add(head, position):
                ...
                new_val = int(input("Please insert an Integer: "))

Prefer to place calls of input() further up in the call stack, such as within def main():, and pass in such a value as a parameter:

def run_and_add(head, position, new_val):

main guard

On which topic, you don't have a main() function, and you really need one. Why? So you or some maintenance engineer can safely import linkedlist when exercising your functions in a test suite. Also, it's convenient to ensure that local variables like first (which are not part of your exported Public API) will disappear when they go out of scope. That way such identifiers won't pollute the module namespace.

def main():
    first = ListNode(1)
    first.next = ListNode(2)
    first.next.next = ListNode(3)
    run_and_add(first, 2)

if __name__ == '__main__':
    main()

single responsibility

run_and_add() is an awkward identifier, suggesting that instead of one we're doing two things. Also I find "run" less than clear.

Consider making caller responsible for passing in an already-created node, and then this could be a simple insert_at_position(head, position, new_node) function.

🌐
Expert-programming-tutor
expert-programming-tutor.com › tutorial › article › KC006000100001_Using_Python_to_Create_and_Manage_Data_with_Linked_Lists.php
การใช้งาน Python ในการสร้างและจัดการข้อมูลด้วย Linked List | EPT | Expert-Programming-Tutor
Linked List เป็นโครงสร้างข้อมูลที่ประกอบไปด้วยองค์ประกอบหรือ 'โหนด' ที่ถูกเชื่อมต่อกันด้วยการอ้างอิงหรือลิงก์ แต่ละโหนดมีสองส่วนหลัก คือ ข้อมูล (data) และอ้างอิงไปยังโหนดถัดไป (next) ในกรณีของ Doubly Linked List ยังจะมีส่วนอ้างอิงถึงโหนด