Когда Del полезен в Python?

375

Я не могу придумать причину, по которой python нуждается в delключевом слове (и большинство языков, похоже, не имеют аналогичного ключевого слова). Например, вместо того, чтобы удалить переменную, можно просто присвоить Noneей. И при удалении из словаря, delметод может быть добавлен.

Есть ли какая-то причина, чтобы оставаться delв Python, или это пережиток дней сбора мусора в Python?

Джейсон Бейкер
источник
46
Историческая справка : Python с самого начала собирал мусор. До 2.0 сборщик мусора в Python не мог обнаруживать циклы ссылок, но это не имело никакого отношения к del.
Стивен Румбальски
28
@ Стивен Румбалси, это как-то связано с делом. Del был использован для разрыва ссылочных циклов.
Уинстон Эверт
6
но delне является следствием предварительной сборки мусора, потому что вы всегда можете это сделать used = None. Просто всегда имело смысл иметь определенный синтаксис для него. Поскольку сейчас у нас цилиндрический ГХ, случаи, когда вы хотите использовать любой из них, невелики.
Уинстон Эверт
3
> и большинство языков, по-видимому, не имеют схожего ключевого слова. Большинство языков для сборки мусора не имеют (или используют его только для указания GC, что он может собирать переменную). Самый старый язык делал: - У старых добрых базовых языков, таких как QuickBasic, были ERASE(убить определенные переменные) и CLEAR(убить все переменные)
DrYak

Ответы:

500

Во-первых, вы можете исключить другие вещи, кроме локальных переменных

del list_item[4]
del dictionary["alpha"]

Оба из которых должны быть явно полезными. Во-вторых, использование delлокальной переменной делает намерение более понятным. Для сравнения:

del foo

в

foo = None

Я знаю, что в случае del fooэтого цель состоит в том, чтобы удалить переменную из области видимости. Не ясно, что foo = Noneделает это. Если кто-то только что назначил, foo = Noneя мог бы подумать, что это мертвый код. Но я сразу знаю, что del fooпытался сделать кто-то, кто кодирует .

Уинстон Эверт
источник
51
+1 да Когда вы назначаете что-то, оно передает намерение использовать это позже. Я действительно видел только то, что delиспользовалось в вычислениях с интенсивным использованием памяти, но когда я увидел это, я сразу понял, почему это было необходимо.
детально
17
Вариант использования удаления из списка или словаря можно легко заменить методом (как я отметил в этом вопросе). Я не уверен, что согласен с использованием delдля обозначения намерения (поскольку комментарий может сделать то же самое без добавления к языку), но я полагаю, что это лучший ответ.
Джейсон Бейкер
6
@JasonBaker, предоставленный на методах. Удаление ломтиков и тому подобного было бы более неудобным при использовании метода. Да, вы можете использовать комментарий. Но я думаю, что использование утверждения лучше, чем комментарий, как его часть языка.
Уинстон Эверт
2
@JasonBaker: Речь идет не только о намерениях, эти два синтаксиса делают две совершенно разные вещи.
Павел Шимерда
2
Это также обрабатывается путем удаления его из соответствующего словаря. Вы также можете оспорить len()функцию таким же образом. Если это так, вопрос может быть закрыт как мнение или даже на вкус. Python просто имеет тенденцию предоставлять примитивы для основных операций вместо того, чтобы полагаться на методы.
Павел Шимерда
161

Вот что delделает (из справочника по языку Python ):

Удаление имени удаляет привязку этого имени из локального или глобального пространства имен

Присвоение Noneимени не удаляет привязку имени из пространства имен.

(Я предполагаю, что могут быть некоторые дебаты о том, действительно ли удаление привязки имени полезно , но это другой вопрос.)

