weakref --- Tài liệu tham khảo yếu

Source code: Lib/weakref.py


Mô-đun weakref cho phép lập trình viên Python tạo weak references cho các đối tượng.

Trong phần sau đây, thuật ngữ referent có nghĩa là đối tượng được tham chiếu bằng tham chiếu yếu.

Tham chiếu yếu đến một đối tượng là không đủ để giữ cho đối tượng tồn tại: khi các tham chiếu duy nhất còn lại đến một tham chiếu là các tham chiếu yếu, garbage collection có thể tự do hủy tham chiếu và sử dụng lại bộ nhớ của nó cho mục đích khác. Tuy nhiên, cho đến khi đối tượng thực sự bị phá hủy, tham chiếu yếu có thể trả về đối tượng ngay cả khi không có tham chiếu mạnh tới nó.

Công dụng chính của các tham chiếu yếu là triển khai bộ nhớ đệm hoặc ánh xạ chứa các đối tượng lớn, trong đó người ta mong muốn rằng một đối tượng lớn không được giữ nguyên chỉ vì nó xuất hiện trong bộ nhớ đệm hoặc ánh xạ.

Ví dụ: nếu bạn có một số đối tượng hình ảnh nhị phân lớn, bạn có thể muốn liên kết tên với mỗi đối tượng. Nếu bạn sử dụng từ điển Python để ánh xạ tên thành hình ảnh hoặc hình ảnh thành tên thì đối tượng hình ảnh sẽ vẫn tồn tại chỉ vì chúng xuất hiện dưới dạng giá trị hoặc khóa trong từ điển. Các lớp WeakKeyDictionaryWeakValueDictionary do mô-đun weakref cung cấp là một lựa chọn thay thế, sử dụng các tham chiếu yếu để xây dựng các ánh xạ không giữ cho các đối tượng tồn tại chỉ vì chúng xuất hiện trong các đối tượng ánh xạ. Ví dụ: nếu một đối tượng hình ảnh là một giá trị trong WeakValueDictionary thì khi các tham chiếu cuối cùng còn lại đến đối tượng hình ảnh đó là các tham chiếu yếu được giữ bởi ánh xạ yếu, việc thu thập rác có thể lấy lại đối tượng và các mục tương ứng của nó trong ánh xạ yếu sẽ bị xóa.

WeakKeyDictionaryWeakValueDictionary sử dụng các tham chiếu yếu trong quá trình triển khai, thiết lập các hàm gọi lại trên các tham chiếu yếu để thông báo cho các từ điển yếu khi một khóa hoặc giá trị được thu hồi bởi bộ thu thập rác. WeakSet triển khai giao diện set nhưng giữ các tham chiếu yếu đến các thành phần của nó, giống như WeakKeyDictionary.

finalize cung cấp một cách đơn giản để đăng ký hàm dọn dẹp sẽ được gọi khi một đối tượng được thu gom rác. Việc sử dụng này đơn giản hơn so với việc thiết lập chức năng gọi lại trên một tham chiếu yếu thô, vì mô-đun tự động đảm bảo rằng trình hoàn thiện vẫn tồn tại cho đến khi đối tượng được thu thập.

Hầu hết các chương trình sẽ thấy rằng việc sử dụng một trong các loại vùng chứa yếu này hoặc finalize là tất cả những gì chúng cần -- thông thường không cần thiết phải trực tiếp tạo các tham chiếu yếu của riêng bạn. Máy móc cấp thấp được mô-đun weakref thể hiện nhằm mang lại lợi ích cho việc sử dụng nâng cao.

Không phải tất cả các đối tượng đều có thể được tham chiếu yếu. Các đối tượng hỗ trợ tham chiếu yếu bao gồm các phiên bản lớp, hàm viết bằng Python (nhưng không phải bằng C), phương thức phiên bản, bộ, Frozensets, một số file objects, generators, đối tượng kiểu, ổ cắm, mảng, deques, đối tượng mẫu biểu thức chính quy và đối tượng mã.

Thay đổi trong phiên bản 3.2: Đã thêm hỗ trợ cho các đối tượng thread.lock, threading.Lock và mã.

Một số loại tích hợp sẵn như listdict không hỗ trợ trực tiếp các tham chiếu yếu nhưng có thể thêm hỗ trợ thông qua phân lớp:

