Пользовательские списки Python: наследование от list против UserList
Оглавление
- Создание спископодобных классов в Python
- Создание класса, похожего на список, на основе абстрактного базового Класса
- Наследование от встроенного класса list в Python
- Создание подкласса UserList из коллекций
- Классы, похожие на списки кодирования: Практические примеры
- С учетом производительности: список против списка пользователей
- Заключение
В какой-то момент вашего приключения по написанию кода на Python вам может понадобиться создать пользовательские классы, похожие на списки с измененным поведением, новыми функциональными возможностями или и тем, и другим. Чтобы сделать это в Python, вы можете наследовать от абстрактного базового класса , создать подкласс встроенного класса list напрямую или наследовать от UserList, который находится в collections модуль.
В этом руководстве вы узнаете, как:
- Создавайте пользовательские классы, подобные спискам, наследуя от встроенного
listкласса - Создайте пользовательские классы, подобные спискам, путем создания подклассов
UserListиз модуляcollections
Вы также напишете несколько примеров, которые помогут вам решить, какой родительский класс, list или UserList, использовать при создании пользовательских списков классов.
Чтобы извлечь максимальную пользу из этого руководства, вы должны быть знакомы со встроенным классом Python list и его стандартными функциями. Вам также необходимо знать основы объектно-ориентированного программирования и понимать, как наследование работает в Python.
Скачать бесплатно: Нажмите здесь, чтобы загрузить исходный код, который вы будете использовать для создания пользовательских классов, похожих на списки.
Создание спископодобных классов в Python
Встроенный list класс является основным типом данных в Python. Списки полезны во многих ситуациях и имеют множество практических примеров использования. В некоторых из этих вариантов использования стандартная функциональность Python list может оказаться недостаточной, и вам может потребоваться создать пользовательские классы, подобные спискам, для решения данной проблемы.
Обычно вы найдете по крайней мере две причины для создания пользовательских классов, похожих на списки:
- Расширение обычного списка за счет добавления новых функциональных возможностей
- Изменение функциональности стандартного списка
Вы также можете столкнуться с ситуациями, в которых вам потребуется расширить и стандартные функциональные возможности списка.
В зависимости от ваших конкретных потребностей и уровня квалификации вы можете использовать несколько стратегий для создания собственных классов, похожих на списки. Вы можете:
- Наследуется от соответствующего абстрактного базового класса, такого как
MutableSequence - Наследуется непосредственно от встроенного класса Python
list - Подкласс
UserListизcollections
Примечание: В объектно-ориентированном программировании обычно используются глаголы наследовать и подклассы взаимозаменяемы.
При выборе подходящей стратегии необходимо учитывать несколько факторов. Продолжайте читать для получения более подробной информации.
Создание класса, подобного списку, на основе абстрактного Базового Класса
Вы можете создавать свои собственные классы, подобные спискам, наследуя от соответствующего абстрактного базового класса (ABC), например MutableSequence. Этот ABC предоставляет общие реализации большинства методов list, за исключением .__getitem__(), .__setitem__(), .__delitem__, .__len__(), и .insert(). Таким образом, при наследовании от этого класса вам придется реализовать эти методы самостоятельно.
Написание собственной реализации для всех этих специальных методов - это изрядный объем работы. Это чревато ошибками и требует глубоких знаний Python и его модели данных . Это также может привести к проблемам с производительностью, поскольку вы будете писать методы на чистом Python.
Кроме того, предположим, что вам нужно настроить функциональность любого другого стандартного метода списка, например .append() или .insert(). В этом случае вам придется переопределить реализацию по умолчанию и предоставить подходящую реализацию, которая удовлетворяет вашим потребностям.
Основное преимущество этой стратегии создания спископодобных классов заключается в том, что родительский класс ABC предупредит вас, если вы пропустите какие-либо требуемые методы в вашей пользовательской реализации.
В общем, вам следует использовать эту стратегию, только если вам нужен класс, похожий на список, который принципиально отличается от встроенного класса list.
В этом руководстве вы сосредоточитесь на создании спископодобных классов путем наследования от встроенного класса list и класса UserList из модуля стандартной библиотеки collections. Эти стратегии кажутся самыми быстрыми и практичными.
Наследование от встроенного класса Python list
Долгое время было невозможно наследовать напрямую от типов Python, реализованных в C. В Python 2.2 исправлена эта проблема. Теперь вы можете подклассифицировать встроенные типы, включая list. Это изменение принесло подклассам несколько технических преимуществ, поскольку теперь они:
- Будет работать везде, где требуется оригинальный встроенный тип
- Может определять новые экземпляры, статические и классовые методы
- Могут сохранять свои атрибуты экземпляра в атрибуте
.__slots__class, который, по сути, заменяет атрибут.__dict__.
Первый элемент в этом списке может быть требованием для кода на C, который ожидает наличия встроенного класса на Python. Второй элемент позволяет добавлять новые функциональные возможности поверх стандартного поведения в списке. Наконец, третий пункт позволит вам ограничить атрибуты подкласса только теми атрибутами, которые предопределены в .__slots__.
Чтобы начать работу и начать создавать пользовательские классы, подобные спискам, скажем, что вам нужен список, который автоматически сохраняет все свои элементы в виде строк. Предполагая, что ваш пользовательский список будет хранить числа только в виде строк, вы можете создать следующий подкласс list:
# string_list.py class StringList(list): def __init__(self, iterable): super().__init__(str(item) for item in iterable) def __setitem__(self, index, item): super().__setitem__(index, str(item)) def insert(self, index, item): super().insert(index, str(item)) def append(self, item): super().append(str(item)) def extend(self, other): if isinstance(other, type(self)): super().extend(other) else: super().extend(str(item) for item in other)предварительно> кодовый блок>Ваш класс
StringListнапрямую относится к подклассамlist, что означает, что он унаследует всю функциональность стандартного Pythonlist. Поскольку вы хотите, чтобы в вашем списке элементы хранились в виде строк, вам необходимо изменить все методы, которые добавляют или изменяют элементы в базовом списке. К таким методам относятся следующие:
.__init__инициализирует все новые экземпляры класса..__setitem__()позволяет присвоить новое значение существующему элементу, используя индекс элемента, как показано вa_list[index] = item..insert()позволяет вам вставить новый элемент в заданную позицию в базовом списке, используя индекс элемента..append()добавляет один новый элемент в конец основного списка..extend()добавляет ряд элементов в конец списка.
Другие методы, которые ваш класс StringList унаследовал от list, работают просто отлично, потому что они не добавляют и не обновляют элементы в вашем пользовательском списке.
Примечание: Если вы хотите, чтобы ваш класс StringList поддерживал объединение с помощью оператора plus (+), то вам также потребуется реализовать другие специальные методы, такие как .__add__(), .__radd__(), и .__iadd__().
Чтобы использовать StringList в своем коде, вы можете сделать что-то вроде этого:
>>> from string_list import StringList >>> data = StringList([1, 2, 2, 4, 5]) >>> data ['1', '2', '2', '4', '5'] >>> data.append(6) >>> data ['1', '2', '2', '4', '5', '6'] >>> data.insert(0, 0) >>> data ['0', '1', '2', '2', '4', '5', '6'] >>> data.extend([7, 8, 9]) >>> data ['0', '1', '2', '2', '4', '5', '6', '7', '8', '9'] >>> data[3] = 3 >>> data ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']предварительно> кодовый блок>Ваш класс работает должным образом. Он преобразует все входные значения в строки на лету. Это круто, не так ли? Когда вы создаете новый экземпляр
StringList, инициализатор класса выполняет преобразование.Когда вы добавляете, вставляете, расширяете или присваиваете новые значения экземплярам класса, методы, поддерживающие каждую операцию, будут выполнять процесс преобразования строк. Таким образом, элементы вашего списка всегда будут храниться в виде объектов string.
Подкласс
UserListИзcollectionsДругим способом создания пользовательского класса, подобного списку, является использование класса
UserListиз модуляcollections. Этот класс является оболочкой для встроенного типаlist. Он был разработан для создания объектов, похожих на списки, когда еще не было возможности напрямую наследовать от встроенного классаlist.Несмотря на то, что потребность в этом классе была частично устранена возможностью прямого создания подклассов встроенного класса
list,UserListпо-прежнему доступен в стандартной библиотеке , как для удобства, так и для обратной совместимости.Отличительной особенностью
UserListявляется то, что он предоставляет вам доступ к своему атрибуту.data, который может облегчить создание пользовательских списков, поскольку вам не нужно использоватьsuper()все время. Атрибут.dataсодержит обычный код Pythonlist, который по умолчанию пуст.Вот как вы можете переопределить свой класс
StringList, унаследовав отUserList:# string_list.py from collections import UserList class StringList(UserList): def __init__(self, iterable): super().__init__(str(item) for item in iterable) def __setitem__(self, index, item): self.data[index] = str(item) def insert(self, index, item): self.data.insert(index, str(item)) def append(self, item): self.data.append(str(item)) def extend(self, other): if isinstance(other, type(self)): self.data.extend(other) else: self.data.extend(str(item) for item in other)предварительно> кодовый блок>В этом примере доступ к атрибуту
.dataпозволяет вам более простым образом закодировать класс, используя делегирование, что означает, что список в.dataзаботится об обработке всех запросов.Теперь вам почти не нужно использовать продвинутые инструменты, такие как
super(). Вам просто нужно вызвать эту функцию в инициализаторе класса, чтобы предотвратить проблемы в дальнейших сценариях наследования. В остальных методах вы просто используете.data, который содержит обычный список Python. Работа со списками - это навык, которым вы, вероятно, уже обладаете.Примечание: В приведенном выше примере все будет в порядке, если вы повторно используете исходную внутреннюю реализацию
StringListиз предыдущего раздела но измените родительский класс сlistнаUserList. Ваш код будет работать так же. Однако использование.dataможет облегчить процесс кодирования классов, подобных спискам.Эта новая версия работает так же, как и ваша первая версия
StringList. Запустите следующий код, чтобы опробовать ее:>>> from string_list import StringList >>> data = StringList([1, 2, 2, 4, 5]) >>> data ['1', '2', '2', '4', '5'] >>> data.append(6) >>> data ['1', '2', '2', '4', '5', '6'] >>> data.insert(0, 0) >>> data ['0', '1', '2', '2', '4', '5', '6'] >>> data.extend([7, 8, 9]) >>> data ['0', '1', '2', '2', '4', '5', '6', '7', '8', '9'] >>> data[3] = 3 >>> data ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']предварительно> кодовый блок> Как вы уже поняли,Отображение
.dataявляется наиболее важной функциейUserList. Этот атрибут может упростить ваши занятия, поскольку вам не нужно постоянно использоватьsuper(). Вы можете просто воспользоваться преимуществами.dataи использовать знакомый интерфейсlistдля работы с этим атрибутом.Классы, похожие на списки кодирования: практические примеры
Вы уже знаете, как использовать
listиUserList, когда вам нужно создать пользовательские классы, похожие на списки, которые добавляют или изменяют стандартную функциональностьlist.По общему признанию, когда вы думаете о создании класса, похожего на список, наследование от
list, вероятно, кажется более естественным, чем наследование отUserList, потому что разработчики Python знают оlist. Они могут и не подозревать о существованииUserList.Вы также знаете, что основное различие между этими двумя классами заключается в том, что при наследовании от
UserListу вас есть доступ к атрибуту.data, который представляет собой обычный список, которым вы можете манипулировать с помощью стандартногоlistинтерфейс. Напротив, наследование отlistтребует углубленных знаний о модели данных Python, включая такие инструменты, как встроенная функцияsuper()и некоторые специальные методы.В следующих разделах вы создадите несколько практических примеров, используя оба класса. После написания этих примеров вы будете лучше подготовлены к выбору подходящего инструмента для использования, когда вам нужно будет определить пользовательские классы, подобные спискам, в вашем коде.
Список, Который Принимает только Числовые Данные
В качестве первого примера создания класса, подобного списку, с пользовательским поведением, предположим, что вам нужен список, который принимает только числовые данные. Ваш список должен содержать только целое число, с плавающей точкой и сложные числа. Если вы попытаетесь сохранить значение любого другого типа данных, например строку, то ваш список должен вызвать
TypeError.Вот реализация класса
NumberListс желаемой функциональностью:# number_list.py class NumberList(list): def __init__(self, iterable): super().__init__(self._validate_number(item) for item in iterable) def __setitem__(self, index, item): super().__setitem__(index, self._validate_number(item)) def insert(self, index, item): super().insert(index, self._validate_number(item)) def append(self, item): super().append(self._validate_number(item)) def extend(self, other): if isinstance(other, type(self)): super().extend(other) else: super().extend(self._validate_number(item) for item in other) def _validate_number(self, value): if isinstance(value, (int, float, complex)): return value raise TypeError( f"numeric value expected, got {type(value).__name__}" )предварительно> кодовый блок>В этом примере ваш класс
NumberListнаследуется непосредственно отlist. Это означает, что ваш класс использует все основные функциональные возможности совместно со встроенным классомlist. Вы можете перебирать экземплярыNumberList, получать доступ к его элементам и обновлять их, используя их индексы, вызывать общие методыlistи многое другое.Теперь, чтобы убедиться, что каждый введенный элемент является числом, вам необходимо проверить каждый элемент во всех методах, которые поддерживают операции по добавлению новых элементов или обновлению существующих элементов в списке. Требуемые методы такие же, как в примере
StringList, приведенном в разделе Наследование от встроенного в Python классаlist.Для проверки входных данных используется вспомогательный метод, называемый
._validate_number(). Этот метод использует встроенную функциюisinstance(), чтобы проверить, является ли текущее входное значение экземпляромint,float, илиcomplex,, которые являются встроенными классами, представляющими числовые значения в Python.Примечание: Более общим способом проверить, является ли значение числом в Python, было бы использование
Numberиз модуляnumbers. Это позволит вам также проверять объектыFractionиDecimal.Если входное значение является экземпляром числового типа данных, то ваша вспомогательная функция возвращает само значение. В противном случае функция генерирует
TypeErrorисключение с соответствующим сообщением об ошибке.Чтобы использовать
NumberList, вернитесь к своей интерактивной сессии и запустите следующий код:>>> from number_list import NumberList >>> numbers = NumberList([1.1, 2, 3j]) >>> numbers [1.1, 2, 3j] >>> numbers.append("4.2") Traceback (most recent call last): ... TypeError: numeric value expected, got str >>> numbers.append(4.2) >>> numbers [1.1, 2, 3j, 4.2] >>> numbers.insert(0, "0") Traceback (most recent call last): ... TypeError: numeric value expected, got str >>> numbers.insert(0, 0) >>> numbers [0, 1.1, 2, 3j, 4.2] >>> numbers.extend(["5.3", "6"]) Traceback (most recent call last): ... TypeError: numeric value expected, got str >>> numbers.extend([5.3, 6]) >>> numbers [0, 1.1, 2, 3j, 4.2, 5.3, 6]предварительно> кодовый блок>В этих примерах операции, которые добавляют или изменяют данные в
numbers, автоматически проверяют вводимые данные, чтобы гарантировать, что принимаются только числовые значения. Если вы добавите строковое значение кnumbers, то получитеTypeError.Альтернативная реализация
NumberListс использованиемUserListможет выглядеть примерно так:# number_list.py from collections import UserList class NumberList(UserList): def __init__(self, iterable): super().__init__(self._validate_number(item) for item in iterable) def __setitem__(self, index, item): self.data[index] = self._validate_number(item) def insert(self, index, item): self.data.insert(index, self._validate_number(item)) def append(self, item): self.data.append(self._validate_number(item)) def extend(self, other): if isinstance(other, type(self)): self.data.extend(other) else: self.data.extend(self._validate_number(item) for item in other) def _validate_number(self, value): if isinstance(value, (int, float, complex)): return value raise TypeError( f"numeric value expected, got {type(value).__name__}" )предварительно> кодовый блок>В этой новой реализации
NumberListвы наследуете отUserList. Опять же, ваш класс будет использовать все основные функциональные возможности обычного класса.list.В этом примере вместо постоянного использования
super()для доступа к методам и атрибутам родительского класса вы используете атрибут.dataнапрямую. В какой-то степени использование.data, возможно, упрощает ваш код по сравнению с использованиемsuper()и других продвинутых инструментов, таких как специальные методы.Обратите внимание, что в инициализаторе класса используется только
super(),.__init__(). Это наилучшая практика при работе с наследованием в Python. Это позволяет вам правильно инициализировать атрибуты в родительском классе, ничего не нарушая.Список С Дополнительными Функциями
Теперь предположим, что вам нужен класс, похожий на список, со всей стандартной функциональностью обычного Python
list. Ваш класс также должен предоставлять некоторую дополнительную функциональность, заимствованную из типа данных Array в JavaScript. Например, вам понадобятся такие методы, как следующие:
.join()объединяет все элементы списка в одну строку..map(action)возвращает новые элементы, которые являются результатом применения вызываемого параметраaction()к каждому элементу в базовом списке..filter(predicate)возвращает все элементы, которые возвращаютTrueпри вызовеpredicate()для них..for_each(func)вызываетfunc()для каждого элемента в базовом списке, чтобы создать некоторый побочный эффект.Вот класс, который реализует все эти новые функции путем создания подклассов
list:# custom_list.py class CustomList(list): def join(self, separator=" "): return separator.join(str(item) for item in self) def map(self, action): return type(self)(action(item) for item in self) def filter(self, predicate): return type(self)(item for item in self if predicate(item)) def for_each(self, func): for item in self: func(item)предварительно> кодовый блок>Метод
.join()вCustomListпринимает символ-разделитель в качестве аргумента и использует его для объединения элементов в текущем объекте списка, который представлен символомself. Для этого вы используетеstr.join()с генераторным выражением в качестве аргумента. Это генераторное выражение преобразует каждый элемент в строковый объект с помощьюstr().Метод
.map()возвращает объектCustomList. Чтобы создать этот объект, вы используете генераторное выражение, которое применяетaction()к каждому элементу текущего объекта,self. Обратите внимание, что действие может быть любым вызываемым объектом, который принимает элемент в качестве аргумента и возвращает преобразованный элемент.Метод
.filter()также возвращает объектCustomList. Для создания этого объекта используется генераторное выражение, которое выдает элементы, для которыхpredicate()возвращаетTrue. В этом случаеpredicate()должно быть логической функцией, которая возвращаетTrueилиFalseв зависимости от определенных условий, примененных к входному элементу.Наконец, метод
.for_each()вызываетfunc()для каждого элемента в базовом списке. Этот вызов ничего не возвращает, но вызывает некоторые побочные эффекты, как вы увидите ниже.Чтобы использовать этот класс в своем коде, вы можете сделать что-то вроде следующего:
>>> from custom_list import CustomList >>> words = CustomList( ... [ ... "Hello,", ... "Pythonista!", ... "Welcome", ... "to", ... "Real", ... "Python!" ... ] ... ) >>> words.join() 'Hello, Pythonista! Welcome to Real Python!' >>> words.map(str.upper) ['HELLO,', 'PYTHONISTA!', 'WELCOME', 'TO', 'REAL', 'PYTHON!'] >>> words.filter(lambda word: word.startswith("Py")) ['Pythonista!', 'Python!'] >>> words.for_each(print) Hello, Pythonista! Welcome to Real Python!предварительно> кодовый блок>В этих примерах сначала вызывается
.join()дляwords. Этот метод возвращает уникальную строку, которая получается в результате объединения всех элементов в базовом списке.Вызов
.map()возвращает объектCustomList, содержащий слова в верхнем регистре. Это преобразование является результатом примененияstr.upper()ко всем элементам вwords. Этот метод работает практически аналогично встроенной функцииmap(). Основное отличие заключается в том, что вместо возврата списка встроенная функцияmap()возвращает итератор, который лениво возвращает преобразованные элементы .Метод
.filter()принимает в качестве аргумента функциюlambda. В примере эта функцияlambdaиспользуетstr.startswith()для выбора тех слов, которые начинаются с префикса"Py". Обратите внимание, что этот метод работает аналогично встроенной функцииfilter(), которая возвращает итератор вместо списка.Наконец, вызов
.for_each()наwordsвыводит каждое слово на экран в качестве побочного эффекта вызоваprint()для каждого элемента в базовом списке. Обратите внимание, что функция, переданная в.for_each(), должна принимать элемент в качестве аргумента, но не должна возвращать никакого полезного значения.Вы также можете реализовать
CustomList, наследуя отUserList, а не отlist. В этом случае вам не нужно изменять внутреннюю реализацию, только базовый класс:# custom_list.py from collections import UserList class CustomList(UserList): def join(self, separator=" "): return separator.join(str(item) for item in self) def map(self, action): return type(self)(action(item) for item in self) def filter(self, predicate): return type(self)(item for item in self if predicate(item)) def for_each(self, func): for item in self: func(item)предварительно> кодовый блок>Обратите внимание, что в этом примере вы только что изменили родительский класс. Нет необходимости использовать
.dataнапрямую. Однако вы можете использовать его, если хотите. Преимущество заключается в том, что вы предоставите больше контекста другим разработчикам, читающим ваш код:# custom_list.py from collections import UserList class CustomList(UserList): def join(self, separator=" "): return separator.join(str(item) for item in self.data) def map(self, action): return type(self)(action(item) for item in self.data) def filter(self, predicate): return type(self)(item for item in self.data if predicate(item)) def for_each(self, func): for item in self.data: func(item)предварительно> кодовый блок>В этой новой версии
CustomList()единственное изменение заключается в том, что вы заменилиselfнаself.data, чтобы было понятно, что вы работаете сUserListподкласс. Это изменение делает ваш код более понятным.С учетом производительности:
listпо сравнениюUserListНа данный момент вы узнали, как создавать свои собственные классы, подобные спискам, наследуя их от
listилиUserList. Вы также знаете, что единственное видимое различие между этими двумя классами заключается в том, чтоUserListпредоставляет атрибут.data, который может облегчить процесс кодирования.В этом разделе вы рассмотрите аспект, который может оказаться важным при принятии решения о том, использовать ли
listилиUserListдля создания пользовательских классов, подобных спискам. Это производительность!Чтобы оценить, есть ли различия в производительности между классами, которые наследуются от
listиUserList, вы будете использовать классStringList. Продолжайте и создайте новый файл на Python, содержащий следующий код:# performance.py from collections import UserList class StringList_list(list): def __init__(self, iterable): super().__init__(str(item) for item in iterable) def __setitem__(self, index, item): super().__setitem__(index, str(item)) def insert(self, index, item): super().insert(index, str(item)) def append(self, item): super().append(str(item)) def extend(self, other): if isinstance(other, type(self)): super().extend(other) else: super().extend(str(item) for item in other) class StringList_UserList(UserList): def __init__(self, iterable): super().__init__(str(item) for item in iterable) def __setitem__(self, index, item): self.data[index] = str(item) def insert(self, index, item): self.data.insert(index, str(item)) def append(self, item): self.data.append(str(item)) def extend(self, other): if isinstance(other, type(self)): self.data.extend(other) else: self.data.extend(str(item) for item in other)предварительно> кодовый блок>Эти два класса работают одинаково. Однако они внутренне различны.
StringList_listнаследуется отlist, а его реализация основана наsuper(). В отличие от этого,StringList_UserListнаследуется отUserList, и его реализация зависит от внутреннего атрибута.data.Чтобы сравнить производительность этих двух классов, вам следует начать с определения времени выполнения стандартных операций со списком, таких как создание экземпляра. Однако в этих примерах оба инициализатора эквивалентны, поэтому они должны выполнять одинаковую работу.
Также полезно измерять время выполнения новых функций. Например, вы можете проверить время выполнения
.extend(). Продолжайте и запустите следующий код:>>> import timeit >>> from performance import StringList_list, StringList_UserList >>> init_data = range(10000) >>> extended_list = StringList_list(init_data) >>> list_extend = min( ... timeit.repeat( ... stmt="extended_list.extend(init_data)", ... number=5, ... repeat=2, ... globals=globals(), ... ) ... ) * 1e6 >>> extended_user_list = StringList_UserList(init_data) >>> user_list_extend = min( ... timeit.repeat( ... stmt="extended_user_list.extend(init_data)", ... number=5, ... repeat=2, ... globals=globals(), ... ) ... ) * 1e6 >>> f"StringList_list().extend() time: {list_extend:.2f} μs" 'StringList_list().extend() time: 4632.08 μs' >>> f"StringList_UserList().extend() time: {user_list_extend:.2f} μs" 'StringList_UserList().extend() time: 4612.62 μs'предварительно> кодовый блок>В этом тесте производительности вы используете модуль
timeitвместе с функциейmin()для измерения времени выполнения фрагмента кода. Целевой код состоит из вызовов.extend()в экземплярахStringList_listиStringList_UserListс использованием некоторых примеров данных.Разница в производительности между классом, основанным на
list, и классом, основанным наUserList, в этом примере практически отсутствует.Часто, когда вы создаете пользовательский класс, подобный списку, вы ожидаете, что подклассы
listбудут работать лучше, чем подклассыUserList. Почему? Потому чтоlistнаписан на C и оптимизирован для повышения производительности, в то время какUserList- это класс-оболочка, написанный на чистом Python.Однако в приведенном выше примере, похоже, это предположение не совсем верно. По этой причине, чтобы решить, какой суперкласс лучше всего подходит для вашего конкретного случая использования, обязательно запустите тест производительности.
Помимо производительности, наследование от
list, возможно, является естественным способом в Python, главным образом потому, чтоlistнапрямую доступен разработчикам Python как встроенный класс. Кроме того, большинство разработчиков Python будут знакомы со списками и их стандартными функциями, что позволит им быстрее создавать классы, подобные спискам.В отличие от этого, класс
UserListнаходится в модулеcollections, что означает, что вам придется импортировать его, если вы хотите использовать его в своем коде. Кроме того, не все разработчики Python знают о существованииUserList. ОднакоUserListвсе еще может быть полезным инструментом из-за удобства доступа к атрибуту.data, который может облегчить создание пользовательских классов, подобных спискам.Заключение
Теперь вы узнали, как создавать пользовательские классы, похожие на списки с измененным и новым поведением. Чтобы сделать это, вы создали подкласс встроенного класса
listнапрямую. В качестве альтернативы, вы также унаследовали от классаUserList, который доступен в модулеcollections.Наследование от
listи создание подклассовUserListявляются подходящими стратегиями для решения проблемы создания ваших собственных спископодобных классов в Python.В этом руководстве вы узнали, как:
- Создавайте классы, подобные спискам, наследуя от встроенного
listкласса- Создавать классы, подобные спискам, путем создания подклассов
UserListиз модуляcollectionsТеперь вы лучше подготовлены к созданию собственных пользовательских списков, что позволит вам использовать всю мощь этого полезного и распространенного типа данных в Python.
Скачать бесплатно: Нажмите здесь, чтобы загрузить исходный код, который вы будете использовать для создания пользовательских классов, похожих на списки.
<статус завершения article-slug="inherit-python-list" class="btn-group mb-0" data-api-article-bookmark-url="/api/v1/статьи/наследуют-python-список/закладки/" data-api-article-завершение-статус-url="/api/v1/articles/inherit-python-list/completion_status/"> статус завершения> <кнопка поделиться bluesky-text="Интересная статья на #Python от @realpython.com :" email-body="Ознакомьтесь с этой статьей о Python:%0A%0пользовательские списки на Python: наследование от списка пользователей" email-subject="Статья на Python для вас" twitter-text="Интересно #Python статья от @realpython:" url="https://realpython.com/inherit-python-list /" url-title="Пользовательские списки Python: наследование от list против UserList"> кнопка поделиться> Back to Top