Грег Хьюгилл
источник
22
-1 Афиша явно уже понимает это, он спрашивает, почему вы хотите удалить привязку имени.
Уинстон Эверт
71
@Winston Ewert: Я не уверен, что автор понял, что delубирает привязку имени, поскольку он предложил назначить Noneв качестве альтернативы.
Стивен Румбальски
8
@ Steven, постер явно контрастирует, удаляя переменную (удаляя имя) и назначая None. Он не понимает, почему вы должны удалить переменную, когда вы можете просто назначить None. Они имеют тот же эффект, что освобождают ссылку на то, что ранее было связано с этим именем.
Уинстон Эверт
19
@Winston Ewert: Это мне не понятно. Возможно, вам ясно, что вы заявляете, что они «имеют тот же эффект, что и публикация ссылки на то, что ранее было связано с этим именем». Но это (ясно?) Не вся история в том, что попытка использовать имя после удаления его вызывает NameError. Грег Хьюгилл делает это самое различие. И именно это различие делает ваше утверждение о том, что плакат «ясно» понят мне неясным.
Стивен Румбальски
9
@Winston Ewert Я не согласен. Но достаточно сказано. Мы оба сделали наши дела.
Стивен Румбальски
44

Одно место, которое я нашел delполезным, - это очистка внешних переменных в циклах for:

for x in some_list:
  do(x)
del x

Теперь вы можете быть уверены, что x будет неопределенным, если вы используете его вне цикла for.

Джейсон Бейкер
источник
1
Ваша delстрока здесь должна быть с отступом (т.е. частью цикла)?
Сэм
11
@ Сам, нет, они не предназначены.
user5319825
7
@ Сэм Нет, идея здесь в том, что после последней итерации цикла (после выполнения do () над последним элементом some_list) x будет оставаться ссылкой на конечное значение some_list. del x гарантирует, что это не останется назначенным как таковое.
Матфея
12
Это будет происходить, NameError: name 'x' is not definedесли список пуст.
WofWca
18

Удаление переменной отличается от установки на None

Удаление имен переменных с помощью del, вероятно, редко используется, но это невозможно достичь без ключевого слова. Если вы можете создать имя переменной, написав a=1, приятно, что вы теоретически можете отменить это, удалив a.

В некоторых случаях это может облегчить отладку, поскольку попытка доступа к удаленной переменной вызовет NameError.

Вы можете удалить атрибуты экземпляра класса

Python позволяет написать что-то вроде:

class A(object):
    def set_a(self, a):
        self.a=a
a=A()
a.set_a(3)
if hasattr(a, "a"):
    print("Hallo")

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

del a.a
Бернхард
источник
16

Есть конкретный пример того, когда вы должны использовать del(могут быть и другие, но я знаю об этом один раз), когда вы используете sys.exc_info()для проверки исключения. Эта функция возвращает кортеж, тип возникшего исключения, сообщение и трассировку.

Первых двух значений обычно достаточно, чтобы диагностировать ошибку и воздействовать на нее, но третье содержит весь стек вызовов между тем, где возникло исключение, и тем, где исключение перехвачено. В частности, если вы делаете что-то вроде

try:
    do_evil()
except:
    exc_type, exc_value, tb = sys.exc_info()
    if something(exc_value):
        raise

traceback tbзаканчивается в локальных элементах стека вызовов, создавая циклическую ссылку, которую нельзя собрать мусором. Таким образом, важно сделать:

try:
    do_evil()
except:
    exc_type, exc_value, tb = sys.exc_info()
    del tb
    if something(exc_value):
        raise

разорвать круговую ссылку. Во многих случаях , когда вы хотели бы назвать sys.exc_info(), как с метаклассом магии, отслеживающий это полезно, так что вы должны убедиться , что вы очистить его , прежде чем вы можете возможно оставить обработчик исключений. Если вам не нужна трассировка, вы должны немедленно удалить ее или просто выполните:

exc_type, exc_value = sys.exc_info()[:2]

Чтобы избежать всего этого вместе.

SingleNegationElimination
источник
18
Уже не правда, что сборщик мусора не будет собирать его. Однако цикл задержит сбор.
Уинстон Эверт
Это не слишком актуально и не отвечает на вопрос оп.
WhyNotHugo
15