lớp Dict(dict):
    vượt qua

obj = Dict(red=1, green=2, blue=3) đối tượng # this có khả năng tham chiếu yếu

Các loại tích hợp khác như tupleint không hỗ trợ các tham chiếu yếu ngay cả khi được phân lớp con.

Các loại tiện ích mở rộng có thể dễ dàng được tạo để hỗ trợ các tham chiếu yếu; xem Hỗ trợ tham khảo yếu.

Khi __slots__ được xác định cho một loại nhất định, hỗ trợ tham chiếu yếu sẽ bị tắt trừ khi chuỗi '__weakref__' cũng xuất hiện trong chuỗi các chuỗi trong khai báo __slots__. Xem __slots__ documentation để biết chi tiết.

class weakref.ref(object[, callback])

Trả về tham chiếu yếu cho object. Đối tượng ban đầu có thể được truy xuất bằng cách gọi đối tượng tham chiếu nếu đối tượng tham chiếu vẫn còn tồn tại; nếu người tham chiếu không còn tồn tại, việc gọi đối tượng tham chiếu sẽ khiến None được trả về. Nếu callback được cung cấp chứ không phải None và đối tượng yếu được trả về vẫn còn tồn tại, lệnh gọi lại sẽ được gọi khi đối tượng sắp được hoàn tất; đối tượng tham chiếu yếu sẽ được chuyển làm tham số duy nhất cho lệnh gọi lại; người giới thiệu sẽ không còn nữa.

Cho phép xây dựng nhiều tham chiếu yếu cho cùng một đối tượng. Các cuộc gọi lại được đăng ký cho mỗi tham chiếu yếu sẽ được gọi từ cuộc gọi lại được đăng ký gần đây nhất đến cuộc gọi lại được đăng ký cũ nhất.

Các ngoại lệ do lệnh gọi lại đưa ra sẽ được ghi chú trên đầu ra lỗi tiêu chuẩn, nhưng không thể truyền bá; chúng được xử lý theo cách giống hệt như các ngoại lệ được tạo ra từ phương thức __del__() của đối tượng.

Tham chiếu yếu là hashable nếu object có thể băm được. Họ sẽ duy trì giá trị băm ngay cả sau khi object bị xóa. Nếu hash() được gọi lần đầu tiên chỉ sau khi object bị xóa, cuộc gọi sẽ tăng TypeError.

Tham chiếu yếu hỗ trợ kiểm tra tính bằng nhau nhưng không hỗ trợ thứ tự. Nếu các tham chiếu vẫn còn tồn tại thì hai tham chiếu có cùng mối quan hệ bình đẳng như các tham chiếu của chúng (bất kể callback). Nếu một trong hai tham chiếu đã bị xóa, các tham chiếu chỉ bằng nhau nếu các đối tượng tham chiếu là cùng một đối tượng.

Đây là loại có thể phân lớp chứ không phải là chức năng của nhà máy.

__callback__

Thuộc tính chỉ đọc này trả về cuộc gọi lại hiện được liên kết với điểm yếu. Nếu không có lệnh gọi lại hoặc nếu tham chiếu của điểm yếu không còn tồn tại thì thuộc tính này sẽ có giá trị None.

Thay đổi trong phiên bản 3.4: Đã thêm thuộc tính __callback__.

weakref.proxy(object[, callback])

Trả lại proxy cho object sử dụng tham chiếu yếu. Điều này hỗ trợ việc sử dụng proxy trong hầu hết các ngữ cảnh thay vì yêu cầu hội thảo rõ ràng được sử dụng với các đối tượng tham chiếu yếu. Đối tượng được trả về sẽ có loại ProxyType hoặc CallableProxyType, tùy thuộc vào việc object có thể gọi được hay không. Các đối tượng proxy không phải là hashable bất kể người tham chiếu; điều này tránh được một số vấn đề liên quan đến bản chất có thể thay đổi cơ bản của chúng và ngăn chặn việc sử dụng chúng làm khóa từ điển. callback giống với tham số cùng tên của hàm ref().

Việc truy cập một thuộc tính của đối tượng proxy sau khi tham chiếu được thu thập rác sẽ làm tăng ReferenceError.

Thay đổi trong phiên bản 3.8: Mở rộng hỗ trợ toán tử trên các đối tượng proxy để bao gồm các toán tử nhân ma trận @@=.

