Khi nào del hữu ích trong python?


374

Tôi thực sự không thể nghĩ ra bất kỳ lý do nào khiến trăn cần deltừ khóa (và hầu hết các ngôn ngữ dường như không có từ khóa tương tự). Chẳng hạn, thay vì xóa một biến, người ta chỉ có thể gán Nonecho nó. Và khi xóa từ điển, một delphương thức có thể được thêm vào.

Có bất kỳ lý do để giữ deltrăn, hoặc đó là một dấu tích của những ngày thu gom rác của Python?


46
Ghi chú lịch sử : Python đã thu gom rác ngay từ đầu. Trước 2.0, trình thu gom rác của Python không thể phát hiện các chu kỳ tham chiếu, nhưng điều đó không liên quan gì del.
Steven Rumbalski

28
@Steven Rumbalksi, nó có liên quan đến del. Del đã được sử dụng để phá vỡ các chu kỳ tham chiếu.
Winston Ewert

6
nhưng delkhông phải là một di tích của bộ sưu tập tiền rác bởi vì bạn luôn có thể có used = None. Nó luôn luôn có ý nghĩa để có cú pháp cụ thể cho nó. Vì hiện tại chúng ta có GC hình trụ, nên các trường hợp bạn muốn sử dụng là nhỏ.
Winston Ewert

3
> và hầu hết các ngôn ngữ dường như không có từ khóa tương tự Hầu hết các ngôn ngữ được thu gom rác không (hoặc chỉ sử dụng nó để gợi ý cho GC rằng nó có thể thu thập một biến). Hầu hết các ngôn ngữ cũ hơn đã làm: - Các ngôn ngữ BASIC cũ tốt như QuickBasic đã có ERASE(tiêu diệt các biến cụ thể) và CLEAR(tiêu diệt tất cả các biến)
DrYak

Câu trả lời:


499

Đầu tiên, bạn có thể xóa những thứ khác ngoài các biến cục bộ

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

Cả hai đều rõ ràng hữu ích. Thứ hai, sử dụng deltrên một biến cục bộ làm cho ý định rõ ràng hơn. Đối chiếu:

del foo

đến

foo = None

Tôi biết trong trường hợp del foođó, mục đích là loại bỏ biến khỏi phạm vi. Nó không rõ ràng foo = Noneđang làm điều đó. Nếu ai đó chỉ định foo = Nonetôi có thể nghĩ đó là mã chết. Nhưng tôi ngay lập tức biết ai đó del foođang cố gắng làm gì.


51
+1, vâng. Khi bạn chỉ định một cái gì đó, nó truyền đạt ý định sử dụng nó sau này. Tôi chỉ thực sự thấy delđược sử dụng trong các tính toán sử dụng nhiều bộ nhớ, nhưng khi tôi nhìn thấy nó, tôi nhận ra ngay tại sao nó lại cần thiết.
gièm pha

17
Trường hợp sử dụng xóa khỏi danh sách hoặc từ điển có thể dễ dàng được thay thế bằng một phương thức (như tôi đã lưu ý trong câu hỏi). Tôi không chắc chắn tôi đồng ý với việc sử dụng delđể báo hiệu ý định (vì một nhận xét có thể làm tương tự mà không cần thêm ngôn ngữ), nhưng tôi cho rằng đó là câu trả lời tốt nhất.
Jason Baker

6
@JasonBaker, được cấp trên các phương pháp. Xóa các lát và như vậy sẽ khó xử hơn bằng cách sử dụng một phương pháp mặc dù. Vâng, bạn có thể sử dụng một bình luận. Nhưng tôi nghĩ rằng sử dụng một tuyên bố là tốt hơn sau đó một nhận xét là một phần của ngôn ngữ.
Winston Ewert

2
@JasonBaker: Không chỉ về ý định, hai cú pháp đó còn làm hai việc rất khác nhau.
Pavel imerda

2
Nó cũng được xử lý bằng cách xóa nó khỏi từ điển tương ứng. Bạn cũng có thể tranh chấp len()chức năng theo cách tương tự. Nếu đây là trường hợp, các câu hỏi có thể đã được đóng lại như ý kiến ​​hoặc thậm chí dựa trên hương vị. Python chỉ đơn giản là có xu hướng cung cấp nguyên thủy cho các hoạt động cơ bản thay vì dựa vào các phương thức.
Pavel imerda

161

Có phần này của những gì del(từ Tài liệu tham khảo ngôn ngữ Python ):

Xóa tên sẽ loại bỏ ràng buộc của tên đó khỏi không gian tên cục bộ hoặc toàn cầu

