Problem 480030 · medium · Level 04 Non-Linear Data Structures

A Playlist That Acts Like a List

py-dunder · py-dataclasses · py-properties · sequence protocol

A music app's playlists should feel like lists: you can ask for their length, take the third track or the last five, loop over them, check whether a song is on them and join two of them. But a playlist is not just a list: it has a name, a total duration and its own way of printing itself.

Write two classes:

  • Track(title, artist, seconds): a record with those three fields, printed like Track(title='Hey', artist='Ola', seconds=185). Tracks with the same fields are equal and can be put in a set.
  • Playlist(name, tracks=()), where tracks is any iterable of Track objects:
    • len(p) is the number of tracks; p[i] is a track (negative indices count from the end; an index out of range raises IndexError);
    • p[i:j], p[::2] and every other slice give a new Playlist with the same name;
    • for t in p, list(p) and reversed(p) go through the tracks;
    • x in p is True if x is a track on the playlist, or a string equal to the title of one;
    • p + q is a new playlist named "<p's name> + <q's name>" with p's tracks followed by q's;
    • p.seconds is the total length in seconds, as an attribute without brackets;
    • str(p) is "<name>: <n> tracks, <duration>" with 1 track for a single track, and the duration as M:SS, or H:MM:SS from one hour.

Changing the iterable you passed in afterwards must not change the playlist. The setup's library(n, seed) returns a list of n generated tracks.

Examples

Input:  p = Playlist("Road trip", [Track("Hey", "Ola", 185), Track("Go", "Kai", 240), Track("Up", "Ola", 200)])
        len(p), p[-1].title, str(p[1:]), "Go" in p, [t.title for t in reversed(p)]
Output: (3, 'Up', 'Road trip: 2 tracks, 7:20', True, ['Up', 'Go', 'Hey'])

Input:  str(p + Playlist("Extra", [Track("Long", "Mo", 3000)])), p.seconds
Output: ('Road trip + Extra: 4 tracks, 1:00:25', 625)

Constraints

  • Up to 10**4 tracks; seconds are positive integers.

Goals

  • Make a class work with `len`, indexing, slicing, `for`, `in`, `reversed` and `+`
  • Return an object of your own class from a slice or a sum, not a plain list
  • Keep the tracks private behind the sequence interface
Starting Python…