weakref.getweakrefcount(object)

Trả về số lượng tham chiếu yếu và proxy tham chiếu đến object.

weakref.getweakrefs(object)

Trả về danh sách tất cả các đối tượng proxy và tham chiếu yếu tham chiếu đến object.

class weakref.WeakKeyDictionary([dict])

Mapping class that references keys weakly. Các mục trong từ điển sẽ bị loại bỏ khi không còn tham chiếu mạnh đến khóa nữa. Điều này có thể được sử dụng để liên kết dữ liệu bổ sung với một đối tượng thuộc sở hữu của các phần khác của ứng dụng mà không cần thêm thuộc tính cho các đối tượng đó. Điều này có thể đặc biệt hữu ích với các đối tượng ghi đè quyền truy cập thuộc tính.

Lưu ý rằng khi một khóa có giá trị bằng khóa hiện có (nhưng không nhận dạng bằng) được chèn vào từ điển, nó sẽ thay thế giá trị nhưng không thay thế khóa hiện có. Do đó, khi tham chiếu đến khóa gốc bị xóa, nó cũng xóa mục nhập trong từ điển:

>>> lớp T(str): đậu
...
>>> k1, k2 = T(), T()
>>> d = yếuref.WeakKeyDictionary()
>>> d[k1] = 1 # d = {k1: 1}
>>> d[k2] = 2 # d = {k1: 2}
>>> del k1 # d = {}

Một cách giải quyết khác là xóa khóa trước khi gán lại

>>> lớp T(str): đậu
...
>>> k1, k2 = T(), T()
>>> d = yếuref.WeakKeyDictionary()
>>> d[k1] = 1 # d = {k1: 1}
>>> del d[k1]
>>> d[k2] = 2 # d = {k2: 2}
>>> del k1 # d = {k2: 2}

Thay đổi trong phiên bản 3.9: Đã thêm hỗ trợ cho các toán tử ||=, như được chỉ định trong PEP 584.

Các đối tượng WeakKeyDictionary có một phương thức bổ sung hiển thị trực tiếp các tham chiếu bên trong. Các tham chiếu không được đảm bảo là "trực tiếp" tại thời điểm chúng được sử dụng, vì vậy kết quả của việc gọi các tham chiếu cần phải được kiểm tra trước khi sử dụng. Điều này có thể được sử dụng để tránh tạo các tham chiếu khiến trình thu gom rác giữ các khóa lâu hơn mức cần thiết.

WeakKeyDictionary.keyrefs()

Trả về một tham chiếu yếu cho các khóa có thể lặp lại.

class weakref.WeakValueDictionary([dict])

Lớp ánh xạ tham chiếu các giá trị yếu. Các mục trong từ điển sẽ bị loại bỏ khi không còn tham chiếu mạnh đến giá trị nữa.

Thay đổi trong phiên bản 3.9: Đã thêm hỗ trợ cho các toán tử ||=, như được chỉ định trong PEP 584.

Đối tượng WeakValueDictionary có một phương thức bổ sung có cùng vấn đề với phương thức WeakKeyDictionary.keyrefs().

WeakValueDictionary.valuerefs()

Trả về giá trị có thể lặp lại của các tham chiếu yếu.

class weakref.WeakSet([elements])

Đặt lớp giữ các tham chiếu yếu đến các phần tử của nó. Một phần tử sẽ bị loại bỏ khi không còn tham chiếu mạnh đến nó nữa.

class weakref.WeakMethod(method[, callback])

Lớp con ref tùy chỉnh mô phỏng tham chiếu yếu đến một phương thức bị ràng buộc (tức là một phương thức được xác định trên một lớp và tra cứu trên một phiên bản). Vì một phương thức bị ràng buộc là phù du nên một tham chiếu yếu tiêu chuẩn không thể giữ được nó. WeakMethod có mã đặc biệt để tạo lại phương thức bị ràng buộc cho đến khi đối tượng hoặc hàm ban đầu chết

>>> lớp C:
... phương thức def (tự):
... print("phương thức được gọi!")
...
>>> c = C()
>>> r = yếuref.ref(c.method)
>>> r()
>>> r = yếuref.WeakMethod(c.method)
>>> r()
<phương thức ràng buộc C.phương thức của đối tượng <__main__.C tại 0x7fc859830220>>
>>> r()()
phương thức được gọi!
>>> del c
>>> gc.collect()
0
>>> r()
>>>

