Python's del: Удаление ссылок из областей и контейнеров
Оглавление
- Знакомство с оператором del в Python
- Удаление имен из области видимости
- Удаление Элементов из Изменяемых Коллекций
- Удаление Участников Из Пользовательских Классов
- Предотвращение удаления атрибутов в пользовательских классах
- Заключение
Инструкция Python del позволит вам удалить имен и ссылок из разных пространств имен. Это также позволит вам удалить ненужные элементы из ваших списков и ключи из ваших словарей. Если вы хотите научиться использовать del в своем коде на Python, то это руководство для вас. Инструкция del позволяет вам лучше управлять ресурсами памяти в вашем коде, повышая эффективность ваших программ.
В этом руководстве вы узнаете, как:
- Писать и использовать
delинструкции в Python - Воспользуйтесь
del, чтобы удалить имена из разных областей - Используйте
delдля удаления элементов списка и ключей словаря - Удалить атрибуты объекта с помощью
del - Напишите классы, которые предотвращают удаление атрибутов с помощью
del
Чтобы извлечь максимальную пользу из этого руководства, вы должны быть знакомы со встроенными в Python типами данных, в частности списками и словари. Вы также должны знать основы классов и объектно-ориентированного программирования на Python.
Бесплатный бонус: Нажмите здесь, чтобы загрузить пример кода, который поможет вам писать более эффективные программы с помощью инструкции Python del.
Знакомство с инструкцией Python del
Инструкция Python del позволяет удалять ссылки на объекты из заданного пространства имен или типа контейнера. Он выполняет операцию, противоположную тому, что выполняет оператор присваивания. Это своего рода оператор отмены назначения, который позволяет вам отменить привязку одного или нескольких имен к объектам, на которые они ссылаются.
Это естественная функция, которая должна быть в Python. Если вы можете создать переменную, записав variable = value, то у вас должна быть возможность отменить эту операцию, удалив variable. Вот тут-то на сцену и выходит del.
Оператор del может пригодиться в различных ситуациях программирования, таких как:
- Высвобождение ресурсов памяти
- Предотвращение случайного использования переменных и имен
- Как избежать конфликтов имен
Конечно, этот список неполон. Вы можете найти другие подходящие варианты использования этого оператора. В этом руководстве вы узнаете, как работает оператор del и как его использовать в вашем коде. Чтобы начать, вы начнете с изучения общего синтаксиса del в Python.
Изучение синтаксиса del
Оператор Python del состоит из ключевого слова del , за которым следует разделенный запятыми ряд ссылок .
Примечание: В этом руководстве вы будете использовать термин ссылка для общего обозначения имен или идентификаторов, которые могут содержать ссылки на объекты в Python.
Вот общий синтаксис оператора del в Python:
del reference_1[, reference_2, ..., reference_n]предварительно> кодовый блок>Оператор
delпозволяет удалить одну или несколько ссылок из заданного пространства имен. Это также позволяет удалять данные из изменяемых типов контейнеров, таких как списки и словари.Вы часто будете использовать это утверждение с одним аргументом. Однако оно также поддерживает ряд аргументов, разделенных запятыми.
В приведенной выше конструкции
reference_*представляет собой любой идентификатор, который может содержать ссылки на конкретные объекты, хранящиеся в памяти. На практике эти ссылки могут включать в себя:
- Идентификаторы, такие как переменные и имена функций, классов, модулей и пакетов
- Индексы изменяемых последовательностей, таких как
a_list[index] - Фрагменты изменяемых последовательностей, таких как
a_list[start:stop:step] - Ключи словарей, таких как
a_dict[key] - Элементы классов и объектов, такие как атрибуты и методы
Вы можете использовать любую из этих ссылок в качестве аргументов для del. Если вы используете последовательность аргументов, разделенных запятыми, то имейте в виду, что del обрабатывает каждый аргумент последовательно слева направо. Такое поведение может быть рискованным, когда вы удаляете элементы из списков, как вы узнаете позже из этого руководства.
Вот краткий пример использования del для удаления переменной:
>>> greeting = "Hi, Pythonista!" >>> greeting 'Hi, Pythonista!' >>> del greeting >>> greeting Traceback (most recent call last): ... NameError: name 'greeting' is not definedпредварительно> кодовый блок>После выполнения инструкции
delпеременнаяgreetingбольше не доступна. Если вы попытаетесь получить доступ кgreeting, то получитеNameError, потому что эта переменная больше не существует.Удаление владельцев ссылок, таких как переменные, является основной целью
del. Этот оператор не удаляет объекты. В следующем разделе вы более подробно ознакомитесь с фактическими и непосредственными последствиями выполнения инструкцииdel.Понимание последствий
delКак вы уже узнали, оператор
delв Python удаляет ссылки из пространств имен или контейнеров данных. Он не удаляет конкретные объекты. Например, вы не можете удалить литералы встроенных типов с помощьюdel:>>> del 314 File "<input>", line 1 del 314 ^^ SyntaxError: cannot delete literal >>> del "Hello, World!" File "<input>", line 1 del "Hello, World!" ^^^^^^^^^^^^^^^ SyntaxError: cannot delete literalпредварительно> кодовый блок>В этих примерах обратите внимание, что вы не можете использовать оператор
delнепосредственно для объектов. Вы должны использовать его с переменными, именами и другими идентификаторами, как вы уже узнали:>>> number = 314 >>> del numberпредварительно> кодовый блок>Однако выполнение
del, как в этом примере, не означает, что вы удаляете число314из памяти. Это только удаляет имяnumberиз вашего текущего пространства имен или области .Когда вы передаете идентификатор — например, имя переменной, класса, функции или метода — в качестве аргумента
del, оператор отсоединяет идентификатор от объекта, на который ссылается ссылка, и удаляет идентификатор из содержащего его пространства имен. Однако указанный объект может быть удален из памяти вашего компьютера не сразу.Аналогично, когда вы используете индекс списка, фрагмент или ключ словаря в качестве аргумента для
del, вы удаляете ссылку на целевой элемент, фрагмент или ключ из содержащей его структуры данных. Это может не означать немедленного удаления указанного объекта из памяти вашего компьютера.Короче говоря,
delне удаляет объекты из памяти и не освобождает используемое пространство. Он удаляет только ссылки на объекты. Такое поведение может вызвать вопрос. Еслиdelне удаляет объекты из памяти, то как вы можете использоватьdelдля освобождения ресурсов памяти во время выполнения вашего кода?Чтобы ответить на этот вопрос, вам нужно понять, как Python управляет удалением объектов из памяти. В следующем разделе вы узнаете о системе сбора мусора в Python и о том, как она соотносится с использованием
delдля освобождения памяти в ваших программах.Распутывание
delпротив сборки мусораВ Python есть система сбора мусора, которая заботится об удалении неиспользуемых объектов из памяти и освобождении ресурсов для последующего использования. В реализации Python CPython количество ссылок является основным показателем, который запускает сборку мусора.
По сути, Python отслеживает, сколько идентификаторов содержат ссылки на каждый объект в любой момент времени. Количество идентификаторов, указывающих на данный объект, называется количеством ссылок на объект.
Если есть хотя бы одна активная ссылка на объект, то этот объект останется доступным для вас, занимая память на вашем компьютере. Если вы используете
delдля удаления этой единственной ссылки, то объект готов к сборке мусора, которая удалит объект и освободит память.С другой стороны, если у вас есть несколько активных ссылок на объект и вы используете
delдля удаления некоторых из этих ссылок, но не всех, то система сбора мусора не сможет удалить объект и освободить место для хранения. память.Короче говоря,
delудаляет ссылки, в то время как сборка мусора удаляет объекты и освобождает память. Исходя из этого поведения, вы можете заключить, чтоdelпозволяет уменьшить потребление памяти вашим кодом только в том случае, если он удаляет последнюю ссылку на объект, что подготавливает объект к сборке мусора.Важно отметить, что система сбора мусора в Python не освобождает память, используемую объектом, сразу после удаления последней ссылки на этот объект, в результате чего количество ссылок уменьшается до нуля. Вместо этого сборщик мусора Python периодически сканирует память в поисках объектов, на которые нет ссылок, и освобождает их.
Примечание: Оператор
del- это не единственный инструмент, который вы можете использовать для уменьшения количества ссылок на данный объект. Если вы создаете существующую ссылку на другой объект, то вы неявно удаляете одну ссылку на исходный объект.Другим действием, снижающим количество ссылок, является вывод данной ссылки за пределы области видимости, неявное удаление ссылки.
Когда количество ссылок на объект достигает нуля, сборщик мусора может приступить к удалению этого объекта из памяти. В этот момент в действие вступает специальный метод
.__del__().Python автоматически вызывает
.__del__(), когда данный объект вот-вот будет уничтожен. Этот вызов позволяет объекту освободить внешние ресурсы и очистить себя. Важно отметить, что операторdelне запускает метод.__del__().Вам не нужно будет часто реализовывать метод
.__del__()в ваших собственных классах. Правильное использование метода.__del__()довольно сложно. Если вам когда-нибудь понадобится написать этот метод в одном из ваших классов, то обязательно внимательно прочтите его страницу документации ..Использование
delвместо назначенияNoneНазначение
Noneобращение к ссылке - это операция, которую часто сравнивают с использованием инструкцииdel. Но эта операция не удаляет ссылку, как это делаетdel. Она только переназначает ссылку, указывающую наNone:>>> number = 314 >>> number 314 >>> number = None >>> print(number) Noneпредварительно> кодовый блок>При переназначении переменной
numberнаNoneколичество ссылок на объект314уменьшается до нуля. Из-за этого Python может выполнять сборку объекта, освобождая соответствующую память. Однако это присваивание не удаляет имя переменной из вашей текущей области видимости, как это сделал бы операторdel.Поскольку
Noneявляется одноэлементным объектом, встроенным в Python, это назначение не выделяет и не использует новую память, но сохраняет вашу переменную активной и доступной в вашей текущей области видимости.Подход
Noneможет быть полезен, если вы хотите предотвратить исключениеNameError, возникающее при попытке удалить имя или владельца ссылки, которые не существуют в вашем текущем пространстве имен. В этом случае Python автоматически создаст новую переменную и назначитNoneв качестве ее начального значения.Удаление имен из области видимости
Удаление имени из заданной области - это первый вариант использования
del, о котором вы узнаете в этом руководстве. Область действия данного имени или идентификатора - это область программы, в которой вы можете однозначно получить доступ к этому имени.В Python вы найдете не более четырех областей:
- Область применения локальная, или область на функциональном уровне
- Область , включающая вложенных функций
- Область применения глобальная, или на уровне модуля
- Встроенная область , в которой хранятся встроенные имена
Оператор
delможет удалять имена из некоторых из этих областей. Как вы видели в предыдущих примерах, вы можете использоватьdelдля удаления одной или нескольких переменных из глобальной области видимости:>>> color = "blue" >>> fruit = "apple" >>> pet = "dog" >>> del color >>> color Traceback (most recent call last): ... NameError: name 'color' is not defined >>> del fruit, pet >>> fruit Traceback (most recent call last): ... NameError: name 'fruit' is not defined >>> pet Traceback (most recent call last): ... NameError: name 'pet' is not definedпредварительно> кодовый блок>В этом примере вы создаете три глобальные переменные. Затем вы используете инструкцию
del, чтобы удалить эти переменные из вашей глобальной области видимости, в которой они находятся. Помните, чтоdelимеет эффект инструкции об отмене назначения.Примечание: Вы можете просмотреть все имена, которые находятся в вашей глобальной и встроенной областях, используя встроенную функцию
globals(). Аналогично, вы можете получить доступ к именам в вашей текущей локальной области с помощью функцииlocals(). Обе функции возвращают объекты словаря, сопоставляющие имена объектам в их целевых областях.Вы также можете удалить имена из локальной и закрытой областей в своих пользовательских функциях, используя
delтаким же образом. Однако вы не можете удалить имена из встроенной области:>>> del list Traceback (most recent call last): ... NameError: name 'list' is not defined >>> list() [] >>> del dict Traceback (most recent call last): ... NameError: name 'dict' is not defined >>> dict() {} >>> del max Traceback (most recent call last): ... NameError: name 'max' is not defined >>> max([3, 2, 5]) 5предварительно> кодовый блок>Если вы попытаетесь удалить любое встроенное имя с помощью инструкции
del, то получитеNameError. Сообщение об ошибке может показаться непонятным, поскольку вы можете получить доступ ко всем этим именам, как если бы они находились в глобальной области. Однако эти имена находятся во встроенной области, которая недоступна напрямую с помощьюdel.Несмотря на то, что вы не можете удалить встроенные имена, вы можете в любой момент переопределить или скрыть их в своем коде. В Python много встроенных имен. Некоторые из них могут соответствовать вашим потребностям в именовании в некоторых ситуациях. Например, когда вы начинаете изучать Python, списки могут быть одним из первых встроенных типов данных, о которых вы узнаете.
Скажите, что вы изучаете списки, и запустите следующий код:
>>> list = [1, 2, 3, 4] >>> list [1, 2, 3, 4]предварительно> кодовый блок>В этом примере вы использовали
listв качестве имени для объектаlist, содержащего несколько чисел. Переназначение встроенного имени, как вы сделали в этом фрагменте кода, скрывает исходный объект за именем, что не позволяет использовать его в вашем коде:>>> list(range(10)) Traceback (most recent call last): ... TypeError: 'list' object is not callableпредварительно> кодовый блок>Теперь вызов
list()завершается ошибкой, потому что вы переопределили имя в своем предыдущем коде. Быстрое решение этой проблемы заключается в использовании инструкцииdelдля удаления поддельного встроенного имени и восстановления исходного имени:>>> del list # Remove the redefined name >>> list() # Now the original name is available again []предварительно> кодовый блок>Если вы случайно переназначите встроенное имя в интерактивном сеансе, то вы можете быстро выполнить инструкцию
del name, чтобы удалить переопределение из вашей области и восстановить исходное встроенное имя в вашей рабочей области.Удаление Элементов из Изменяемых Коллекций
Удаление элементов из изменяемых коллекций, таких как списки или словари, возможно, является наиболее распространенным вариантом использования инструкции Python
del. Несмотря на то, что эти встроенные типы данных предоставляют методы, которые вы можете использовать для удаления элементов по индексу или ключу,delможет давать несколько иные результаты или быть подходящим в разных сценариях.Вы также можете использовать инструкцию
del, чтобы удалить несколько элементов из списка за один раз. Для этого вы можете использовать синтаксис slice. Для этого конкретного случая использования вы не найдете метод, который выполняет эту задачу. Итак,delэто ваш путь.В следующих разделах вы узнаете, как использовать
delдля удаления элементов из существующих списков. Вы также узнаете, как удалять пары ключ-значение из словарей с помощьюdel. Обладая этими навыками, вы сможете сделать свой код более эффективным, позволив системе сбора мусора Python освободить ресурсы памяти, которые больше не понадобятся вашим спискам и словарям.Удаление Элементов из списка
Вы уже знаете, что синтаксис индексации списка,
a_list[index], позволяет вам получать доступ к отдельным элементам списка. Этот синтаксис предоставляет идентификатор, который содержит ссылку на элемент в целевом индексе.Если вам нужно удалить элемент из существующего списка, вы можете использовать инструкцию
delвместе с оператором индексирования. Общий синтаксис таков:del a_list[index]предварительно> кодовый блок>Этот оператор удалит элемент, который находится в позиции, определенной
indexвa_list. Вот пример, чтобы вы могли убедиться, как это работает на практике:>>> computer_parts = [ ... "CPU", ... "motherboard", ... "case", ... "monitor", ... "keyboard", ... "mouse" ... ] >>> del computer_parts[3] >>> computer_parts ['CPU', 'motherboard', 'case', 'keyboard', 'mouse'] >>> del computer_parts[-1] >>> computer_parts ['CPU', 'motherboard', 'case', 'keyboard']предварительно> кодовый блок>В этом фрагменте кода вы создаете список, содержащий некоторые компоненты компьютера. Затем вы используете
del, чтобы удалить"monitor", элемент с индексом3. Наконец, вы удаляете последний элемент из списка, используя отрицательный индекс-1.Примечание: Оператору
delне удается удалить элементы из неизменяемых типов последовательностей, таких как строки, байт и кортежей:>>> site = "realpython.com/" >>> del site[-1] Traceback (most recent call last): ... TypeError: 'str' object doesn't support item deletion >>> color = (255, 0, 0) >>> del color[0] Traceback (most recent call last): ... TypeError: 'tuple' object doesn't support item deletionпредварительно> кодовый блок>Удаление элемента из этого кортежа повлекло бы за собой мутацию на месте, которая недопустима в неизменяемых типах коллекций.
Будьте осторожны при использовании расширенного синтаксиса
delдля удаления нескольких элементов из списка. В конечном итоге вы можете удалить не те элементы или даже получить ошибку.IndexError.Например, предположим, что вам нужно удалить элементы, содержащие
Noneв выборке числовых данных, чтобы вы могли использовать эти данные в некоторых вычислениях. Тогда вы можете подумать о том, чтобы использовать следующий операторdel:>>> sample = [3, None, 2, 4, None, 5, 2] >>> del sample[1], sample[4] >>> sample [3, 2, 4, None, 2]предварительно> кодовый блок>Что только что произошло? Вы не удалили второе
None, а вместо этого удалили число5. Проблема в том, чтоdelпоследовательно обрабатывает свои аргументы слева направо. В этом примереdelсначала удаляет второй элемент изsample. Затем из измененного списка удаляется четвертый элемент,5. Чтобы получить желаемый результат, вам нужно учитывать, что список целей меняется после каждого удаления.Возможным обходным путем является удаление элементов справа налево:
>>> sample = [3, None, 2, 4, None, 5, 2] >>> del sample[4], sample[1] >>> sample [3, 2, 4, 5, 2]предварительно> кодовый блок>В этом примере вы удалили элементы в обратном порядке, начиная с наибольшего индекса и возвращаясь к наименьшему. Этот метод позволяет безопасно удалять несколько элементов из списков с помощью одной инструкции
del.Когда вы используете индекс списка в качестве аргумента для
del, Python возвращается к вызову специального метода.__delitem__(), который выполняет фактическое удаление . Вот пример, иллюстрирующий такое поведение:>>> class List(list): ... def __delitem__(self, index): ... print( ... f"Running .__delitem__() to delete {self[index]}" ... ) ... super().__delitem__(index) ... >>> sample = List([3, None, 2, 4, None, 5, 2]) >>> del sample[1] Running .__delitem__() to delete None >>> del sample[3] Running .__delitem__() to delete Noneпредварительно> кодовый блок>В этом примере вы создаете подкласс встроенного класса
listи переопределяете его метод.__delitem__(). Внутри метода вы выводите сообщение, показывающее, что метод вызывается автоматически. Затем вы используетеsuper()для вызова исходной реализации.__delitem__()в родительском классе,list.Примечание: Чтобы узнать больше о подклассах встроенного типа
list, ознакомьтесь с Пользовательскими списками Python: наследование отlistvsUserList.Говоря о методах, в списках Python есть методы
.remove()и.pop(), которые позволяют удалять элемент по значению или индексу соответственно:>>> pets = ["dog", "cat", "fish", "bird", "hamster"] >>> pets.remove("fish") # Equivalent to del pets[2] >>> pets ['dog', 'cat', 'bird', 'hamster'] >>> pets.pop(3) 'hamster' >>> pets.pop() 'bird' >>> pets ['dog', 'cat']предварительно> кодовый блок>При вызове
.remove()вы удаляете элемент"fish"из своего спискаpets. Вызов этого метода эквивалентен запускуdel pets[2].Использование
delпротив.remove()будет зависеть от того, что вы пытаетесь сделать. Если вы хотите удалить элементы на основе их индекса, не зная их содержимого, тоdel- это решение, поскольку оно работает с индексами элементов. С другой стороны, если вы хотите удалить элементы списка с известным содержимым, то.remove()может быть более удобочитаемым и понятным. В нем будет четко указано, что вы хотите удалить это конкретное значение.Затем вы используете
.pop(), чтобы удалить элемент с индексом3. Помимо удаления целевого элемента,.pop()также возвращает его. Вот почему на экране отображается'hamster'. Наконец, вызов.pop()без целевого индекса удаляет и возвращает конечный элемент из целевого списка.Использование
delвместо.pop()в еще большей степени зависит от ваших конкретных потребностей. В этом случае оба варианта работают с индексами товаров. Отличительной особенностью является возвращаемое значение. Если вас не интересует возвращаемое значение, то используйтеdel. Если вам нужно возвращаемое значение для дальнейших вычислений, то вы должны использовать.pop().Удаление фрагмента из списка
Удаление фрагмента элементов из существующего списка - это еще один случай повседневного использования инструкции
del. Вы не найдете ни одного методаlist, который бы выполнял подобную задачу, поэтому, возможно, вам подойдет методdel. Чтобы удалить фрагмент из списка, вам нужно использовать следующий синтаксис:>>> del a_list[start:stop:step]предварительно> кодовый блок>Синтаксис разбиения принимает до трех аргументов, разделенных двоеточием:
start,stop, иstep. Они определяют индекс, с которого начинается срез, индекс, на котором срез должен заканчиваться для получения значений, и шаг между значениями.Вот несколько примеров использования описанной выше конструкции для удаления фрагментов из списков:
>>> digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> del digits[3:6] >>> digits [0, 1, 2, 6, 7, 8, 9] >>> del digits[:3] >>> digits [6, 7, 8, 9] >>> del digits[2:] >>> digits [6, 7]предварительно> кодовый блок>В первом примере вы удаляете все элементы из индекса
3в6. Обратите внимание, что интервал нарезки открыт наstop, что означает, что элемент с индексом6не включается в удаление. Другими словами, операция нарезки останавливается при достижении индексаstopбез включения элемента с этим индексом.Во втором примере вы не используете значение
start, что означает, что вы хотите начать удаление с первого элемента в списке. Наконец, в третьем примере вы не используете значениеstop. Это означает, что вы хотите удалить все элементы из2в конец списка.Вы также можете использовать
step, чтобы определить, сколько элементов вы хотите пропустить во время нарезки. Рассмотрим следующий пример:>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> del numbers[::2] >>> numbers [2, 4, 6, 8]предварительно> кодовый блок>В этом примере вы используете
stepиз2, чтобы удалить все остальные элементы изnumbers. Это ловкий трюк, который удаляет нечетные числа из вашего списка.Вы можете использовать объект
sliceвместо оператора нарезки, чтобы удалять элементы из ваших списков:>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> every_other = slice(0, None, 2) >>> del numbers[every_other] >>> numbers [2, 4, 6, 8]предварительно> кодовый блок>В этом примере вы используете встроенный класс
slice()для создания объекта slice. Конструктор класса принимает три аргументаstart,stop, иstep. Они имеют то же значение, что и эквивалентные индексы в операторе нарезки.Этот конкретный объект slice извлекает элементы, начиная с
0и до конца списка, что можно выполнить, установив для аргументаstopзначениеNone. Аргументstepпринимает значение2. Этот фрагмент удаляет все остальные элементы из целевого списка.Интересно, что удаление фрагмента элементов из существующего списка имеет тот же эффект, что и назначение пустого списка тому же фрагменту:
>>> digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> digits[3:6] = [] >>> digits [0, 1, 2, 6, 7, 8, 9] >>> digits[:3] = [] >>> digits [6, 7, 8, 9] >>> digits[2:] = [] >>> digits [6, 7]предварительно> кодовый блок>Присвоение данному фрагменту пустого списка приводит к тому же эффекту, что и удаление целевого фрагмента с помощью инструкции
del. Этот пример эквивалентен его первой версии, приведенной в начале этого раздела.Удаление ненужных элементов из ваших списков может сэкономить немного памяти в ваших программах. Обычно вы не сохраняете дополнительные ссылки на элементы списка, поэтому, если вы удаляете несколько элементов из списка, объекты, хранящиеся в этих элементах, становятся доступными для сборки мусора, что может привести к высвобождению некоторого объема памяти.
Удаление Ключей из Словарей
Удаление пар ключ-значение из словаря - еще один распространенный вариант использования
del. Чтобы это сработало, вам необходимо указать ключ, который вы хотите удалить, в качестве аргументаdel. Вот общий синтаксис для этого:del a_dict[key]предварительно> кодовый блок>Этот оператор удаляет
keyи связанное с ним значение из содержащего его словаряa_dict. Чтобы увидеть эту конструкцию в действии, запустите следующий пример:>>> vegetable_prices = { ... "carrots": 0.99, ... "spinach": 1.50, ... "onions": 0.50, ... "cucumbers": 0.75, ... "peppers": 1.25, ... } >>> del vegetable_prices["spinach"] >>> del vegetable_prices["onions"], vegetable_prices["cucumbers"] >>> vegetable_prices {'carrots': 0.99, 'peppers': 1.25}предварительно> кодовый блок>Здесь вы используете
del, чтобы удалить ключи"spinach","onions", и"cucumbers"из вашего словаряvegetable_prices. В случае со словарями совершенно безопасно удалять несколько ключей в одной строке, используя расширенный синтаксисdel, поскольку ключи уникальны.Вы также можете использовать некоторые методы для удаления ключей из словаря. Например, вы можете использовать методы
.pop()и.popitem():>>> school_supplies = { ... "pencil": 0.99, ... "pen": 1.49, ... "notebook": 2.99, ... "highlighter": 0.79, ... "ruler": 1.29, ... } >>> school_supplies.pop("notebook") 2.99 >>> school_supplies.popitem() ('ruler', 1.29) >>> school_supplies.popitem() ('highlighter', 0.79) >>> school_supplies {'pencil': 0.99, 'pen': 1.49}предварительно> кодовый блок>В первом примере вы используете
.pop()для удаления ключа"notebook"и связанного с ним значения2.99из вашего словаряschool_supplies. Вы должны вызвать.pop()с существующим ключом в качестве аргумента. Во втором примере вы вызываете.popitem(), чтобы удалить последний добавленный элемент изschool_supplies.Несмотря на то, что эти методы удаляют ключи из существующих словарей, они работают несколько иначе, чем
del. Помимо удаления целевого ключа,.pop()возвращает значение, связанное с этим ключом. Аналогично,.popitem()удаляет ключ и возвращает соответствующую пару ключ-значение в виде кортежа. Операторdelне возвращает удаленное значение.Python автоматически вызывает
.__delitem__()специальный метод, когда вы используетеdelдля удаления ключа из словаря. Вот пример, иллюстрирующий такое поведение:>>> class Dict(dict): ... def __delitem__(self, key) -> None: ... print(f"Running .__delitem__() to delete {(key, self[key])}") ... super().__delitem__(key) ... >>> ordinals = Dict( ... {"First": "I", "Second": "II", "Third": "III", "Fourth": "IV"} ... ) >>> del ordinals["Second"] Running .__delitem__() to delete ('Second', 'II') >>> del ordinals["Fourth"] Running .__delitem__() to delete ('Fourth', 'IV')предварительно> кодовый блок>В этом примере вы переопределяете метод
.__delitem__()в классеDict, который наследуется от встроенного классаdict. Выполнение инструкцииdelс ключомDictв качестве аргумента запускает вашу пользовательскую реализацию.__delitem__(), которая выводит сообщение, содержащее информацию об удаленном элементе.Чтобы узнать больше о том, как безопасно создать подкласс встроенного типа
dictв Python, ознакомьтесь с Пользовательскими словарями Python: наследование отdictиUserDict.Удаление Элементов из Изменяемых Вложенных Коллекций
Иногда может потребоваться вложить изменяемые типы, такие как списки и словари, во внешние списки, кортежи и словари. Как только вы создадите одну из этих вложенных структур, вы сможете обращаться к отдельным элементам на разных уровнях вложенности, используя индексы или ключи этих элементов. Крайние левые индексы или ключи будут извлекать внешние последовательности или словари, в то время как крайние правые индексы будут извлекать наиболее глубоко вложенные объекты.
Возможность доступа к вложенным элементам позволяет использовать
delдля их удаления, используя следующий синтаксис:# Syntax for sequences del sequence[outer_index][nested_index_1]...[nested_index_n] # Syntax for dictionaries del dictionary[outer_key][nested_key_1]...[nested_key_n] # Syntax for combined structures del sequence_of_dicts[sequence_index][dict_key] del dict_of_lists[dict_key][list_index]предварительно> кодовый блок>Чтобы получить доступ к объектам из вложенных изменяемых коллекций и удалить их, вы должны использовать оператор индексирования, указав соответствующие индексы слева направо. Подсчитайте, сколько уровней вложенности вам нужно пройти, чтобы добраться до целевого элемента. Вот сколько индексов вы должны указать.
Вот пример того, как удалить вложенные элементы из списка списков:
>>> matrix = [ ... [1, 2, 3], ... [4, 5, 6], ... [7, 8, 9] ... ] >>> target_row = 0 >>> target_col = 2 >>> del matrix[target_row][target_col] >>> matrix [ [1, 2], [4, 5, 6], [7, 8, 9] ] >>> target_row = 1 >>> del matrix[target_row][target_col] >>> matrix [ [1, 2], [4, 5], [7, 8, 9] ] >>> target_row = 2 >>> del matrix[target_row][target_col] >>> matrix [ [1, 2], [4, 5], [7, 8] ]предварительно> кодовый блок>В этом примере вы использовали
delдля удаления вложенных элементов в вашем спискеmatrix. В частности, вы удалили последнее значение из каждой строки матрицы. Использование описательных имен для индексов позволяет улучшить читаемость вашего кода, упрощая его использование.Как вы можете видеть,
target_rowпредставляет индекс вложенного списка вmatrix, в то время какtarget_colпредставляет индекс целевого элемента во вложенном списке. Вот как вы получаете доступ к вложенным элементам.Несмотря на то, что кортежи Python являются неизменяемыми типами данных, которые не допускают прямого удаления, они могут хранить списки, словари и другие изменяемые типы, которые допускают удаление. Таким образом, вы также можете использовать
delдля вложенных изменяемых объектов в ваших кортежах.Чтобы проиллюстрировать это поведение, предположим, что вы работаете с цветами в цветовой модели RGB. Для представления каждого цвета используется кортеж, поскольку цвета должны быть постоянными. Вы решаете создать свои цветные объекты, указав название в верхнем регистре и список значений RGB:
>>> red = ("RED", [255, 0, 0]) >>> blue = ("BLUE", [0, 0, 255])предварительно> кодовый блок>Эти цвета выглядят великолепно. У них есть название и список значений RBG. Вы не можете удалить элементы первого уровня в этих кортежах, потому что кортежи неизменяемы. Однако вы можете удалить элементы из вложенных списков, содержащих значения RGB:
>>> del red[1][0] >>> red ('RED', [0, 0])предварительно> кодовый блок>Кортежи полностью неизменяемы, когда все их элементы также неизменяемы. Если один или несколько элементов в кортеже являются изменяемыми, то вы можете удалить из них значения, используя вложенную индексацию, как вы делали в приведенном выше примере.
Другой распространенной практикой в Python является вложение списков в словари. Этот тип конструкции часто используется при работе с файлами JSON, которые предоставляют понятный и читаемый формат для обмена информацией.
Рассмотрим следующий пример словаря, в котором товары хранятся по классам:
>>> products = { ... "fruits": ["apple", "banana", "orange"], ... "veggies": ["tomato", "avocado", "pepper"] ... } >>> del products["fruits"][1] >>> products { 'fruits': ['apple', 'orange'], 'veggies': ['tomato', 'avocado', 'pepper'] } >>> del products["veggies"][0] >>> products { 'fruits': ['apple', 'orange'], 'veggies': ['avocado', 'pepper'] }предварительно> кодовый блок>Чтобы удалить элемент из внутренних списков в вашем словаре
product, вы сначала используете ключ словаря, а затем индекс целевого элемента во вложенном списке.В значениях словаря Python можно хранить объекты любого типа данных. Вы можете хранить строки, кортежи, списки, другие словари, классы, функции и даже объекты, определенные пользователем. Если объект с заданным значением является изменяемым, то вы можете удалить из него элементы с помощью
del.Вот еще один пример. На этот раз у вас есть список словарей. Каждый вложенный словарь содержит другой словарь:
>>> prices = [ ... {"fruits": {"apple": 1.2, "banana": 1.3, "orange": 1.5}}, ... {"veggies": {"tomato": 2.1, "avocado": 3.2, "pepper": 1.8}}, ... ] >>> del prices[0]["fruits"]["banana"] >>> prices [ {'fruits': {'apple': 1.2, 'orange': 1.5}}, {'veggies': {'tomato': 2.1, 'avocado': 3.2, 'pepper': 1.8}} ] >>> del prices[0]["fruits"] >>> prices [{}, {'veggies': {'tomato': 2.1, 'avocado': 3.2, 'pepper': 1.8}}]предварительно> кодовый блок>Чтобы удалить элемент из наиболее глубоко вложенного словаря, вам нужно использовать индекс содержащего списка, затем ключ словаря второго уровня и, наконец, ключ элемента, который вы хотите удалить. Обратите внимание, что вы можете использовать другие комбинации индексных клавиш для удаления других элементов, в зависимости от уровня их вложенности.
Удаление Участников Из Пользовательских Классов
Когда дело доходит до пользовательских классов, вы можете удалить как атрибуты класса, так и экземпляра, используя инструкцию
del. Вы также можете использоватьdelдля удаления методов из ваших классов. Удаление членов классов и экземпляров может быть полезным лишь в редких случаях, но в Python это возможно.Общий синтаксис для удаления членов класса с помощью
delвыглядит следующим образом:del a_class.class_attribute del a_class.method del an_instance.instance_attributeпредварительно> кодовый блок>Чтобы передать целевой элемент в качестве аргумента
del, вам нужно использовать точечную запись либо для класса, либо для одного из его экземпляров, в зависимости от того, какой тип элемента вам нужно удалить. В следующих разделах вы приведете примеры кода, показывающие, как все это работает на практике.Удаление членов класса: Общий пример
Чтобы проиллюстрировать, как работает синтаксис
delдля удаления членов класса, напишите следующий классSampleClassв вашем Python interactive REPL:>>> class SampleClass: ... class_attribute = 0 ... def __init__(self, arg): ... self.instance_attribute = arg ... def method(self): ... print(self.instance_attribute) ... >>> SampleClass.__dict__ mappingproxy({ '__module__': '__main__', 'class_attribute': 0, '__init__': <function SampleClass.__init__ at 0x105534360>, 'method': <function SampleClass.method at 0x104bcf240>, ... }) >>> sample_instance = SampleClass("Value") >>> sample_instance.__dict__ {'instance_attribute': 'Value'}предварительно> кодовый блок>В этом фрагменте кода вы создаете пример класса с атрибутом class, атрибутом instance и методом. Выделенные строки показывают, как эти элементы взаимодействуют с классом и экземпляром
.__dict__атрибуты, которые хранят атрибуты класса и экземпляра соответственно.Теперь выполните следующие действия, чтобы удалить члены этого класса и его объект:
>>> # Delete members through the instance >>> del sample_instance.instance_attribute >>> del sample_instance.class_attribute Traceback (most recent call last): ... AttributeError: 'SampleClass' object has no attribute 'class_attribute' >>> del sample_instance.method Traceback (most recent call last): ... AttributeError: 'SampleClass' object has no attribute 'method' >>> sample_instance.__dict__ {} >>> # Delete members through the class >>> del SampleClass.class_attribute >>> del SampleClass.method >>> SampleClass.__dict__ mappingproxy({ '__module__': '__main__', '__init__': <function SampleClass.__init__ at 0x105534360>, ... })предварительно> кодовый блок>Используя экземпляр данного класса, вы можете удалить только атрибуты экземпляра. Если вы попытаетесь удалить атрибуты и методы класса, то получите исключение
AttributeError. Вы можете удалить атрибуты и методы класса только через сам класс, как вы можете убедиться в последних двух примерах.Удаление атрибута экземпляра: Практический пример
Когда
delбудет полезно для удаления атрибутов экземпляра? Когда вам нужно уменьшить объем памяти вашего объекта, подготовив временные атрибуты экземпляра для системы сбора мусора Python, что может привести к освобождению ценных ресурсов памяти на вашем компьютере.Например, предположим, что вы хотите написать класс
Factorialдля представления факториала числа. Конструктор класса должен принимать число в качестве аргумента и создавать экземпляр с двумя атрибутами, доступными только для чтения,.numberи.factorial.Класс должен использовать кэширование в качестве оптимизации, чтобы избежать ненужного повторения промежуточных вычислений. Он также должен быть экономичным с точки зрения памяти и удалять кэш после вычисления факториала.
Вот возможная реализация для вашего класса
Factorial:1# factorial.py 2 3class Factorial: 4 def __init__(self, number): 5 self._number = number 6 self._cache = {0: 1, 1: 1} 7 self._factorial = self._calculate_factorial(number) 8 del self._cache 9 10 def _calculate_factorial(self, number): 11 if number in self._cache: 12 return self._cache[number] 13 current_factorial = number * self._calculate_factorial(number - 1) 14 self._cache[number] = current_factorial 15 return current_factorial 16 17 @property 18 def number(self): 19 return self._number 20 21 @property 22 def factorial(self): 23 return self._factorial 24 25 def __str__(self) -> str: 26 return f"{self._number}! = {self._factorial}" 27 28 def __repr__(self): 29 return f"{type(self).__name__}({self._number})"предварительно> кодовый блок>Ничего себе! В этом коде многое происходит. Вот разбивка наиболее важных строк:
Строка 4 определяет инициализатор, который принимает введенный вами номер в качестве аргумента.
Строка 5 определяет непубличный атрибут экземпляра с именем
._numberдля хранения введенного вами номера.Строка 6 определяет другой атрибут экземпляра, называемый
._cache, который будет работать как кэш для промежуточных вычислений. Изначально этот атрибут содержит факториал0и1.Строка 7 определяет атрибут
._factorial, который содержит факториал входного числа. Обратите внимание, что этот атрибут получает свое значение в результате вызова._calculate_factorial().В строке 8 используется
delдля удаления кэшированных данных. Это удаление позволит вам освободить память для последующего использования и сделает ваши экземплярыFactorialболее легкими, а ваш код - более экономичным с точки зрения памяти.Строки с 10 по 15 определяют метод
._calculate_factorial(), который вычисляет факториал вашего входного числа, используя._cacheдля сохранения уже вычисленных значений.Строки с 17 по 23 определяют два свойства для управления атрибутами
.numberи.factorial. Обратите внимание, что у этих свойств нет методов настройки, что делает базовые атрибуты доступными только для чтения.Строки с 25 по 26 определяют
.__str__()метод, который возвращает удобное для пользователя строковое представление для ваших экземпляров.Строки с 28 по 29 определяют метод
.__repr__(), который возвращает удобное для разработчика строковое представление объектовFactorial.Вот как работает ваш класс на практике:
>>> from factorial import Factorial >>> for i in range(5): ... print(Factorial(i)) ... 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 >>> factorial_5 = Factorial(5) >>> factorial_5 Factorial(5) >>> factorial_5.number 5 >>> factorial_5.factorial 120 >>> print(factorial_5) 5! = 120 >>> factorial_5.factorial = 720 Traceback (most recent call last): ... AttributeError: property 'factorial' of 'Factorial' object has no setter >>> factorial_5._cache Traceback (most recent call last): ... AttributeError: 'Factorial' object has no attribute '_cache'предварительно> кодовый блок>В первом примере вы запускаете цикл
for, который создает пять экземпляровFactorialи выводит их на экран. Обратите внимание, как работает метод.__str__(), выводя экземпляр с использованием математической факториальной записи.Затем вы создаете другой экземпляр
Factorialс5в качестве входного номера. Обратите внимание, что вы можете получить доступ к атрибутам.numberи.factorial, но не можете переназначить их, поскольку они доступны только для чтения. Наконец, если вы попытаетесь получить доступ к непубличному атрибуту._cache, то получите значениеAttributeError, потому что вы уже удалили этот атрибут в методе.__init__().Удаление атрибута
._cacheпосле вычисления факториала приводит к экономии памяти в экземплярахFactorial. Чтобы проиллюстрировать это, добавьте библиотеку Pympler, установивpymplerсpip. Эта библиотека позволит вам измерить объем памяти, занимаемый вашимиFactorialэкземплярами.Затем выполните следующий код:
>>> from pympler import asizeof >>> asizeof.asizeof(Factorial(100)) 600предварительно> кодовый блок>Здесь вы используете функцию
asizeof()для вычисления суммарного размера в байтах экземпляраFactorial. Как вы можете видеть, этот конкретный экземплярFactorialзанимает 600 байт в памяти вашего компьютера.Теперь вернитесь к своему файлу
factorial.pyи закомментируйте строку 8, которая содержит инструкциюdel, которая удаляет._cache. После внесения этого изменения продолжайте и снова запустите вышеуказанный вызов функции:>>> asizeof.asizeof(Factorial(100)) 14216предварительно> кодовый блок>Теперь размер вашего экземпляра
Factorialболее чем в 23 раза больше, чем раньше. Это результат удаления, а не сохранения ненужных кэшированных данных после завершения создания экземпляра.Предотвращение удаления атрибутов в пользовательских классах
В Python вы можете написать свои классы таким образом, чтобы предотвратить удаление атрибутов экземпляра. Вы найдете множество методов, позволяющих это сделать. В этом разделе вы узнаете о двух из этих методов:
- Предоставление пользовательского метода
.__delattr__()- Используя
propertyс помощью метода удаленияЧтобы начать, вы начнете с переопределения специального метода
.__delattr__()с помощью пользовательской реализации. По сути, Python автоматически вызывает этот метод, когда вы используете данный атрибут экземпляра в качестве аргумента инструкцииdel.В качестве примера того, как создать класс, который не позволяет удалить его атрибут экземпляра, рассмотрим следующий класс toy:
# non_deletable.py class NonDeletable: def __init__(self, value): self.value = value def __delattr__(self, name): raise AttributeError( f"{type(self).__name__} object doesn't support attribute deletion" )предварительно> кодовый блок>В этом классе вы переопределяете специальный метод
.__delattr__(). Ваша реализация вызывает исключениеAttributeErrorвсякий раз, когда пользователь пытается удалить какой-либо атрибут, например,.value, используя инструкциюdel.Вот пример того, как ваш класс ведет себя на практике:
>>> one = NonDeletable(1) >>> one.value 1 >>> del one.value Traceback (most recent call last): ... AttributeError: NonDeletable object doesn't support attribute deletionпредварительно> кодовый блок>Всякий раз, когда вы пытаетесь удалить атрибут
.valueлюбого экземпляраNonDeletable, вы получаете исключениеAttributeError, которое возникает из вашей пользовательской реализации.__delattr__(). Этот метод обеспечивает быстрое решение для тех ситуаций, когда вам необходимо предотвратить удаление атрибута экземпляра.Еще один распространенный и быстрый способ предотвращения удаления атрибутов экземпляра - превратить целевой атрибут в свойство и предоставить подходящий метод удаления. Например, предположим, что вы хотите закодировать класс
Person, который запрещает пользователям удалять его атрибут.name:# person.py class Person: def __init__(self, name): self.name = name @property def name(self): return self._name @name.setter def name(self, value): self._name = str(value).upper() @name.deleter def name(self): raise AttributeError("can't delete attribute 'name'")предварительно> кодовый блок>В этом примере
Personимеет атрибут.name, который вы реализуете с помощью свойства. У атрибута есть метод получения, который возвращает значение._name, которое является обычным атрибутом, в котором хранятся данные.Метод setter принимает новое значение для атрибутов, преобразует его в строку и преобразует в заглавные буквы.
Наконец, метод deleter генерирует значение
AttributeError, сигнализирующее о том, что атрибут не подлежит удалению. Python вызывает этот метод автоматически, когда вы используете атрибут.nameв качестве аргумента в инструкцииdel:>>> from person import Person >>> jane = Person("Jane") >>> jane.name 'JANE' >>> jane.name = "Jane Doe" >>> jane.name 'JANE DOE' >>> del jane.name Traceback (most recent call last): ... AttributeError: can't delete attribute 'name'предварительно> кодовый блок>Ваш класс
Personработает должным образом. Вы можете получить доступ к его атрибуту.nameи обновлять его новыми значениями, каждый раз вводя значение в верхнем регистре. Метод deleter вашего свойства.nameв конце определения класса гарантирует, что ваши пользователи не смогут удалить атрибут.Заключение
Вы узнали, как работает инструкция Python
delи как использовать ее для удаления имен и ссылок в вашем коде. Теперь вы знаете, как удалять переменные из разных пространств имен, элементы из списков, ключи из словарей и члены из пользовательских объектов и классов. Вы также узнали, какdelможет помочь вам, когда вам нужно освободить память, чтобы сделать ваш код более эффективным и легким.В этом руководстве вы узнали, как:
- Напишите
delинструкции в вашем коде на Python- Удалить имен из разных пространств имен с помощью
del- Быстрое удаление элементов списка и ключей словаря с помощью
del- Используйте
delдля удаления атрибутов для пользовательских классов и объектов- Предотвратить удаление атрибутов в ваших пользовательских классах
Теперь, когда вы многое узнали о инструкции Python
del, вы можете написать код, экономящий память, удалив ссылки на ненужные объекты, что подготавливает эти объекты для системы сбора мусора.Бесплатный бонус: Нажмите здесь, чтобы загрузить пример кода, который поможет вам писать более эффективные программы с помощью инструкции Python
<статус завершения article-slug="python-del-statement" class="btn-group mb-0" data-api-article-bookmark-url="/api/v1/articles/python-del-statement/bookmark/" data-api-article-завершение-status-url="/api/v1/articles/python-del-statement/завершение_статуса/"> статус завершения> <кнопка поделиться bluesky-text="Интересная статья на #Python от @realpython.com :" email-body="Ознакомьтесь с этой статьей о Python:%0A%0apython's del: Удаление ссылок из областей и контейнеров" email-subject="Статья о Python для вас" twitter-text="Интересная #Статья о Python от @realpython:" url="https://realpython.com/python-del-statement /" url-title="Python's del: удаление ссылок из областей и контейнеров"> кнопка поделиться> Back to Topdel.