Коли дель корисний у python?


374

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

Чи є якась причина триматися delв пітоні, чи це пережиток днів попереднього збирання сміття Python?


46
Історична довідка : Python від початку збирав сміття. До 2.0, збирач сміття Python не міг виявити цикли посилань, але це не мало нічого спільного del.
Стівен Румбальський

28
@Steven Rumbalksi, це має щось спільне з del. Del використовується для розбиття еталонних циклів.
Вінстон Еверт

6
але delце не рудимент попереднього вивезення сміття, оскільки ви завжди могли це мати used = None. Просто завжди має сенс мати для нього певний синтаксис. Оскільки у нас зараз є циліндричний GC, випадків, коли ви хочете використовувати будь-який, є мало.
Вінстон Еверт

3
> а більшість мов, схоже, не мають подібного ключового слова. Більшість мов, зібраних зі сміттям , не мають (або використовують лише його, щоб натякнути GC, що він може збирати змінну). Більшість старих мов: - Старі добрі ОСНОВНІ мови, такі як QuickBasic ERASE(вбити конкретні змінні) та CLEAR(знищити всі змінні)
DrYak,

Відповіді:


499

По-перше, ви можете виділити інші речі, крім локальних змінних

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: Мені це незрозуміло. Можливо, вам зрозуміло, що ви заявляєте, що вони "мають той же ефект, коли випускають посилання на все, що раніше було пов'язане з цим ім'ям". Але це (очевидно?) Не вся історія в тому, що спроба використовувати ім'я після його видалення викликає a NameError. Грег Х'югілл робить це дуже відмінним. І саме ця відмінність робить ваше твердження про те, що плакат "чітко" зрозумів для мене незрозумілим.
Стівен Румбальський

9
@Winston Ewert Я не згоден. Але досить сказано. Ми обидва склали свої справи.
Стівен Румбальський

44

Одне місце, яке я вважаю delкорисним, - це очищення сторонніх змінних для циклів:

for x in some_list:
  do(x)
del x

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


1
Чи delповинна ваша лінія тут бути відступною (тобто частиною циклу)?
Сем

11
@Sam, Ні, вони не призначені.
user5319825

7
@Sam Ні, ідея тут полягає в тому, що після останньої ітерації циклу (після виконання 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

трекбек, 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]

Щоб уникнути цього всього разом.


18
Це вже не вірно, що сміттєзбірник його не збирає. Однак цикл затримає збір.
Вінстон Еверт

Це не надто актуально і не відповідає на запитання ОП.
WhyNotHugo

15

Просто інше мислення.

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


12

Використання "del" явно також є кращою практикою, ніж присвоєння змінної None. Якщо ви спробуєте дельтувати змінну, яка не існує, ви отримаєте помилку під час виконання, але якщо ви спробуєте встановити змінну, яка не існує в None, Python беззвучно встановить нову змінну на None, залишивши змінну вам хотіли видалити, де це було. Тож дель допоможе вам зловити свої помилки раніше


11

Щоб додати кілька пунктів до вищезазначених відповідей: del x

