Kiedy del jest przydatny w Pythonie?

374

Naprawdę nie mogę wymyślić żadnego powodu, dla którego python potrzebuje delsłowa kluczowego (a większość języków wydaje się nie mieć podobnego słowa kluczowego). Na przykład zamiast usuwać zmienną, można ją po prostu przypisać None. Podczas usuwania ze słownika delmożna dodać metodę.

Czy jest jakiś powód, aby pozostawać delw Pythonie, czy też jest to pozostałość po czasach, w których Python zbierał śmieci?

Jason Baker
źródło
46
Uwaga historyczna : Python od początku miał śmieci. Przed wersją 2.0 moduł śmieciowy Pythona nie mógł wykryć cykli odwołań, ale nie miało to nic wspólnego del.
Steven Rumbalski
28
@Steven Rumbalksi, ma to coś wspólnego z del. Del został użyty do przerwania cykli odniesienia.
Winston Ewert
6
ale delnie jest to pozostałość po śmieciach, ponieważ zawsze można to mieć used = None. Po prostu zawsze warto mieć określoną składnię. Ponieważ mamy teraz cylindryczny GC, przypadki, w których chcesz użyć jednego z nich, są niewielkie.
Winston Ewert
3
> i wydaje się, że większość języków nie ma podobnego słowa kluczowego Większość języków, w których gromadzone są śmieci , nie ma (lub używa go tylko w celu wskazania GC, że może zbierać zmienną). Większość starszych języków: - stare dobre języki BASIC, takie jak QuickBasic, miały ERASE(zabijają określone zmienne) i CLEAR(zabijają wszystkie zmienne)
DrYak

Odpowiedzi:

498

Po pierwsze, możesz usuwać inne rzeczy oprócz zmiennych lokalnych

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

Oba powinny być wyraźnie przydatne. Po drugie, użycie delzmiennej lokalnej sprawia, że ​​intencja jest bardziej przejrzysta. Porównać:

del foo

do

foo = None

Wiem, że w tym przypadku del foochodzi o usunięcie zmiennej z zakresu. Nie jest jasne, czy foo = Noneto robi. Gdyby ktoś właśnie przydzielił foo = None, mógłbym pomyśleć, że to martwy kod. Ale od razu wiem, co del foopróbował zrobić ktoś, kto koduje .

Winston Ewert
źródło
51
+1, tak. Kiedy coś przypisujesz, przekazuje zamiar użycia go później. Naprawdę widziałem tylko delużywane w obliczeniach wymagających dużej ilości pamięci, ale kiedy go zobaczyłem, natychmiast zrozumiałem, dlaczego jest to konieczne.
detly
17
Przypadek użycia usuwania z listy lub słownika można łatwo zastąpić metodą (jak zauważyłem w pytaniu). Nie jestem pewien, czy zgadzam się na użycie delsygnału do wyrażenia intencji (ponieważ komentarz mógłby zrobić to samo bez dodawania do języka), ale przypuszczam, że to najlepsza odpowiedź.
Jason Baker
6
@JasonBaker, przyznane na metody. Jednak usuwanie plastrów i tym podobnych byłoby bardziej niewygodne przy użyciu metody. Tak, możesz użyć komentarza. Ale myślę, że użycie oświadczenia jest lepsze niż komentarz jako jego część języka.
Winston Ewert,
2
@JasonBaker: Nie chodzi tylko o cel, te dwie składnie robią dwie bardzo różne rzeczy.
Pavel Šimerda
2
Jest to również obsługiwane przez usunięcie go z odpowiedniego słownika. Możesz również zakwestionować tę len()funkcję w ten sam sposób. W takim przypadku kwestia mogła zostać zamknięta w oparciu o opinię lub nawet smak. Python po prostu ma tendencję do dostarczania prymitywów dla podstawowych operacji zamiast polegać na metodach.
Pavel Šimerda
161

Jest ta część tego, co delrobi (z Python Language Reference ):

Usunięcie nazwy usuwa powiązanie tej nazwy z lokalnej lub globalnej przestrzeni nazw

Przypisanie Nonedo nazwy nie usuwa wiązania nazwy z przestrzeni nazw.

(Przypuszczam, że może być trochę debaty na temat tego, czy usunięcie wiązania nazwy jest rzeczywiście przydatne , ale to inne pytanie.)