Просто другое мышление.

При отладке приложений http в среде, такой как Django, стек вызовов, полный бесполезных и испорченных ранее переменных, особенно когда это очень длинный список, может быть очень болезненным для разработчиков. Таким образом, на этом этапе управление пространством имен может быть полезным.

tdihp
источник
12

Использование «del» явно также лучше, чем присвоение переменной None. Если вы попытаетесь удалить переменную, которая не существует, вы получите ошибку времени выполнения, но если вы попытаетесь установить переменную, которая не существует, None, Python автоматически установит новую переменную в None, оставив переменную, которую вы хотел удалить, где это было. Так что Del поможет вам поймать ваши ошибки раньше

Matt
источник
11

Чтобы добавить несколько пунктов к ответам выше: del x

Определение xуказывает r -> o(ссылка, rуказывающая на объект o), но del xизменяется, rа не o. Это операция над ссылкой (указателем) на объект, а не на объект, связанный с ним x. Различение между rи oявляется ключевым здесь.

  • Это удаляет это из locals().
  • Удаляет его из globals()если xпринадлежит там.
  • Удаляет его из фрейма стека (физически удаляет из него ссылку, но сам объект находится в пуле объектов, а не в фрейме стека).
  • Удаляет его из текущей области. Очень полезно ограничить диапазон определения локальной переменной, что в противном случае может вызвать проблемы.
  • Это скорее декларация названия, а не определение содержания.
  • Это влияет на то, где xпринадлежит, а не на то, куда xуказывает. Единственное физическое изменение в памяти - это. Например, если он xнаходится в словаре или списке, он (как ссылка) удаляется оттуда (и не обязательно из пула объектов). В этом примере словарь locals(), которому он принадлежит, является стеком frame ( ), который перекрывается с globals().
Сохаил Си
источник
6

Принудительное закрытие файла после использования numpy.load:

Использование ниши возможно, но я нашел это полезным при использовании numpy.loadдля чтения файла. Время от времени я обновлял файл, и мне нужно было скопировать файл с таким же именем в каталог.

Я использовал, delчтобы выпустить файл и позволить мне скопировать в новый файл.

Примечание. Я хочу избежать withдиспетчера контекста, поскольку я поиграл с графиками в командной строке и не хотел много нажимать на вкладку!

Смотрите этот вопрос.

atomh33ls
источник
У меня было нечто подобное с загруженными изображениями с помощью библиотеки изображений Python (PIL). Я открываю изображение, и если оно имело определенные размеры, я хотел удалить файл; однако файл все еще использовался Python. Поэтому я сказал «del img», а затем мог удалить файл.
физическая
2
Помните, что это деталь реализации, поскольку спецификация языка не гарантирует, когда вызывается __del__()метод для мусорных объектов или даже если он вызывается вообще. Таким образом, API, которые не предлагают никакого способа освобождения ресурсов, кроме как надеются, что __del__()метод вызывается через некоторое время (вскоре после того, как объект стал мусором), в некоторой степени нарушаются.
Блэкджек,
6

delчасто встречается в __init__.pyфайлах. Любая глобальная переменная, определенная в __init__.pyфайле, автоматически «экспортируется» (она будет включена в from module import *). Один из способов избежать этого - определить __all__, но это может запутаться, и не все используют это.

Например, если у вас был код __init__.pyкак

import sys
if sys.version_info < (3,):
    print("Python 2 not supported")

Тогда ваш модуль будет экспортировать sysимя. Вы должны вместо этого написать

import sys
if sys.version_info < (3,):
    print("Python 2 not supported")

del sys
asmeurer
источник
4

В качестве примера того, что delможно использовать, я считаю это полезным в следующих ситуациях:

def f(a, b, c=3):
    return '{} {} {}'.format(a, b, c)

def g(**kwargs):
    if 'c' in kwargs and kwargs['c'] is None:
        del kwargs['c']

    return f(**kwargs)