Gán Nonetên không loại bỏ ràng buộc của tên khỏi không gian tên.

(Tôi cho rằng có thể có một số tranh luận về việc loại bỏ ràng buộc tên có thực sự hữu ích hay không , nhưng đó là một câu hỏi khác.)


22
-1 Người đăng rõ ràng đã hiểu điều này, anh ta hỏi tại sao bạn muốn xóa ràng buộc tên.
Winston Ewert

71
@Winston Ewert: Tôi không chắc người đăng đã hiểu rằng delloại bỏ ràng buộc tên như anh ấy đề nghị chỉ định Nonethay thế.
Steven Rumbalski

8
@Steven, người đăng tương phản rõ ràng xóa biến (xóa tên) và gán Không. Anh ta không thấy lý do tại sao bạn nên xóa biến khi bạn chỉ có thể gán Không. Điều này có tác dụng tương tự ở chỗ họ phát hành tham chiếu đến bất cứ điều gì trước đây bị ràng buộc với tên đó.
Winston Ewert

19
@Winston Ewert: Nó không rõ ràng với tôi. Có lẽ rõ ràng với bạn rằng bạn nói rằng chúng "có tác dụng tương tự trong việc phát hành tham chiếu đến bất cứ điều gì trước đây bị ràng buộc với tên đó." Nhưng đó (rõ ràng?) Không phải là toàn bộ câu chuyện trong đó một nỗ lực sử dụng tên sau khi xóa nó tăng lên a NameError. Greg Hewgill làm cho điều này rất khác biệt. Và chính sự khác biệt này khiến cho sự khẳng định của bạn về những gì người đăng "rõ ràng" hiểu không rõ ràng với tôi.
Steven Rumbalski

9
@Winston Ewert Tôi không đồng ý. Nhưng đủ nói. Cả hai chúng tôi đã thực hiện các trường hợp của chúng tôi.
Steven Rumbalski

44

Một nơi tôi thấy delhữu ích là dọn sạch các biến không liên quan trong các vòng lặp:

for x in some_list:
  do(x)
del x

Bây giờ bạn có thể chắc chắn rằng x sẽ không được xác định nếu bạn sử dụng nó bên ngoài vòng lặp for.


1
deldòng của bạn ở đây được cho là thụt lề (tức là một phần của vòng lặp)?
Sam

11
@Sam, Không họ không có ý định.
dùng5319825

7
@Sam Không, ý tưởng ở đây là sau lần lặp cuối cùng của vòng lặp (sau khi thực hiện do () trên phần tử cuối cùng của some_list), x sẽ vẫn là tham chiếu đến giá trị cuối cùng của some_list. del x đảm bảo rằng điều này không được chỉ định như vậy.
Matthew

12
Điều này sẽ gây ra NameError: name 'x' is not definednếu danh sách trống.
WofWca

18

Xóa một biến khác với đặt nó thành Không

Xóa tên biến delcó lẽ là thứ được sử dụng hiếm khi, nhưng đó là thứ không thể đạt được một cách tầm thường nếu không có từ khóa. Nếu bạn có thể tạo một tên biến bằng cách viếta=1 , thật tuyệt khi bạn về mặt lý thuyết có thể hoàn tác điều này bằng cách xóa a.

Nó có thể làm cho việc gỡ lỗi dễ dàng hơn trong một số trường hợp vì cố gắng truy cập vào một biến bị xóa sẽ nâng cao NameError.

Bạn có thể xóa các thuộc tính thể hiện của lớp

Python cho phép bạn viết một cái gì đó như:

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

Nếu bạn chọn tự động thêm các thuộc tính vào một thể hiện của lớp, bạn chắc chắn muốn có thể hoàn tác nó bằng cách viết

del a.a

16

Có một ví dụ cụ thể về thời điểm bạn nên sử dụng del(có thể có những người khác, nhưng tôi biết về điều này một lần) khi bạn đang sử dụngsys.exc_info() để kiểm tra một ngoại lệ. Hàm này trả về một tuple, loại ngoại lệ được đưa ra, thông báo và truy nguyên.

Hai giá trị đầu tiên thường đủ để chẩn đoán lỗi và hành động theo lỗi đó, nhưng giá trị thứ ba chứa toàn bộ ngăn xếp cuộc gọi giữa nơi ngoại lệ được đưa ra và nơi bắt gặp ngoại lệ. Đặc biệt, nếu bạn làm một cái gì đó như

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

truy nguyên, tbkết thúc tại các địa phương của ngăn xếp cuộc gọi, tạo ra một tham chiếu vòng tròn không thể được thu gom rác. Vì vậy, điều quan trọng là phải làm:

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

