DEV Community

Cover image for Quark's Outlines: Additional Methods for Emulation of Python
Mike Vincent
Mike Vincent

Posted on

Quark's Outlines: Additional Methods for Emulation of Python

Quark’s Outlines: Additional Methods for Emulation of Python Sequence Types

Overview, Historical Timeline, Problems & Solutions

An Overview of Additional Methods for Emulating Python Sequence Types

What are the extra Python methods for sequence emulation?

When you want to make your own class behave like a list or string, Python lets you define special methods. Some methods are used when the object is changed. Others are used when the object is read. Python gives you three special methods to support slice behavior in a way that matches the built-in sequence types.

These methods are:

  • __getslice__(self, i, j) — for reading slices
  • __setslice__(self, i, j, sequence) — for setting slices
  • __delslice__(self, i, j) — for deleting slices

These methods work only with basic slice syntax using one colon, like a[1:5]. They do not handle extended slices like a[::2]. For those, Python uses __getitem__, __setitem__, or __delitem__.

Python lets your class act like a sequence using __getslice__.

class Demo:
    def __init__(self, items):
        self.data = items
    def __getslice__(self, i, j):
        return Demo(self.data[i:j])

d = Demo([1, 2, 3, 4])
print(d[1:3].data)
# prints:
# [2, 3]
Enter fullscreen mode Exit fullscreen mode

Here, d[1:3] calls __getslice__, which returns a new Demo object with the sliced values.

What happens with missing or negative values?

When Python calls __getslice__, it fills in missing values. If i is not written, Python gives it the value 0. If j is not written, Python uses len(self). Also, negative values are already changed to their positive equivalents when the method is called. This is different from how __getitem__ handles slices.

Python handles missing or negative values before calling __getslice__.

class Demo:
    def __init__(self, items):
        self.data = items
    def __getslice__(self, i, j):
        return self.data[i:j]

d = Demo([10, 20, 30, 40])
print(d[:-1])
# prints:
# [10, 20, 30]
Enter fullscreen mode Exit fullscreen mode

Python adjusts the -1 to the correct positive index before calling the method.


A Historical Timeline of Python Sequence Emulation

Where do Python’s sequence emulation methods come from?

Python was designed to support flexible object models. Early in its development, Python added support for special methods that let user-defined classes act like built-in types. Sequence emulation followed this path, giving custom classes the ability to act like lists using slice notation.


People built systems to support sequence-like types

1960 —Indexing and slicing arrays in ALGOL made it easier to work with parts of sequences.

1980 —User-defined types with slicing appeared in languages like ABC, which shaped Python.


People added slice emulation to Python

1991 —Basic slicing syntax in Python 0.9.0 supported a[i:j] on lists and strings.

1994 —__getslice__, __setslice__, __delslice__ introduced in Python 1.0 to let classes handle slice behavior directly.

2001 —Extended slices using a[i:j:k] added in Python 2.0, but handled with __getitem__ instead.

2018 —Old slice methods deprecated in favor of __getitem__ with slice() objects for clarity and consistency.


Problems & Solutions with Python Slice Emulation Methods

How do you use additional Python slice methods the right way?

Python gives you special methods to make a class act like a sequence. These help your object support slice reads, changes, and deletions when using basic slice syntax. These problems show how each method helps you shape custom behavior for your data.


Problem: How do you return part of your object using slice syntax in Python?

You have a custom class that stores a list of numbers. You want to support obj[1:4] to return just part of the values. You want the result to be another object of the same kind.

Problem: You want to return a sliced part of your object with matching type.

Solution: Python lets you define __getslice__ to return a custom object using sliced values.

Python lets you define __getslice__ to support simple read slices.

class Bag:
    def __init__(self, items):
        self.data = items
    def __getslice__(self, i, j):
        return Bag(self.data[i:j])

b = Bag(['a', 'b', 'c', 'd'])
print(b[1:3].data)
# prints:
# ['b', 'c']
Enter fullscreen mode Exit fullscreen mode

The method __getslice__ returns a new Bag that holds part of the original data.


Problem: How do you let someone assign to a slice in Python?

You want your class to act like a mutable list. You want to allow code like obj[1:3] = ['x', 'y'] to change part of the stored list. Without a special method, Python will not know what to do.

Problem: You want to allow assignment to slices in your object.

Solution: Python lets you define __setslice__ to change values in a slice.

Python lets you assign to a slice using __setslice__.

class Bag:
    def __init__(self, items):
        self.data = items
    def __setslice__(self, i, j, seq):
        self.data[i:j] = seq

b = Bag([1, 2, 3, 4])
b[1:3] = [9, 9]
print(b.data)
# prints:
# [1, 9, 9, 4]
Enter fullscreen mode Exit fullscreen mode

The method __setslice__ handles the slice replacement directly.


Problem: How do you support deleting a slice in Python?

You want users of your object to be able to delete a range of values, like del obj[2:4]. If you do not define a method for this, Python will raise an error.

Problem: You want to allow slice-based deletion inside your object.

Solution: Python lets you define __delslice__ to handle slice deletes.

Python lets you delete a slice using __delslice__.

class Bag:
    def __init__(self, items):
        self.data = items
    def __delslice__(self, i, j):
        del self.data[i:j]

b = Bag(['a', 'b', 'c', 'd', 'e'])
del b[1:4]
print(b.data)
# prints:
# ['a', 'e']
Enter fullscreen mode Exit fullscreen mode

This removes items from index 1 through 3, leaving only the rest.


Problem: How do you handle missing slice indexes in Python?

You want your slice methods to work even when someone writes obj[2:] or obj[:3]. You are not sure how to handle missing i or j.

Problem: You want missing slice values to behave like built-in lists.

Solution: Python fills in the missing values before calling __getslice__, __setslice__, or __delslice__.

Python adjusts missing indexes before calling the slice method.

class Bag:
    def __init__(self, items):
        self.data = items
    def __getslice__(self, i, j):
        return self.data[i:j]

b = Bag([10, 20, 30, 40])
print(b[:2])
# prints:
# [10, 20]
Enter fullscreen mode Exit fullscreen mode

Even though the slice leaves out the start, Python fills in 0 before calling the method.


Problem: How do you support slicing without extended syntax in Python?

You want your object to work with obj[1:4], but you notice obj[1:4:2] raises an error. You do not understand why one works and the other does not.

Problem: You want to support slices, but extended slices fail.

Solution: Python only calls these methods for simple slices. Use __getitem__ to support extended slice notation.

Python uses __getslice__ only for simple slices with one colon.

class Bag:
    def __init__(self, items):
        self.data = items
    def __getslice__(self, i, j):
        return self.data[i:j]

b = Bag([1, 2, 3, 4, 5])
print(b[1:4])
# prints:
# [2, 3, 4]
# b[::2] would raise an error unless __getitem__ is defined
Enter fullscreen mode Exit fullscreen mode

To support obj[::2], define __getitem__ and check for a slice object.


Like, Comment, Share, and Subscribe

Did you find this helpful? Let me know by clicking the like button below. I'd love to hear your thoughts in the comments, too! If you want to see more content like this, don't forget to subscribe. Thanks for reading!


Mike Vincent is an American software engineer and app developer from Los Angeles, California. More about Mike Vincent

Top comments (0)