callback giống với tham số cùng tên của hàm ref().

Added in version 3.4.

class weakref.finalize(obj, func, /, *args, **kwargs)

Trả về một đối tượng hoàn thiện có thể gọi được, đối tượng này sẽ được gọi khi obj được thu gom rác. Không giống như một tham chiếu yếu thông thường, trình hoàn thiện sẽ luôn tồn tại cho đến khi đối tượng tham chiếu được thu thập, giúp đơn giản hóa đáng kể việc quản lý vòng đời.

Bộ hoàn thiện được coi là alive cho đến khi nó được gọi (rõ ràng hoặc lúc thu gom rác) và sau đó là dead. Việc gọi trình hoàn thiện trực tiếp trả về kết quả đánh giá func(*arg, **kwargs), trong khi gọi trình hoàn thiện đã chết sẽ trả về None.

Các ngoại lệ do các lệnh gọi lại bộ hoàn thiện đưa ra trong quá trình thu gom rác sẽ được hiển thị trên đầu ra lỗi tiêu chuẩn nhưng không thể lan truyền. Chúng được xử lý theo cách tương tự như các ngoại lệ được tạo ra từ phương thức __del__() của đối tượng hoặc lệnh gọi lại của tham chiếu yếu.

Khi chương trình thoát, mỗi trình hoàn thiện trực tiếp còn lại sẽ được gọi trừ khi thuộc tính atexit của nó được đặt thành false. Chúng được gọi theo thứ tự ngược lại của sự sáng tạo.

Trình hoàn thiện sẽ không bao giờ gọi lại lệnh gọi lại của nó trong phần sau của interpreter shutdown khi các mô-đun toàn cầu có khả năng đã được thay thế bằng None.

__call__()

Nếu self còn sống thì đánh dấu nó là đã chết và trả về kết quả gọi func(*args, **kwargs). Nếu self chết thì trả về None.

detach()

Nếu self còn sống thì đánh dấu nó là đã chết và trả về bộ dữ liệu (obj, func, args, kwargs). Nếu self chết thì trả về None.

peek()

Nếu self còn sống thì trả về bộ (obj, func, args, kwargs). Nếu self chết thì trả về None.

alive

Thuộc tính đúng nếu bộ hoàn thiện còn sống, nếu không thì sai.

atexit

Thuộc tính boolean có thể ghi theo mặc định là đúng. Khi chương trình thoát, nó sẽ gọi tất cả các bộ hoàn thiện trực tiếp còn lại mà atexit là đúng. Chúng được gọi theo thứ tự ngược lại của sự sáng tạo.

Ghi chú

Điều quan trọng là phải đảm bảo rằng func, argskwargs không sở hữu bất kỳ tham chiếu nào đến obj, trực tiếp hoặc gián tiếp, vì nếu không, obj sẽ không bao giờ được thu thập rác. Đặc biệt, func không nên là một phương thức bị ràng buộc của obj.

Added in version 3.4.

weakref.ReferenceType

Đối tượng loại cho các đối tượng tham chiếu yếu.

weakref.ProxyType

Đối tượng kiểu cho proxy của các đối tượng không thể gọi được.

weakref.CallableProxyType

Đối tượng loại cho proxy của các đối tượng có thể gọi được.

weakref.ProxyTypes

Trình tự chứa tất cả các đối tượng loại cho proxy. Điều này có thể giúp việc kiểm tra xem một đối tượng có phải là proxy trở nên đơn giản hơn hay không mà không phụ thuộc vào việc đặt tên cho cả hai loại proxy.

Xem thêm

PEP 205 - Tài liệu tham khảo yếu

Đề xuất và lý do căn bản cho tính năng này, bao gồm các liên kết đến các triển khai trước đó và thông tin về các tính năng tương tự bằng các ngôn ngữ khác.

Đối tượng tham chiếu yếu

Đối tượng tham chiếu yếu không có phương thức và không có thuộc tính nào ngoài ref.__callback__. Một đối tượng tham chiếu yếu cho phép lấy được tham chiếu, nếu nó vẫn tồn tại, bằng cách gọi nó:

>>> import weakref
>>> class Object:
...     pass
...
>>> o = Object()
>>> r = weakref.ref(o)
>>> o2 = r()
>>> o is o2
True

Nếu tham chiếu không còn tồn tại, việc gọi đối tượng tham chiếu sẽ trả về None:

>>> del o, o2
>>> print(r())
None

Việc kiểm tra xem đối tượng tham chiếu yếu vẫn còn hoạt động hay không nên được thực hiện bằng cách sử dụng biểu thức ref() is not None. Thông thường, mã ứng dụng cần sử dụng đối tượng tham chiếu phải tuân theo mẫu này

# r là một đối tượng tham chiếu yếu
o = r()
nếu o  Không:
    # referent đã được thu gom rác
    print("Đối tượng đã bị hủy phân bổ; không thể đóng băng.")
khác:
    print("Đối tượng vẫn còn tồn tại!")
    o.do_something_useful()

Việc sử dụng thử nghiệm riêng biệt cho "độ sống" sẽ tạo ra các điều kiện tương tranh trong các ứng dụng theo luồng; một luồng khác có thể khiến tham chiếu yếu bị vô hiệu trước khi tham chiếu yếu được gọi; Thành ngữ hiển thị ở trên là an toàn trong các ứng dụng luồng cũng như các ứng dụng luồng đơn.

Các phiên bản chuyên dụng của đối tượng ref có thể được tạo thông qua phân lớp. Điều này được sử dụng khi triển khai WeakValueDictionary để giảm chi phí bộ nhớ cho mỗi mục trong ánh xạ. Điều này có thể hữu ích nhất khi liên kết thông tin bổ sung với một tham chiếu, nhưng cũng có thể được sử dụng để chèn xử lý bổ sung vào các cuộc gọi nhằm truy xuất tham chiếu.

Ví dụ này cho thấy cách sử dụng một lớp con của ref để lưu trữ thông tin bổ sung về một đối tượng và ảnh hưởng đến giá trị được trả về khi tham chiếu được truy cập:

nhập điểm yếu

lớp ExtendedRef (yếu.ref):
    def __init__(self, ob, callback=None, /, **chú thích):
        super().__init__(ob, gọi lại)
        self.__counter = 0
        cho k, v trong chú thích.items():
            setattr(tự, k, v)

    chắc chắn __call__(tự):
        """Trả về một cặp chứa tham chiếu và số lượng
        số lần tham chiếu đã được gọi.
        """
        ob = super().__call__()
        nếu ob không phải  Không :
            self.__counter += 1
            ob = (ob, self.__counter)
        trả lại

Ví dụ

Ví dụ đơn giản này cho thấy cách ứng dụng có thể sử dụng ID đối tượng để truy xuất các đối tượng mà nó đã thấy trước đó. ID của các đối tượng sau đó có thể được sử dụng trong các cấu trúc dữ liệu khác mà không buộc các đối tượng vẫn tồn tại, nhưng các đối tượng vẫn có thể được ID truy xuất nếu chúng làm như vậy.

nhập điểm yếu

_id2obj_dict = yếuref.WeakValueDictionary()

chắc chắn nhớ(obj):
    oid = id(obj)
    _id2obj_dict[oid] = obj
    trả lại oid

def id2obj(oid):
    trả về _id2obj_dict[oid]

Đối tượng hoàn thiện

Lợi ích chính của việc sử dụng finalize là giúp việc đăng ký cuộc gọi lại trở nên đơn giản mà không cần phải bảo toàn đối tượng hoàn thiện được trả về. Ví dụ

>>> import weakref
>>> class Object:
...     pass
...
>>> kenny = Object()
>>> weakref.finalize(kenny, print, "You killed Kenny!")
<finalize object at ...; for 'Object' at ...>
>>> del kenny
You killed Kenny!

Bộ hoàn thiện cũng có thể được gọi trực tiếp. Tuy nhiên, trình hoàn thiện sẽ gọi lại lệnh gọi lại nhiều nhất một lần.

>>> def callback(x, y, z):
...     print("CALLBACK")
...     return x + y + z
...
>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> assert f.alive
>>> assert f() == 6
CALLBACK
>>> assert not f.alive
>>> f()                     # callback not called because finalizer dead
>>> del obj                 # callback not called because finalizer dead