để phá vỡ tham chiếu tròn. Trong nhiều trường hợp bạn muốn gọi sys.exc_info(), như với phép thuật siêu dữ liệu, truy nguyên hữu ích, vì vậy bạn phải đảm bảo rằng bạn dọn sạch nó trước khi bạn có thể rời khỏi trình xử lý ngoại lệ. Nếu bạn không cần truy nguyên, bạn nên xóa nó ngay lập tức hoặc chỉ cần làm:

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

Để tránh tất cả cùng nhau.


18
Nó không còn đúng là người thu gom rác sẽ không thu thập nó. Tuy nhiên, chu trình sẽ trì hoãn việc thu thập.
Winston Ewert

Điều này không quá liên quan và không trả lời câu hỏi của op.
WhyNotHugo

15

Chỉ là một suy nghĩ khác.

Khi gỡ lỗi các ứng dụng http trong khung như Django, ngăn xếp cuộc gọi chứa đầy các biến vô dụng và sai lầm được sử dụng trước đây, đặc biệt khi đó là một danh sách rất dài, có thể rất đau đối với các nhà phát triển. vì vậy, tại thời điểm này, kiểm soát không gian tên có thể hữu ích.


12

Sử dụng "del" một cách rõ ràng cũng là cách thực hành tốt hơn so với việc gán một biến cho Không có. Nếu bạn cố gắng xóa một biến không tồn tại, bạn sẽ gặp lỗi thời gian chạy nhưng nếu bạn cố gắng đặt biến không tồn tại thành Không, Python sẽ âm thầm đặt biến mới thành Không có, để lại biến bạn muốn xóa nó ở đâu Vì vậy, del sẽ giúp bạn bắt lỗi sớm hơn


11

Để thêm một vài điểm cho câu trả lời trên: del x

Định nghĩa xchỉ ra r -> o(một tham chiếu rtrỏ đến một đối tượng o) nhưng del xthay đổi rhơn là o. Nó là một hoạt động trên tham chiếu (con trỏ) đến đối tượng chứ không phải là đối tượng được liên kết với x. Phân biệt giữa rolà chìa khóa ở đây.

  • Nó loại bỏ nó khỏi locals() .
  • Loại bỏ nó khỏi globals()nếux thuộc về đó.
  • Loại bỏ nó khỏi khung ngăn xếp (loại bỏ tham chiếu vật lý khỏi nó, nhưng bản thân đối tượng nằm trong nhóm đối tượng và không nằm trong khung ngăn xếp).
  • Loại bỏ nó khỏi phạm vi hiện tại. Nó rất hữu ích để giới hạn khoảng định nghĩa của một biến cục bộ, điều này có thể gây ra vấn đề.
  • Nó là về khai báo tên hơn là định nghĩa nội dung.
  • Nó ảnh hưởng đến nơi xthuộc về, không phải nơi xchỉ đến. Sự thay đổi vật lý duy nhất trong bộ nhớ là điều này. Ví dụ: nếu xtrong từ điển hoặc danh sách, nó (như một tài liệu tham khảo) được xóa khỏi đó (và không nhất thiết phải từ nhóm đối tượng). Trong ví dụ này, từ điển nó thuộc về stack stack ( locals()), trùng với globals().

6

Buộc đóng tệp sau khi sử dụng numpy.load:

Một cách sử dụng thích hợp có lẽ nhưng tôi thấy nó hữu ích khi sử dụng numpy.loadđể đọc một tập tin. Thỉnh thoảng tôi sẽ cập nhật tệp và cần sao chép một tệp có cùng tên vào thư mục.

Tôi đã sử dụng delđể phát hành tệp và cho phép tôi sao chép trong tệp mới.

Lưu ý Tôi muốn tránh trình withquản lý bối cảnh khi tôi đang chơi xung quanh với các ô trên dòng lệnh và không muốn nhấn tab nhiều!

Xem câu hỏi này .


Tôi đã có một cái gì đó tương tự với hình ảnh được tải với Thư viện hình ảnh Python (PIL). Tôi mở một hình ảnh và nếu nó có kích thước nhất định, tôi muốn xóa tệp; tuy nhiên tập tin vẫn được Python sử dụng. Do đó, tôi đã nói 'del img', và sau đó có thể xóa tệp.
vật lý

2
Xin lưu ý rằng đây là một chi tiết triển khai vì đặc tả ngôn ngữ không đảm bảo khi __del__()phương thức trên các đối tượng rác được gọi hoặc ngay cả khi nó được gọi. Vì vậy, các API không có cách nào để phát hành các nguồn tài nguyên ngoài việc hy vọng __del__()phương thức này được gọi một thời gian (ngay sau khi đối tượng là rác) bị phá vỡ ở một mức độ nào đó.
BlackJack