# g(a=1, b=2, c=None) === '1 2 3'
# g(a=1, b=2) === '1 2 3'
# g(a=1, b=2, c=4) === '1 2 4'

Эти две функции могут быть в разных пакетах / модулях, и программисту не нужно знать, что cв fдействительности имеет аргумент значения по умолчанию . Таким образом, используя kwargs в сочетании с del, вы можете сказать «Я хочу значение по умолчанию для c», установив его в None (или в этом случае также оставьте его).

Вы можете сделать то же самое с чем-то вроде:

def g(a, b, c=None):
    kwargs = {'a': a,
              'b': b}
    if c is not None:
        kwargs['c'] = c

    return f(**kwargs)

Однако предыдущий пример я считаю более СУХИМ и элегантным.

Jocke
источник
3

Когда Del полезен в Python?

Вы можете использовать его для удаления одного элемента массива вместо синтаксиса среза x[i:i+1]=[]. Это может быть полезно, если, например, вы находитесь os.walkи хотите удалить элемент в каталоге. Я бы не счел это ключевым словом полезным, поскольку можно было бы просто создать [].remove(index)метод (на .removeсамом деле это метод search-and-remove-first-instance-of-value).

ninjagecko
источник
7
[].pop(index)и [].remove(item). Не используйте переменную named, "index"когда говорите о значении, это выглядит странно.
Лыжи
@Ski pop использует индекс . Это верный ответ, тогда как половина из этих ответов просто приводит пример с использованием del, где None также будет работать. Объект списка со значением None все еще находится в списке, тогда как del удаляет элементы.
user5389726598465
3

Я обнаружил, delчто это полезно для псевдо-ручного управления памятью при обработке больших данных с помощью Numpy. Например:

for image_name in large_image_set:
    large_image = io.imread(image_name)
    height, width, depth = large_image.shape
    large_mask = np.all(large_image == <some_condition>)
    # Clear memory, make space
    del large_image; gc.collect()

    large_processed_image = np.zeros((height, width, depth))
    large_processed_image[large_mask] = (new_value)
    io.imsave("processed_image.png", large_processed_image)

    # Clear memory, make space
    del large_mask, large_processed_image; gc.collect()

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

нерв
источник
2

Я думаю, что одна из причин, по которой del имеет свой собственный синтаксис, заключается в том, что в некоторых случаях замена его на функцию может быть сложной, поскольку она работает с привязкой или переменной, а не со значением, на которое она ссылается. Таким образом, если нужно создать версию функции del, необходимо передать контекст. Del foo должен был бы стать globals (). Remove ('foo') или locals (). Remove ('foo'), что приводит к беспорядку и менее читаемый. Тем не менее, я говорю, что избавление от del было бы хорошо, учитывая его, казалось бы, редкое использование. Но удаление языковых особенностей / недостатков может быть болезненным. Может быть, Python 4 удалит это :)

нечетко-вафельный
источник
2

Я хотел бы остановиться на принятом ответе, чтобы подчеркнуть нюанс между установкой переменной Noneи ее удалением с помощью del:

Дана переменная foo = 'bar'и следующее определение функции:

def test_var(var):
    if var:
        print('variable tested true')
    else:
        print('variable tested false')

После первоначального объявления test_var(foo)доходность variable tested trueсоответствует ожидаемой.

Теперь попробуйте:

foo = None
test_var(foo)

который дает variable tested false.

Сравните это поведение с:

del foo
test_var(foo)

который сейчас поднимает NameError: name 'foo' is not defined.

Джекоб
источник
0

Еще одно использование ниши: в pyroot с ROOT5 или ROOT6 «del» может быть полезен для удаления объекта python, который ссылается на более не существующий объект C ++. Это позволяет динамическому поиску Pyroot находить объект C ++ с одинаковым именем и связывать его с именем Python. Таким образом, вы можете иметь такой сценарий:

