Вероятно, мы научились нарезке списков и строк, когда изучали основы Python.

lis = [1,2,3,4,5]
print(lis[0:5:2])    # [1, 3, 5]

string = 'abcde'
print(string[0:5:2]) # ace

В этой статье мы обсудим, как включить это поведение для наших собственных пользовательских классов и объектов.

Магический метод __getitem__

Магический метод __getitem__ определяет, что происходит, когда мы индексируем объект, например. myobject[0].

class Dog:
    def __getitem__(self, index):
        return index * 10
    
dog = Dog()

print(dog[4])    # 40
print(dog[5])    # 50
print(dog[6])    # 60

В этом примере мы заставляем магический метод __getitem__ просто возвращать index * 10.

Встроенный объект slice()

Давайте сначала сделаем так, чтобы магический метод __getitem__ возвращал все, что передает пользователь.

class Dog:
    def __getitem__(self, index):
        return index

dog = Dog()

print(dog[0:5:2])    # slice(0, 5, 2)
print(dog[0:4:1])    # slice(0, 4, 1)

И если мы попытаемся нарезать объект dog (например, как мы делаем для нарезки списка), мы получим объект среза, например. slice(0, 5, 2) или slice(0, 4, 1)

Разбивка объекта slice()

Давайте немного изменим магический метод __getitem__.

class Dog:
    def __getitem__(self, index):
        if type(index) == slice:
            return index.start, index.stop, index.step
        return index

dog = Dog()

print(dog[0:5:2])    # (0, 5, 2)

print(dog[100])      # 100

Если мы передаем слайс, мы возвращаем кортеж (slice.start, slice.stop, slice.step). Если мы перейдем во что-нибудь еще, например. целое число, мы просто возвращаем все, что передаем.

Объект среза содержит атрибуты .start .stop и .step, к которым у нас есть прямой доступ. Таким образом, мы можем заставить магический метод __getitem__ делать все, что захотим, даже если он занимает срез.

Настройка этого поведения

Допустим, у нас есть объект Dog dog, и мы хотим настроить поведение среза — dog[0:5:2] вернет словарь, содержащий имя собаки, возраст, а также начало, остановку и шаг.

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __getitem__(self, index):
        output = {'name':, self.name, 'age':self.age}
        if type(index) == slice:
            output['start'] = index.start
            output['stop'] = index.stop
            outupt['step'] = index.step
        return output

dog = Dog('rocky', 4)

print(dog[0:5:2])

# {'name':'rocky', 'age':4, 'start':0, 'stop':5, 'step':2}

В некотором смысле, у нас есть абсолютный контроль над этим поведением — мы можем заставить его делать то, что не имеет ничего общего с нарезкой!

Несколько заключительных слов

Если эта история была полезной и вы хотите оказать небольшую поддержку, вы можете:

  1. Похлопайте 50 раз за эту историю (мне это очень-очень помогает)
  2. Подпишитесь на членство в Medium, используя мою ссылку (5 долларов в месяц, чтобы читать неограниченное количество историй на Medium)

Настройка моего домашнего офиса: https://zlliu.co/workspace

Получить мои бесплатные электронные книги: https://zlliu.co/books

  1. 40 практических вопросов по Python для начинающих
  2. 20 практических вопросов по рекурсии
  3. Python от нуля к единице