Python - Weak References

Python sử dụng cơ chế đếm tham chiếu khi thực hiện chính sách thu gom rác. Mỗi khi một đối tượng trong bộ nhớ được tham chiếu, số đếm sẽ được tăng lên một. Ngược lại, khi tham chiếu bị xóa, số đếm sẽ giảm đi 1. Nếu bộ thu gom rác chạy trong nền tìm thấy bất kỳ đối tượng nào có số đếm bằng 0, nó sẽ bị xóa và bộ nhớ mà nó chiếm giữ sẽ được thu hồi.

Tham chiếu yếu là một tham chiếu không bảo vệ đối tượng khỏi việc bị thu gom rác. Nó trở nên quan trọng khi bạn cần triển khai bộ nhớ đệm cho các đối tượng lớn, cũng như trong tình huống cần giảm thiểu vấn đề từ các tham chiếu vòng.

Để tạo các tham chiếu yếu, Python đã cung cấp cho chúng ta một mô-đun có tên là weakref.

Lớp ref trong mô-đun này quản lý tham chiếu yếu đến một đối tượng. Khi được gọi, nó sẽ lấy lại đối tượng gốc.

Để tạo một tham chiếu yếu −

weakref.ref(class())

Example

import weakref
class Myclass:
   def __del__(self):
      print('(Deleting {})'.format(self))
obj = Myclass()
r = weakref.ref(obj)

print('object:', obj)
print('reference:', r)
print('call r():', r())

print('deleting obj')
del obj
print('r():', r())

Gọi đối tượng tham chiếu sau khi xóa đối tượng tham chiếu sẽ trả về None.

Nó sẽ tạo ra output

object: <__main__.Myclass object at 0x00000209D7173290>
reference: <weakref at 0x00000209D7175940; to 'Myclass' at
0x00000209D7173290>
call r(): <__main__.Myclass object at 0x00000209D7173290>
deleting obj
(Deleting <__main__.Myclass object at 0x00000209D7173290>)
r(): None

The callback Function

Constructor của lớp ref có một tham số tùy chọn gọi là hàm callback, hàm này sẽ được gọi khi đối tượng được tham chiếu bị xóa.

import weakref
class Myclass:
   def __del__(self):
      print('(Deleting {})'.format(self))
def mycallback(rfr):
   """called when referenced object is deleted"""
   print('calling ({})'.format(rfr))
obj = Myclass()
r = weakref.ref(obj, mycallback)

print('object:', obj)
print('reference:', r)
print('call r():', r())

print('deleting obj')
del obj
print('r():', r())

Nó sẽ tạo ra output

object: <__main__.Myclass object at 0x000002A0499D3590>
reference: <weakref at 0x000002A0499D59E0; to 'Myclass' at
0x000002A0499D3590>
call r(): <__main__.Myclass object at 0x000002A0499D3590>
deleting obj
(Deleting <__main__.Myclass object at 0x000002A0499D3590>)
calling (<weakref at 0x000002A0499D59E0; dead>)
r(): None

Finalizing Objects

Mô-đun weakref cung cấp lớp finalize. Đối tượng của nó được gọi khi bộ thu gom rác thu thập đối tượng. Đối tượng tồn tại cho đến khi đối tượng tham chiếu được gọi.

import weakref
class Myclass:
   def __del__(self):
      print('(Deleting {})'.format(self))

def finalizer(*args):
   print('Finalizer{!r})'.format(args))

obj = Myclass()
r = weakref.finalize(obj, finalizer, "Call to finalizer")

print('object:', obj)
print('reference:', r)
print('call r():', r())

print('deleting obj')
del obj
print('r():', r())

Nó sẽ tạo ra output

object: <__main__.Myclass object at 0x0000021015103590>
reference: <finalize object at 0x21014eabe80; for 'Myclass' at
0x21015103590>
Finalizer('Call to finalizer',))
call r(): None
deleting obj
(Deleting <__main__.Myclass object at 0x0000021015103590>)
r(): None

Mô-đun weakref cung cấp các lớp WeakKeyDictionary và WeakValueDictionary. Chúng không giữ cho các đối tượng tồn tại khi chúng xuất hiện trong các đối tượng ánh xạ. Chúng phù hợp hơn để tạo một bộ nhớ đệm cho nhiều đối tượng.

WeakKeyDictionary

Lớp ánh xạ tham chiếu các khóa một cách 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ào đến khóa.

Một thể hiện của lớp WeakKeyDictionary được tạo ra với một từ điển hiện có hoặc không có bất kỳ đối số nào. Chức năng của nó giống như một từ điển bình thường để thêm và xóa các mục ánh xạ.