6

delthường thấy trong __init__.pycác tập tin. Bất kỳ biến toàn cục nào được xác định trong __init__.pytệp sẽ tự động được "xuất" (nó sẽ được bao gồm trong a from module import *). Một cách để tránh điều này là xác định__all__ , nhưng điều này có thể trở nên lộn xộn và không phải ai cũng sử dụng nó.

Ví dụ: nếu bạn có mã __init__.pynhư

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

Sau đó, mô-đun của bạn sẽ xuất systên. Thay vào đó bạn nên viết

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

del sys

4

Như một ví dụ về những gì delcó thể được sử dụng cho, tôi thấy nó hữu ích trong các tình huống như thế này:

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'

Hai hàm này có thể nằm trong các gói / mô-đun khác nhau và lập trình viên không cần biết đối số giá trị mặc định ctrongf thực sự có. Vì vậy, bằng cách sử dụng kwargs kết hợp với del, bạn có thể nói "Tôi muốn giá trị mặc định trên c" bằng cách đặt nó thành Không (hoặc trong trường hợp này cũng để lại nó).

Bạn có thể làm điều tương tự với một cái gì đó như:

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

    return f(**kwargs)

Tuy nhiên tôi thấy ví dụ trước DRY và thanh lịch hơn.


3

Khi nào del hữu ích trong python?

Bạn có thể sử dụng nó để loại bỏ một phần tử duy nhất của một mảng thay vì cú pháp lát x[i:i+1]=[]. Điều này có thể hữu ích nếu ví dụ bạn đang ở os.walkvà muốn xóa một phần tử trong thư mục. Tôi sẽ không xem xét một từ khóa hữu ích cho việc này mặc dù, vì người ta chỉ có thể tạo ra một [].remove(index)phương thức ( .removephương thức này thực sự là tìm kiếm và loại bỏ-thể hiện-giá trị đầu tiên).


7
[].pop(index)[].remove(item). Đừng sử dụng biến có tên "index"khi nói về giá trị, làm cho nó trông khó hiểu.
Trượt tuyết

@Ski pop không sử dụng chỉ mục . Đây là một câu trả lời hợp lệ trong khi một nửa những câu trả lời này chỉ đưa ra một ví dụ sử dụng del trong đó Không ai cũng sẽ làm việc. Một đối tượng danh sách được đặt thành Không có trong danh sách trong khi del xóa các mục.
dùng5389726598465

3

Tôi đã thấy delhữu ích cho việc quản lý bộ nhớ giả thủ công khi xử lý dữ liệu lớn với Numpy. Ví dụ:

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

Đây có thể là sự khác biệt giữa việc đưa tập lệnh dừng hoạt động khi hệ thống hoán đổi như điên khi Python GC không thể theo kịp và nó chạy hoàn toàn trơn tru dưới ngưỡng bộ nhớ lỏng để lại nhiều khoảng trống để sử dụng máy để duyệt và mã trong khi nó hoạt động.


2

Tôi nghĩ một trong những lý do mà del có cú pháp riêng của nó là việc thay thế nó bằng một hàm có thể khó trong một số trường hợp do nó hoạt động trên ràng buộc hoặc biến và không phải là giá trị mà nó tham chiếu. Do đó, nếu một phiên bản chức năng của del được tạo ra một bối cảnh sẽ cần phải được truyền vào. Del foo sẽ cần phải trở thành toàn cầu (). Remove ('foo') hoặc locals (). Remove ('foo') bị rối và ít đọc hơn. Tuy nhiên, tôi nói rằng việc loại bỏ del sẽ tốt khi sử dụng dường như rất hiếm. Nhưng loại bỏ các tính năng / lỗ hổng ngôn ngữ có thể gây đau đớn. Có lẽ python 4 sẽ gỡ bỏ nó :)


2

Tôi muốn giải thích về câu trả lời được chấp nhận để làm nổi bật sắc thái giữa việc đặt một biến Noneso với loại bỏ nó bằng del:

Cho biến foo = 'bar'và định nghĩa hàm sau:

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

Khi được tuyên bố ban đầu, test_var(foo)sản lượng variable tested truenhư mong đợi.

Bây giờ cố gắng:

foo = None
test_var(foo)

mà năng suất variable tested false.

Tương phản hành vi này với:

del foo
test_var(foo)

mà bây giờ tăng lên NameError: name 'foo' is not defined.


0