Greg Hewgill
źródło
22
-1 Plakat wyraźnie to rozumie, pyta, dlaczego chcesz usunąć powiązanie nazwy.
Winston Ewert
71
@Winston Ewert: Nie jestem pewien, czy plakat zrozumiał, że delusuwa powiązanie nazwy, ponieważ zasugerował przypisanie Nonejako alternatywy.
Steven Rumbalski
8
@ Steven plakat wyraźnie kontrastuje z usunięciem zmiennej (usunięcie nazwy) i przypisaniem Brak. Nie rozumie, dlaczego powinieneś usunąć zmienną, skoro możesz po prostu przypisać Brak. Mają ten sam efekt, że uwalniają odniesienie do wszystkiego, co wcześniej było związane z tą nazwą.
Winston Ewert
19
@Winston Ewert: Dla mnie nie jest to jasne. Być może jest dla ciebie jasne, że oświadczasz, że „mają ten sam efekt, ponieważ uwalniają odniesienie do wszystkiego, co wcześniej było związane z tą nazwą”. Ale to (oczywiście?) Nie cała historia w tym, że próba użycia nazwy po usunięciu powoduje podniesienie NameError. Greg Hewgill czyni to bardzo rozróżnieniem. To właśnie to rozróżnienie sprawia, że ​​stwierdzenie, co plakat „wyraźnie” rozumiał, jest dla mnie niejasne.
Steven Rumbalski
9
@Winston Ewert Nie zgadzam się. Ale wystarczy powiedzieć. Obaj wykonaliśmy nasze sprawy.
Steven Rumbalski
44

Jednym z miejsc, które uważam za delprzydatne, jest usuwanie obcych zmiennych w pętlach:

for x in some_list:
  do(x)
del x

Teraz możesz być pewien, że x nie zostanie zdefiniowane, jeśli użyjesz go poza pętlą for.

Jason Baker
źródło
1
Czy twoja dellinia ma być wcięta (tj. Część pętli)?
Sam
11
@Sam, nie, nie są zamierzone.
user5319825,
7
@Sam Nie, chodzi tutaj o to, że po ostatniej iteracji pętli (po wykonaniu do () na końcowym elemencie some_list) x pozostanie odniesieniem do końcowej wartości some_list. del x upewnia się, że to nie pozostanie przypisane jako takie.
Matthew
12
Spowoduje to NameError: name 'x' is not defined, że lista będzie pusta.
WofWca
18

Usunięcie zmiennej różni się od ustawienia jej na Brak

Usuwanie nazw zmiennych za pomocą delprawdopodobnie jest rzadkie, ale jest to coś, czego nie można by w prosty sposób osiągnąć bez słowa kluczowego. Jeśli możesz utworzyć nazwę zmiennej, pisząc a=1, fajnie jest teoretycznie cofnąć to, usuwając.

W niektórych przypadkach może to ułatwić debugowanie, ponieważ próba uzyskania dostępu do usuniętej zmiennej wywoła błąd NameError.

Możesz usunąć atrybuty instancji klasy

Python pozwala pisać coś takiego:

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

Jeśli zdecydujesz się dynamicznie dodawać atrybuty do instancji klasy, z pewnością chcesz mieć możliwość cofnięcia jej poprzez napisanie

del a.a
Bernhard
źródło
16

Istnieje konkretny przykład tego, kiedy powinieneś używać del(mogą być inne, ale wiem o tym z jednej ręki), kiedy używasz sys.exc_info()do sprawdzenia wyjątku. Ta funkcja zwraca krotkę, typ zgłoszonego wyjątku, komunikat i komunikat śledzenia.

Pierwsze dwie wartości są zwykle wystarczające do zdiagnozowania błędu i działania na jego podstawie, ale trzecia zawiera cały stos wywołań między miejscem zgłoszenia wyjątku a momentem przechwycenia wyjątku. W szczególności, jeśli robisz coś takiego

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

traceback tbkończy się w lokalach stosu wywołań, tworząc cykliczne odwołanie, którego nie można wyrzucić śmieci. Dlatego ważne jest, aby:

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

