Friday, January 14, 2022

Brett Cannon: Unravelling subscriptions in Python

For the next post in my syntactic sugar series I want to cover subscriptions. It&aposs quite possible you&aposre not familiar with this formal term, but you are probably familiar with the syntax: the square brackets used for indexing lists and tuples (sequence[4]), accessing the value of a specified dictionary (dictionary["key"]), etc. To cover this topic we will break up into three parts: general subscriptions, slicing, and multiple arguments.

General subscriptions

A subscription can get, set, or delete items from a collection. These three operations have equivalent special methods called __getitem__, __setitem__, and __delitem__, respectively. Due to the fact that if a subscription is done on an object that does not have an appropriate special method, we will re-implement the appropriate functions from the operator module. All three functions take a similar approach, so I will just show how __getitem__ works and let you look at the source code for the other two functions.

def __getitem__(container, index, /):
    """Return the item in the container at the specified index."""
    container_type = type(container)
    try:
        getitem_method = debuiltins._mro_getattr(container_type, "__getitem__")
    except AttributeError:
        raise TypeError(f"{container_type.__name__!r} object is not subscriptable")
    return getitem_method(container, index)
Implementation of operator.__getitem__

The code:

  1. Gets the type of the container.
  2. Gets the __getitem__ method from the type.
  3. If the method doesn&apost exist, raise TypeError.
  4. Otherwise call the method appropriately.

Slicing

The syntax for slicing maps to the slice class&apos constructor where any empty value is represented by None.

  • :: maps to slice(None, None, None)
  • 1:2:3 maps to slice(1, 2, 3)
  • 1:2 maps to slice(1, 2)
  • : maps to slice(None, None)
  • 1: maps to slice(1, None)
  • :1 maps to slice(None, 1) (maps to slice(1) as well)

The slice object then gets passed into the appropriate special method, so x[1:2:3] is the equivalent of type(x).__getitem__(x, slice(1, 2, 3)).

Multiple arguments

If you don&apost work with the scientific stack and use packages like NumPy, you may not know that you can actually pass in multiple arguments when using the subscription syntax: [1, 2, 3]. The key difference to a function call, though, is all of the values get bundled up into a tuple that gets passed in as the first argument to the appropriate special method. This translates x[1, 2, 3] to type(x).__getitem__((1, 2, 3)). This also means that passing in a tuple with the same values is no different: x[1, 2, 3] == x[(1, 2, 3)].



from Planet Python
via read more

No comments:

Post a Comment

TestDriven.io: Working with Static and Media Files in Django

This article looks at how to work with static and media files in a Django project, locally and in production. from Planet Python via read...