Визначення xвказує r -> o(посилання, що rвказує на об'єкт o), але замість цього del xзмінюється . Це операція над посиланням (вказівником) на об'єкт, а не об'єктом, пов'язаним з . Тут розрізняють і є ключовим.roxro

  • Це видаляє його з locals().
  • Видаляє його, globals()якщо він xналежить.
  • Видаляє його з кадру стека (фізично видаляє з нього посилання, але сам об'єкт знаходиться в пулі об'єктів, а не в кадрі стека).
  • Вилучає його з поточної області. Дуже корисно обмежити діапазон визначення локальної змінної, що в іншому випадку може спричинити проблеми.
  • Йдеться скоріше про оголошення імені, а не про визначення змісту.
  • Це впливає на те, де xналежить, а не там, де xвказує. Єдина фізична зміна пам’яті - це. Наприклад, якщо xє у словнику чи списку, він (як довідник) видаляється звідти (і не обов'язково з об’єднаного пулу). У цьому прикладі словник, якому він належить, є фреймом стека ( locals()), який перекривається globals().

6

Примушуйте закривати файл після використання numpy.load:

Можливість використання ніші, але я вважаю це корисним при використанні numpy.load читанні файлу. Раз у раз я оновлював би файл і мені потрібно було скопіювати файл з тим самим іменем у каталог.

Я раніше delвипускав файл і дозволяв мені копіювати новий файл.

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

Дивіться це питання.


У мене було щось подібне із завантаженими зображеннями з бібліотекою зображень Python (PIL). Я відкриваю зображення, і якщо воно мало певні розміри, я хотів видалити файл; однак файл все ще використовувався Python. Тому я сказав "del img", а потім міг видалити файл.
physicalattraction

2
Слідкуйте за тим, що це деталь реалізації, оскільки специфікація мови не гарантує, коли __del__()метод на об’єктах сміття викликається або навіть якщо він взагалі викликається. Таким чином, API, які не пропонують іншого способу звільнення ресурсів, окрім сподівання, що __del__()метод буде викликаний через деякий час (незабаром після того, як об'єкт буде сміттям), певною мірою порушується.
BlackJack

6

delчасто бачимо у __init__.pyфайлах. Будь-яка глобальна змінна, яка визначена у __init__.pyфайлі, автоматично "експортується" (вона буде включена в a 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

4

Як приклад того, для чого delможна використовувати, я вважаю корисним i такі ситуації:

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)

Однак я вважаю попередній приклад більш сухим та елегантним.


3

Коли дель корисний у python?

Ви можете використовувати його для видалення одного елемента масиву замість синтаксису зрізів x[i:i+1]=[]. Це може бути корисно, якщо ви, наприклад, перебуваєте в ньому os.walkі хочете видалити елемент в каталозі. Я б не вважав ключовим словом корисним для цього, оскільки можна просто зробити [].remove(index)метод ( .removeметод насправді пошук і видалення-перший-екземпляр вартості).


7
[].pop(index)і [].remove(item). Не використовуйте змінну, названу, "index"коли говорити про значення, це робить її заплутаною.
Лижі

@Ski pop дійсно використовує індекс . Це правильна відповідь, тоді як половина цих відповідей лише наводить приклад, використовуючи del, де None також не працює. Об'єкт списку, встановлений "None", все ще знаходиться у списку, тоді як del видаляє елементи.
користувач5389726598465

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 foo повинен стати глобальними (). Remove ('foo') або localals (). Remove ('foo'), який стає брудним і менш читабельний. І все-таки я кажу, що позбавлення від дель буде добре, враховуючи його, здавалося б, рідкісне використання. Але видалення мовних особливостей / недоліків може бути болісним. Можливо, 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).


5
Хм ... Це насправді не повинно було змінити. Хоча, можливо, ваше питання було виправлено додатковою затримкою, яку він запровадив?
Вінстон Еверт

3
Я не думаю, що ви можете підкріпити цю програму зараз будь-якою документацією. Я думаю, що покладатися на GC та закликати __del__()завжди неправильно в Python (я не знаю причин такої конструкції), і краще використовувати API менеджера контексту ( withзаява).
Павло Шімерда,

1
Це якесь програмування вуду. Докладніше див. Опис методу та примітку в документації до об’єкта .__ del __ () , і майте на увазі, що це описує лише поточну реалізацію CPythons з підрахунком посилань. Інші реалізації Python (PyPy, Jython, IronPython, Brython, ...) або майбутні реалізації CPython можуть використовувати іншу схему збору сміття. Jython використовує JVMs GC, який не видаляє об'єкти відразу. serialМодуль також працює з Jython , щоб ваш хак не працює там!
BlackJack

BTW gc.collect буде способом явної переробки. Підтримується в більшості реалізацій python.
tdihp

-2

del є еквівалентом "unset" у багатьох мовах і як перехресна точка, що переходить з іншої мови на python. var до "" або жодна дійсно не видаляє var із області застосування. Це просто спорожняє її значення, ім'я самого var все ще зберігатиметься в пам'яті ... чому?!? у скрипті, що інтенсивно працює на пам’яті .. зберігання сміття за його просто ні ні, і все одно ... кожна мова там має якусь форму функції "unset / delete" var функції ... чому не python?


7
delне викликає сміттєзбірника швидше, ніж = Noneі не залишать сміття позаду. Можливо, вам захочеться зібрати сміття з Python.
SilverbackNet

-3

Кожен об'єкт в python має ідентифікатор, Type, посилання, пов'язаний з ним, коли ми використовуємо del, кількість посилань зменшується, коли кількість відліку стає нульовою, це потенційний кандидат для збирання сміття. Це відрізняє del, порівняно з встановленням ідентифікатора на None. У подальшому випадку це просто означає, що об'єкт просто залишається диким (поки ми не вийдемо за межі, і в цьому випадку кількість знижується), а тепер просто ідентифікатор вказує на якийсь інший об'єкт (місце пам'яті).


3
Я хотів би бачити докази цього. Призначення жодного не повинно зменшувати кількість відліку.
Джейсон Бейкер

3
Це нісенітниця і протилежність збору сміття (в сенсі залишати сміття лежачи навколо).
Павло Шімерда,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.