aby przerwać cykliczne odniesienie. W wielu przypadkach, w których chciałbyś zadzwonić sys.exc_info(), np. W przypadku magii metaklasy, funkcja traceback jest przydatna, więc musisz upewnić się, że ją wyczyściłeś, zanim ewentualnie opuścisz program obsługi wyjątków. Jeśli nie potrzebujesz śledzenia, powinieneś go natychmiast usunąć lub po prostu wykonaj:

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

Aby uniknąć tego wszystkiego razem.

SingleNegationElimination
źródło
18
Nie jest już prawdą, że śmieciarz go nie odbierze. Jednak cykl opóźni zbieranie.
Winston Ewert
To nie jest zbyt istotne i nie odpowiada na pytanie operatora.
WhyNotHugo
15

Po prostu inne myślenie.

Podczas debugowania aplikacji http we frameworku takim jak Django stos wywołań pełen niepotrzebnych i pomieszanych zmiennych używanych poprzednio, szczególnie gdy jest to bardzo długa lista, może być bardzo bolesny dla programistów. więc w tym momencie przydatne może być kontrolowanie przestrzeni nazw.

tdihp
źródło
12

Bezpośrednie użycie „del” jest również lepszą praktyką niż przypisywanie zmiennej do None. Jeśli spróbujesz usunąć zmienną, która nie istnieje, pojawi się błąd czasu wykonywania, ale jeśli spróbujesz ustawić zmienną, która nie istnieje na Brak, Python po cichu ustawi nową zmienną na Brak, pozostawiając zmienną chciałem usunąć tam, gdzie to było. Więc del pomoże ci wcześniej złapać swoje błędy

Matt
źródło
11

Aby dodać kilka punktów do powyższych odpowiedzi: del x

Definicja xwskazuje r -> o(odniesienie rwskazujące na obiekt o), ale del xzmienia się rraczej niż o. Jest to operacja na odwołaniu (wskaźniku) do obiektu, a nie na obiekcie powiązanym x. Kluczowe jest tutaj rozróżnienie między ri o.

  • Usuwa go z locals().
  • Usuwa to z, globals()jeśli xnależy tam.
  • Usuwa go z ramki stosu (fizycznie usuwa z niego referencję, ale sam obiekt znajduje się w puli obiektów, a nie w ramce stosu).
  • Usuwa go z bieżącego zakresu. Bardzo przydatne jest ograniczenie zakresu definicji zmiennej lokalnej, która w przeciwnym razie może powodować problemy.
  • Chodzi raczej o deklarację nazwy niż definicję treści.
  • Wpływa na to, gdzie xnależy, a nie na gdzie xwskazuje. Jedyną fizyczną zmianą w pamięci jest to. Na przykład, jeśli xznajduje się w słowniku lub liście, jest on (jako odniesienie) stamtąd (i niekoniecznie z puli obiektów). W tym przykładzie słownik, do którego należy, to stos frame ( locals()), który pokrywa się z globals().
Sohail Si
źródło
6

Wymuś zamknięcie pliku po użyciu numpy.load:

Być może jest to niszowe zastosowanie, ale przydało mi się przy numpy.loadczytaniu pliku. Co jakiś czas aktualizowałem plik i musiałem skopiować plik o tej samej nazwie do katalogu.

Kiedyś delwypuszczałem plik i pozwalałem mi kopiować w nowym pliku.

Uwaga Chcę uniknąć withmenedżera kontekstu, ponieważ bawiłem się fabułami w wierszu poleceń i nie chciałem zbyt często naciskać klawisza Tab!

Zobacz to pytanie.

atomh33ls
źródło
Miałem coś podobnego z załadowanymi obrazami z biblioteką obrazów Python (PIL). Otwieram obraz, a jeśli miałby określone wymiary, chciałem usunąć plik; jednak plik był nadal używany przez Pythona. Dlatego powiedziałem „del img”, a następnie mogłem usunąć plik.
fizyczna
2
Uwaga: jest to szczegół implementacji, ponieważ specyfikacja języka nie gwarantuje, że __del__()metoda na obiektach typu śmieci zostanie wywołana lub nawet jeśli zostanie w ogóle wywołana. Tak więc interfejsy API, które nie oferują innego sposobu na uwolnienie zasobów niż nadzieja, że __del__()metoda zostanie wywołana przez jakiś czas (wkrótce po tym, jak obiekt zostanie wyrzucony), zostaną w pewnym stopniu zepsute.
BlackJack,
6