Trong đoạn mã dưới đây, ba thể hiện của lớp Person được tạo ra. Sau đó, nó tạo một thể hiện của WeakKeyDictionary với một từ điển trong đó khóa là thể hiện của lớp Person và giá trị là tên của Person đó.

Chúng tôi gọi phương thức keyrefs() để truy xuất các tham chiếu yếu. Khi tham chiếu đến Person1 bị xóa, các khóa của từ điển được in ra một lần nữa. Một thể hiện Person mới được thêm vào từ điển với các khóa được tham chiếu yếu. Cuối cùng, chúng tôi lại in các khóa của từ điển.

Example

import weakref

class Person:
   def __init__(self, person_id, name, age):
      self.emp_id = person_id
      self.name = name
      self.age = age

   def __repr__(self):
      return "{} : {} : {}".format(self.person_id, self.name, self.age)
Person1 = Person(101, "Jeevan", 30)
Person2 = Person(102, "Ramanna", 35)
Person3 = Person(103, "Simran", 28)
weak_dict = weakref.WeakKeyDictionary({Person1: Person1.name, Person2: Person2.name, Person3: Person3.name})
print("Weak Key Dictionary : {}\n".format(weak_dict.data))
print("Dictionary Keys : {}\n".format([key().name for key in weak_dict.keyrefs()]))
del Person1
print("Dictionary Keys : {}\n".format([key().name for key in weak_dict.keyrefs()]))
Person4 = Person(104, "Partho", 32)
weak_dict.update({Person4: Person4.name})

print("Dictionary Keys : {}\n".format([key().name for key in weak_dict.keyrefs()]))

Nó sẽ tạo ra output

Weak Key Dictionary : {<weakref at 0x7f542b6d4180; to 'Person' at 0x7f542b8bbfd0>: 'Jeevan', <weakref at 0x7f542b6d5530; to 'Person' at 0x7f542b8bbeb0>: 'Ramanna', <weakref at 0x7f542b6d55d0; to 'Person' at 0x7f542b8bb7c0>: 'Simran'}

Dictionary Keys : ['Jeevan', 'Ramanna', 'Simran']

Dictionary Keys : ['Ramanna', 'Simran']

Dictionary Keys : ['Ramanna', 'Simran', 'Partho']

WeakValueDictionary

Lớp ánh xạ tham chiếu giá trị một cách 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ào đến giá trị đó nữa.

Chúng tôi sẽ trình bày cách tạo một từ điển với các giá trị tham chiếu yếu bằng cách sử dụng WeakValueDictionary.

Mã code tương tự như ví dụ trước, nhưng lần này chúng ta sử dụng tên của Person làm khóa và đối tượng Person làm giá trị. Chúng ta đang sử dụng phương thức valuerefs() để lấy các giá trị được tham chiếu yếu của từ điển.

Example

import weakref

class Person:
   def __init__(self, person_id, name, age):
      self.emp_id = person_id
      self.name = name
      self.age = age
   
   def __repr__(self):
      return "{} : {} : {}".format(self.person_id, self.name, self.age)

Person1 = Person(101, "Jeevan", 30)
Person2 = Person(102, "Ramanna", 35)
Person3 = Person(103, "Simran", 28)

weak_dict = weakref.WeakValueDictionary({Person1.name:Person1, Person2.name:Person2, Person3.name:Person3})
print("Weak Value Dictionary : {}\n".format(weak_dict.data))
print("Dictionary Values : {}\n".format([value().name for value in weak_dict.valuerefs()]))
del Person1
print("Dictionary Values : {}\n".format([value().name for value in weak_dict.valuerefs()]))
Person4 = Person(104, "Partho", 32)
weak_dict.update({Person4.name: Person4})
print("Dictionary Values : {}\n".format([value().name for value in weak_dict.valuerefs()]))

Nó sẽ tạo ra output

Weak Value Dictionary : {'Jeevan': <weakref at 0x7f3af9fe4180; to 'Person' at 0x7f3afa1c7fd0>, 'Ramanna': <weakref at 0x7f3af9fe5530; to 'Person' at 0x7f3afa1c7eb0>, 'Simran': <weakref at 0x7f3af9fe55d0; to 'Person' at 0x7f3afa1c77c0>}

Dictionary Values : ['Jeevan', 'Ramanna', 'Simran']

Dictionary Values : ['Ramanna', 'Simran']

Dictionary Values : ['Ramanna', 'Simran', 'Partho']