Một cách sử dụng thích hợp khác: Trong pyroot với ROOT5 hoặc ROOT6, "del" có thể hữu ích để loại bỏ một đối tượng python tham chiếu đến một đối tượng C ++ không còn tồn tại. Điều này cho phép tra cứu động của pyroot để tìm một đối tượng C ++ có tên giống hệt nhau và liên kết nó với tên python. Vì vậy, bạn có thể có một kịch bản như:

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

Hy vọng, thị trường ngách này sẽ được đóng lại với quản lý đối tượng saner của ROOT7.


0

Lệnh "del" rất hữu ích để kiểm soát dữ liệu trong một mảng, ví dụ:

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

Đầu ra:

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


-2

Khi tôi phải sử dụng:

del serial
serial = None

bởi vì chỉ sử dụng:

serial = None

không phát hành cổng nối tiếp đủ nhanh để mở lại ngay lập tức. Từ bài học đó tôi đã học được điều đó delthực sự có nghĩa là: "GC này NGAY BÂY GIỜ! Và đợi cho đến khi nó hoàn thành" và điều đó thực sự hữu ích trong rất nhiều tình huống. Tất nhiên, bạn có thể có một system.gc.del_this_and_wait_balbalbalba(obj).


5
Hmm ... Điều đó thực sự không nên tạo ra sự khác biệt. Mặc dù, có lẽ vấn đề của bạn đã được khắc phục bằng sự chậm trễ thêm mà nó đã giới thiệu?
Winston Ewert

3
Tôi không nghĩ rằng bạn có thể sao lưu GC này ngay bây giờ bằng bất kỳ tài liệu nào. Tôi nghĩ rằng việc dựa vào GC và gọi đến __del__()luôn là sai trong Python (tuy nhiên tôi không biết lý do cho thiết kế này) và tốt hơn là sử dụng API của trình quản lý bối cảnh ( withtuyên bố).
Pavel imerda

1
Đó là một loại lập trình voodoo. Xem mô tả phương thức và ghi chú trong tài liệu cho đối tượng .__ del __ () để biết chi tiết hãy nhớ rằng điều này chỉ mô tả việc triển khai hiện tại của CPythons với việc đếm tham chiếu. Các triển khai Python khác (PyPy, Jython, IronPython, Brython, Nhận) hoặc các triển khai CPython trong tương lai có thể sử dụng một sơ đồ thu gom rác khác. Jython sử dụng JVMs GC không xóa các đối tượng ngay lập tức. Các serialmô-đun cũng làm việc với Jython để bạn thuê không làm việc đó!
BlackJack

BTW gc.collect sẽ là cách tái chế rõ ràng. Được hỗ trợ trong hầu hết các triển khai python.
tdihp

-2

del tương đương với "unset" trong nhiều ngôn ngữ và như một điểm tham chiếu chéo chuyển từ ngôn ngữ khác sang python .. mọi người có xu hướng tìm kiếm các lệnh làm điều tương tự mà họ đã từng làm trong ngôn ngữ đầu tiên của họ ... cũng thiết lập một var thành "" hoặc không thực sự loại bỏ var khỏi scope..tôi chỉ làm trống giá trị của nó, tên của var sẽ vẫn được lưu trong bộ nhớ ... tại sao?!? trong một tập lệnh chuyên sâu về bộ nhớ..chất rác đằng sau nó chỉ là không và dù sao đi nữa ... mọi ngôn ngữ ngoài kia đều có một dạng hàm var "unset / xóa "..tại sao không phải python?


7
delkhông gọi người thu gom rác nhanh hơn = None, cũng không để lại rác trong thời gian dài. Bạn có thể muốn tìm hiểu về bộ sưu tập rác của Python.
SilverbackNet

-3

Mỗi đối tượng trong python đều có mã định danh, Loại, số tham chiếu được liên kết với nó, khi chúng tôi sử dụng del số tham chiếu bị giảm, khi số tham chiếu trở thành 0, nó là một ứng cử viên tiềm năng để thu gom rác. Điều này phân biệt del khi so sánh với việc đặt định danh thành Không có. Trong trường hợp sau, nó chỉ đơn giản có nghĩa là đối tượng bị bỏ hoang (cho đến khi chúng ta nằm ngoài phạm vi trong trường hợp đó số đếm bị giảm) và bây giờ đơn giản là định danh trỏ đến một đối tượng khác (vị trí bộ nhớ).


3
Tôi muốn xem bằng chứng về điều này. Chỉ định Không nên giảm số tham chiếu.
Jason Baker

3
Đây là một điều vô nghĩa và ngược lại với việc thu gom rác (theo nghĩa là để lại rác nằm xung quanh).
Pavel imerda
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.