deljest często widoczny w __init__.pyplikach. Każda zmienna globalna zdefiniowana w __init__.pypliku jest automatycznie „eksportowana” (zostanie uwzględniona w a from module import *). Jednym ze sposobów uniknięcia tego jest zdefiniowanie __all__, ale może to być bałagan i nie wszyscy go używają.

Na przykład, jeśli masz __init__.pypodobny kod

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

Następnie moduł wyeksportuje sysnazwę. Zamiast tego powinieneś pisać

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

del sys
asmeurer
źródło
4

Jako przykład tego, co delmożna wykorzystać, uważam, że jest to przydatne w następujących sytuacjach:

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'

Te dwie funkcje mogą być w różnych opakowaniach / modułów i programista nie musi wiedzieć, co argumentem Domyślna wartość cw frzeczywistości mają. Tak więc, używając kwargs w połączeniu z del, możesz powiedzieć „Chcę wartość domyślną na c”, ustawiając ją na None (lub w tym przypadku również ją zostaw).

Możesz zrobić to samo z czymś takim jak:

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

    return f(**kwargs)

Jednak poprzedni przykład uważam za bardziej SUCHY i elegancki.

Jocke
źródło
3

Kiedy del jest przydatny w Pythonie?

Możesz go użyć do usunięcia pojedynczego elementu tablicy zamiast składni plastra x[i:i+1]=[]. Może to być przydatne, jeśli na przykład jesteś w os.walki chcesz usunąć element z katalogu. Nie uważałbym jednak słowa kluczowego za przydatne do tego celu, ponieważ można po prostu stworzyć [].remove(index)metodę ( .removemetoda to w rzeczywistości wyszukiwanie i usuwanie pierwszej instancji wartości).

ninjagecko
źródło
7
[].pop(index)a [].remove(item). Nie używaj zmiennej nazwanej, "index"gdy mówisz o wartości, sprawia, że ​​wygląda ona na mylącą.
Ski
@Ski pop używa indeksu . To jest prawidłowa odpowiedź, podczas gdy połowa z tych odpowiedzi to tylko przykład z del, gdzie żadna również nie działałaby. Obiekt listy ustawiony na Brak jest nadal na liście, podczas gdy del usuwa elementy.
user5389726598465
3

Okazało delsię przydatne do pseudo-ręcznego zarządzania pamięcią podczas obsługi dużych danych za pomocą Numpy. Na przykład:

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()

Może to być różnica między zatrzymaniem skryptu, gdy system zamienia się jak szalony, gdy Python GC nie może nadążyć, i działa idealnie gładko poniżej luźnego progu pamięci, który pozostawia dużo miejsca na używanie maszyny do przeglądania i kod podczas działania.

Nerw
źródło
2

Myślę, że jednym z powodów, dla których del ma swoją własną składnię, jest to, że zastąpienie go funkcją może być trudne w niektórych przypadkach, ponieważ działa na powiązaniu lub zmiennej, a nie na wartości, do której się odwołuje. Zatem jeśli utworzonoby wersję funkcji del, należałoby przekazać kontekst. Del foo musiałby stać się globals (). Remove ('foo') lub locals (). Remove ('foo'), co robi się bałagan i mniej czytelne. Nadal mówię, że pozbycie się dela byłoby dobre, biorąc pod uwagę jego pozornie rzadkie użycie. Ale usuwanie cech / wad językowych może być bolesne. Może python 4 go usunie :)

puszysty wafel
źródło
2

Chciałbym rozwinąć przyjętą odpowiedź, aby podkreślić niuans między ustawieniem zmiennej Nonea jej usunięciem za pomocą del:

Biorąc pod uwagę zmienną foo = 'bar'i następującą definicję funkcji:

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

Po początkowym zadeklarowaniu test_var(foo)plony są variable tested truezgodne z oczekiwaniami.

Spróbuj teraz:

foo = None
test_var(foo)

co daje variable tested false.

Porównaj to zachowanie z:

del foo
test_var(foo)

co teraz podnosi NameError: name 'foo' is not defined.

Jakub
źródło
0