import ROOT as R
input_file = R.TFile('inputs/___my_file_name___.root')
tree = input_file.Get('r')
tree.Draw('hy>>hh(10,0,5)')
R.gPad.Close()
R.hy # shows that hy is still available. It can even be redrawn at this stage.
tree.Draw('hy>>hh(3,0,3)') # overwrites the C++ object in ROOT's namespace
R.hy # shows that R.hy is None, since the C++ object it pointed to is gone
del R.hy
R.hy # now finds the new C++ object

Надеюсь, эта ниша будет закрыта благодаря более рациональному управлению объектами ROOT7.

Амнон Харел
источник
0

Команда "del" очень полезна для управления данными в массиве, например:

elements = ["A", "B", "C", "D"]
# Remove first element.
del elements[:1]
print(elements)

Вывод:

['B', 'C', 'D']

Виктор Маррерп
источник
-2

Однажды мне пришлось использовать:

del serial
serial = None

потому что используя только:

serial = None

не освободил последовательный порт достаточно быстро, чтобы немедленно открыть его снова. Из этого урока я узнал, что на delсамом деле означало: «Собери это СЕЙЧАС! И подожди, пока это будет сделано», и это действительно полезно во многих ситуациях. Конечно, вы можете иметь system.gc.del_this_and_wait_balbalbalba(obj).

alfredolavin
источник
5
Хм ... Это действительно не должно было иметь значение. Хотя, возможно, ваша проблема была исправлена ​​из-за дополнительной задержки, которую она внесла?
Уинстон Эверт
3
Я не думаю, что вы можете подтвердить это сейчас какой-либо документацией. Я думаю, что полагаться на GC и вызывать __del__()всегда неправильно в Python (хотя я не знаю причин такого дизайна), и лучше использовать API менеджера контекста ( withутверждение).
Павел Шимерда
1
Это какое-то программирование вуду. Подробности смотрите в описании метода и примечании в документации для object .__ del __ () , и имейте в виду, что это описывает только текущую реализацию CPythons с подсчетом ссылок. Другие реализации Python (PyPy, Jython, IronPython, Brython,…) или будущие реализации CPython могут использовать другую схему сборки мусора. Jython использует GC JVM, который не удаляет объекты немедленно. serialМодуль также работает с Jython , чтобы ваш хак не работает там!
Блэкджек,
Кстати, gc.collect - это способ явной переработки. Поддерживается в большинстве реализаций Python.
tdihp
-2

del является эквивалентом «unset» во многих языках и как точка пересечения, переходящая с другого языка на python. Люди склонны искать команды, которые делают то же самое, что они делали на своем родном языке ... также устанавливают переменная в "" или "none" на самом деле не удаляет переменную из области видимости ... она просто очищает ее значение, а имя самой переменной все равно будет храниться в памяти ... почему?!? в скрипте, интенсивно использующем память .. хранение мусора позади его просто нет нет и в любом случае ... у каждого языка есть какая-то форма функции "unset / delete" var .. почему не python?

Франциско
источник
7
delне вызывает сборщик мусора быстрее, чем = Noneи не оставит мусор в долгосрочной перспективе. Возможно, вы захотите собрать мусор в Python.
SilverbackNet
-3

Каждый объект в Python имеет идентификатор Type, связанный с ним счетчик ссылок, когда мы используем del, счетчик ссылок уменьшается, когда счетчик ссылок становится равным нулю, это потенциальный кандидат для сбора мусора. Это отличает del по сравнению с установкой идентификатора в None. В более позднем случае это просто означает, что объект просто пропущен (до тех пор, пока мы не выйдем из области видимости, в этом случае счет уменьшится), и теперь просто идентификатор указывает на какой-то другой объект (область памяти).

SaiReddy
источник
3
Я хотел бы видеть доказательства этого. Назначение None должно уменьшить счетчик ссылок.
Джейсон Бэйкер
3
Это глупость и противоположность уборке мусора (в смысле оставления мусора валяющимся).
Павел Шимерда