Bạn có thể hủy đăng ký trình hoàn thiện bằng phương pháp detach() của nó. Điều này sẽ tắt trình hoàn thiện và trả về các đối số được truyền cho hàm tạo khi nó được tạo.

>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> f.detach()
(<...Object object ...>, <function callback ...>, (1, 2), {'z': 3})
>>> newobj, func, args, kwargs = _
>>> assert not f.alive
>>> assert newobj is obj
>>> assert func(*args, **kwargs) == 6
CALLBACK

Trừ khi bạn đặt thuộc tính atexit thành False, trình hoàn thiện sẽ được gọi khi chương trình thoát nếu nó vẫn còn hoạt động. Ví dụ

>>> obj = Đối tượng()
>>> yếuref.finalize(obj, print, "obj đã chết hoặc đang thoát")
<hoàn thiện đối tượng tại ...; cho 'Đối tượng' tại ...>
>>> thoát()
obj đã chết hoặc đang thoát

So sánh các công cụ hoàn thiện với các phương pháp __del__()

Giả sử chúng ta muốn tạo một lớp có các thể hiện đại diện cho các thư mục tạm thời. Các thư mục sẽ bị xóa cùng với nội dung của chúng khi xảy ra sự kiện đầu tiên sau đây:

  • đối tượng là rác được thu thập,

  • phương thức remove() của đối tượng được gọi, hoặc

  • chương trình thoát ra.

Chúng ta có thể thử triển khai lớp bằng phương thức __del__() như sau:

lớp TempDir:
    định nghĩa __init__(tự):
        self.name = tempfile.mkdtemp()

    chắc chắn loại bỏ (tự):
        nếu self.name không phải  Không:
            im lặng.rmtree(self.name)
            self.name = Không

    @property
    def đã bị xóa (tự):
        return self.name  Không 

    chắc chắn __del__(tự):
        tự.remove()

Bắt đầu với Python 3.4, các phương thức __del__() không còn ngăn chặn việc thu thập rác trong các chu trình tham chiếu và các mô-đun toàn cầu không còn bị buộc phải chuyển sang None trong interpreter shutdown nữa. Vì vậy, mã này sẽ hoạt động mà không gặp bất kỳ sự cố nào trên CPython.

Tuy nhiên, việc xử lý các phương thức __del__() nổi tiếng là cách triển khai cụ thể vì nó phụ thuộc vào các chi tiết nội bộ của quá trình triển khai trình thu gom rác của trình thông dịch.

Một giải pháp thay thế mạnh mẽ hơn có thể là xác định một trình hoàn thiện chỉ tham chiếu các hàm và đối tượng cụ thể mà nó cần, thay vì có quyền truy cập vào trạng thái đầy đủ của đối tượng:

lớp TempDir:
    định nghĩa __init__(tự):
        self.name = tempfile.mkdtemp()
        self._finalizer = yếuref.finalize(self, Shutil.rmtree, self.name)

    chắc chắn loại bỏ (tự):
        self._finalizer()

    @property
    def đã bị xóa (tự):
        trả về không self._finalizer.alive

Được xác định như thế này, trình hoàn thiện của chúng tôi chỉ nhận được một tham chiếu đến các chi tiết cần thiết để dọn sạch thư mục một cách thích hợp. Nếu đối tượng không bao giờ được thu gom rác thì trình hoàn thiện sẽ vẫn được gọi khi thoát.

Ưu điểm khác của công cụ hoàn thiện dựa trên yếu là chúng có thể được sử dụng để đăng ký công cụ hoàn thiện cho các lớp trong đó định nghĩa được kiểm soát bởi bên thứ ba, chẳng hạn như chạy mã khi mô-đun không được tải

nhập điểm yếu, sys
def dỡ hàng_module():
    # implicit tham chiếu đến toàn cục mô-đun từ thân hàm
yếuref.finalize(sys.modules[__name__], dỡ_module)

Ghi chú

Nếu bạn tạo một đối tượng bộ hoàn thiện trong một luồng daemon ngay khi chương trình thoát thì có khả năng bộ hoàn thiện không được gọi khi thoát. Tuy nhiên, trong một chuỗi daemon atexit.register(), try: ... finally: ...with: ... cũng không đảm bảo rằng quá trình dọn dẹp sẽ diễn ra.