Jeszcze inne zastosowanie niszowe: w pyroot z ROOT5 lub ROOT6 „del” może być użyteczny do usunięcia obiektu python, który odwoływał się do nieistniejącego już obiektu C ++. Umożliwia to dynamiczne wyszukiwanie pyroot w celu znalezienia identycznie nazwanego obiektu C ++ i powiązania go z nazwą python. Możesz mieć scenariusz taki jak:

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

Mamy nadzieję, że ta nisza zostanie zamknięta dzięki rozsądnemu zarządzaniu obiektami ROOT7.

Amnon Harel
źródło
0

Polecenie „del” jest bardzo przydatne do kontrolowania danych w tablicy, na przykład:

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

Wynik:

[„B”, „C”, „D”]

Victor Marrerp
źródło
-2

Raz musiałem użyć:

del serial
serial = None

ponieważ używając tylko:

serial = None

nie zwolnił portu szeregowego wystarczająco szybko, aby natychmiast go ponownie otworzyć. Z tej lekcji dowiedziałem się, że to delnaprawdę znaczy: „GC to TERAZ! I poczekaj, aż się skończy”, a to jest naprawdę przydatne w wielu sytuacjach. Oczywiście możesz mieć system.gc.del_this_and_wait_balbalbalba(obj).

alfredolawina
źródło
5
Hmm ... To naprawdę nie powinno mieć znaczenia. Chociaż być może problem został rozwiązany przez dodatkowe opóźnienie, które wprowadził?
Winston Ewert
3
Nie sądzę, że możesz teraz poprzeć GC za pomocą jakiejkolwiek dokumentacji. Myślę, że poleganie na GC i wywoływanie __del__()zawsze jest złe w Pythonie (chociaż nie znam przyczyn tego projektu) i lepiej jest użyć interfejsu API menedżera kontekstu ( withinstrukcja).
Pavel Šimerda
1
To coś w rodzaju programowania voodoo. Zobacz opis metody i uwagę w dokumentacji dla obiektu .__ del __ (), aby uzyskać szczegółowe informacje, i pamiętaj, że opisuje to tylko bieżącą implementację CPythons z liczeniem referencji. Inne implementacje Pythona (PyPy, Jython, IronPython, Brython,…) lub przyszłe implementacje CPython mogą używać innego schematu wyrzucania elementów bezużytecznych. Jython używa GV JVM, która nie usuwa obiektów natychmiast. serialModuł współpracuje również z Jython więc siekać nie wszystko działa!
BlackJack,
BTW gc.collect będzie sposobem na jawny recykling. Obsługiwane w większości implementacji Pythona.
tdihp
-2

del jest odpowiednikiem „unset” w wielu językach i jako punkt odniesienia przenoszący się z innego języka na python. ludzie zwykle szukają poleceń, które robią to samo, co kiedyś w swoim pierwszym języku ... również ustawiają var na „” lub żaden tak naprawdę nie usuwa var z zakresu .. po prostu opróżnia swoją wartość sama nazwa var nadal byłaby przechowywana w pamięci ... dlaczego?!? w skrypcie intensywnie korzystającym z pamięci .. utrzymywanie śmieci za jego po prostu „nie”, a zresztą… każdy język ma jakąś formę funkcji „unset / delete” var. dlaczego nie python?

Francisco
źródło
7
delnie wywołuje śmietnika ani szybciej = None, ani nie pozostawia śmieci w dłuższej perspektywie. Możesz chcieć zagłębić się w zbiór śmieci w Pythonie.
SilverbackNet,
-3

Każdy obiekt w pythonie ma powiązany z nim identyfikator Typ, liczba referencji, kiedy używamy del liczba referencji jest zmniejszana, gdy liczba referencji staje się zerowa, jest to potencjalny kandydat na zbieranie śmieci. To odróżnia del w porównaniu z ustawieniem identyfikatora na None. W późniejszym przypadku oznacza to po prostu, że obiekt jest po prostu pomijany (dopóki nie znajdziemy się poza zasięgiem, w którym to przypadku liczba zostanie zmniejszona) i po prostu teraz identyfikator wskazuje na inny obiekt (lokalizację pamięci).

SaiReddy
źródło
3
Chciałbym zobaczyć na to dowód. Przypisanie None nie powinno zmniejszać liczby referencji.
Jason Baker
3
Jest to nonsens i przeciwieństwo odśmiecania śmieci (w sensie pozostawienia śmieci leżących w pobliżu).
Pavel Šimerda