Personal book library Very new to OOP with Python - and feel like I still don't totally get OOP. However I thought I'd put together a personal online book library to practice inheritance, methods, docstrings etc.
Program allows one to add books (comics or novels - with comics inheriting from novels) to one's own library, and create a custom reading lists.
There are a few different methods. For example, a library method which will return all books over a specified length.
Note that I've removed quite a lot from this, just so it's not 5 pages long! I would normally have several different types of books, attributes such as Author etc. But hopefully from the below you get a good idea of the structure.
Speaking of structure, I would just like to know if mine is any good. Also there is an issue whereby if you remove a book from the library, it will still show up in any reading lists I created. I don't know if there's a way to deal with this, or if I'm thinking about this the wrong way.
class Library:
    
    def __init__(self, books = None):
        if books is None:
            self.books = []
        else:
            self.books = books

    def add_book(self, book):
        self.books.append(book)
        
    def remove_book(self, book):
        self.books.remove(book)
        
    def pages_over_n(self, number):
        &quot;&quot;&quot;Returns books that have more than specified number of pages&quot;&quot;&quot;
        return [book for book in self.books if book.pages &gt; number]
    
    def get_library(self):
        &quot;&quot;&quot;Returns all books in Library&quot;&quot;&quot;
        return self.books
    
    def __repr__(self):
        return f&quot;Library({self.books})&quot;
    
    def __str__(self):
        return f&quot;Library has {len(self.books)}&quot;

class Novel:
    
    def __init__(self, title, pages, publicationDate):
        self.title = title
        self.pages = pages
        self.publicationDate = publicationDate
        
    def __repr__(self):
        return f&quot;Novel({self.title}, {self.pages}, {self.publicationDate})&quot;
    
    def __str__(self):
        return f&quot;Media: {self.title}&quot;
    
class ComicBook(Novel):
    
    def __init__(self, title, pages, publicationDate, artist):
        super().__init__(title, pages, publicationDate)
        self.artist = artist
        
    def get_artist(self):
        return f&quot;Artist: {self.artist}&quot;
        
    def __repr__(self):
        return (f&quot;ComicBook({self.title}, {self.pages}, &quot; 
                f&quot;{self.publicationDate}, {self.artist})&quot;)
    
class ReadingList:
    
    def __init__(self):
        self.list = []
    
    def add_book(self, book):
        &quot;&quot;&quot;Add book to reading list&quot;&quot;&quot;
        self.list.append(book)
        
    def remove_book(self, book):
        &quot;&quot;&quot;Remove book from reading list&quot;&quot;&quot;
        self.list.remove(book)

    def total_pages(self):
        &quot;&quot;&quot;Returns total pages in reading list&quot;&quot;&quot;
        total = 0
        for book in self.list:
            total += book.pages
        return total

    def __repr__(self):
        return f&quot;ReadingList({self.list})&quot;
    
    def __str__(self):
        return f&quot;Reading list of size {len(self.list)}&quot;

# Initialise Library
library = Library()

# Create a few books
novel1 = Novel(&quot;Harry Potter&quot;, 500, 1991)
novel2 = Novel(&quot;LotR&quot;, 1000, 1960)
novel3 = Novel(&quot;Game of Thrones&quot;, 2000, 2018)
comic1 = ComicBook(&quot;Batman&quot;, 100, 2020, &quot;Stan Lee&quot;)

# Add books to library.
library.add_book(novel1)
library.add_book(novel2)
library.add_book(novel3)
library.add_book(comic1)

# Create a new reading list.
readingListOne = ReadingList()

# Add a couple of books to reading list.
readingListOne.add_book(novel1)
readingListOne.add_book(comic1)
Simplify the constructor of class Library
You have a default value of None for the parameter books, but then immediately proceed to convert that None to an empty list. Why not use [] as the default value?
class Library:
    def __init__(self, books = []):
        self.books = list(books)

    ...

Note that to prevent the issue of mutable default arguments as mentioned by ojdo, you should make a copy of the initial list.
Be consistent
You take an optional list of books in the constructor of class Library, but there is no such thing in class ReadingList. That is a bit surprising.
Also be consistent with naming things. In class Library, the list of books is called books, but in class ReadlingList you named it list. I would name it books in both cases.
Also be consistent with the names of functions:

get_library() should be get_books(), since it only returns the list of books, not an object of type Library.
pages_over_n() should be get_books_with_over_n_pages() or something similar, to indicate that it returns Books, not pages.
total_pages() should be renamed get_total_pages() or calculate_total_pages(). Prefer using verbs for function names, and nouns for variables.

And I also wonder why a ComicBook has an artist, but a Novel has no author? And why is there a get_artist() but no get_title(), get_publication_date() and so on? Making everything consistent will make using your classes much easier.
Removing books from the Library
As you noticed, you can remove a book from the library, but it won't be removed from any reading lists. But that is fine. Why would a book magically disappear from a real life reading list if your library removes that book? Note that your code is perfectly fine, Python won't actually delete the Book object from memory until the last reference to it is gone.
However, if you want a book to disappear from the ReadingList when it is removed from the Library, then somehow there needs to be some communications between those two objects. There are two options:

A Library keeps a list of ReadingLists that reference its Books, and when you call remove_book() on a Library, it will in turn iterate over all the ReadingLists and call remove_book() on them as well.

A ReadingList keeps a reference to the Library whose Books it contains. Whenever you call a member function that accesses the list of books on the reading list, this member function first has to filter out all the books that are no longer in the Library.