


Latar Belakang
Sebelum kita mula membincangkan rujukan yang lemah ( weakref ), mari kita lihat dahulu apakah rujukan yang lemah? Apa sebenarnya yang dilakukannya?
Andaikan kita mempunyai atur cara berbilang benang yang memproses data aplikasi secara serentak:
# 占用大量资源,创建销毁成本很高\ class Data:\ def __init__(self, key):\ pass
Data aplikasi Data dikenal pasti secara unik oleh kunci dan data yang sama boleh diakses oleh berbilang rangkaian pada masa yang sama masa. Memandangkan Data memerlukan banyak sumber sistem, kos penciptaan dan penggunaan adalah tinggi. Kami berharap Data hanya mengekalkan satu salinan dalam program, dan tidak mahu menciptanya berulang kali walaupun ia diakses oleh berbilang rangkaian pada masa yang sama.
Untuk tujuan ini, kami cuba mereka bentuk Cacher middleware caching:
import threading # 数据缓存 class Cacher: def __init__(self): self.pool = {} self.lock = threading.Lock() def get(self, key): with self.lock: data = self.pool.get(key) if data: return data self.pool[key] = data = Data(key) return data
Cacher secara dalaman menggunakan objek dict untuk cache salinan Data yang dibuat, dan menyediakan kaedah dapatkan untuk mendapatkan Data data aplikasi. Apabila kaedah dapatkan memperoleh data, ia mula-mula menyemak kamus cache Jika data sudah wujud, ia dikembalikan secara langsung jika data tidak wujud, ia mencipta satu dan menyimpannya dalam kamus. Oleh itu, data dimasukkan ke dalam kamus cache selepas ia mula-mula dibuat Jika urutan lain mengaksesnya pada masa yang sama kemudian, salinan yang sama dalam cache akan digunakan.
Rasa sangat baik! Tetapi lalat dalam salap ialah: Cacher mempunyai risiko kebocoran sumber!
Oleh kerana setelah Data dibuat, ia disimpan dalam kamus cache dan tidak akan dikeluarkan! Dalam erti kata lain, sumber program, seperti ingatan, akan terus berkembang dan akhirnya boleh meletup. Oleh itu, kami berharap bahawa sekeping data boleh dikeluarkan secara automatik selepas semua rangkaian tidak lagi mengaksesnya.
Kami boleh mengekalkan bilangan rujukan data dalam Cacher, dan kaedah dapatkan secara automatik mengumpulkan kiraan ini. Pada masa yang sama, kaedah alih keluar baharu disediakan untuk mengeluarkan data. Ia mula-mula mengurangkan bilangan rujukan dan memadamkan data daripada medan cache apabila bilangan rujukan menurun kepada sifar.
Benang memanggil kaedah dapatkan untuk mendapatkan data Selepas data habis, kaedah alih keluar perlu dipanggil untuk melepaskannya. Cacher adalah bersamaan dengan melaksanakan kaedah pengiraan rujukan itu sendiri, yang terlalu menyusahkan! Adakah Python tidak mempunyai mekanisme pengumpulan sampah terbina dalam? Mengapakah aplikasi itu perlu melaksanakannya sendiri?
Inti utama konflik terletak pada kamus cache Cacher: sebagai perisian tengah, ia tidak menggunakan objek data itu sendiri, jadi secara teorinya ia tidak sepatutnya mempunyai rujukan kepada data. Adakah terdapat teknologi hitam yang boleh mencari objek sasaran tanpa menghasilkan rujukan? Kami tahu bahawa tugasan menjana rujukan!
Penggunaan biasa
Pada masa ini, Rujukan lemah ( weakref ) membuat penampilan hebat! Rujukan yang lemah ialah objek khas yang boleh dikaitkan dengan objek sasaran tanpa menghasilkan rujukan.
# 创建一个数据 >>> d = Data('fasionchan.com') >>> d <__main__.Data object at 0x1018571f0> # 创建一个指向该数据的弱引用 >>> import weakref >>> r = weakref.ref(d) # 调用弱引用对象,即可找到指向的对象 >>> r() <__main__.Data object at 0x1018571f0> >>> r() is d True # 删除临时变量d,Data对象就没有其他引用了,它将被回收 >>> del d # 再次调用弱引用对象,发现目标Data对象已经不在了(返回None) >>> r()
Dengan cara ini, kita hanya perlu menukar kamus cache Cacher untuk menyimpan rujukan yang lemah, dan masalah akan diselesaikan!
import threading import weakref # 数据缓存 class Cacher: def __init__(self): self.pool = {} self.lock = threading.Lock() def get(self, key): with self.lock: r = self.pool.get(key) if r: data = r() if data: return data data = Data(key) self.pool[key] = weakref.ref(data) return data
Memandangkan kamus cache hanya menyimpan rujukan yang lemah kepada objek Data, Cacher tidak akan menjejaskan kiraan rujukan objek Data. Apabila semua urutan telah selesai menggunakan data, kiraan rujukan turun kepada sifar dan dikeluarkan.
Malah, adalah sangat biasa untuk menggunakan kamus untuk cache objek data Atas sebab ini, modul weakref juga menyediakan dua objek kamus yang hanya menyimpan rujukan yang lemah:
- weakref. WeakKeyDictionary , kunci hanya menyimpan kelas pemetaan rujukan lemah (apabila kunci tidak lagi mempunyai rujukan yang kuat, entri pasangan nilai kunci akan hilang secara automatik); .WeakValueDictionary , nilai hanya memegang rujukan lemah kelas Pemetaan (apabila nilai tidak lagi mempunyai rujukan yang kukuh, entri pasangan nilai kunci akan hilang secara automatik); kamus boleh dilaksanakan menggunakan weakref.WeakValueDictionary, antara mukanya Ia betul-betul sama seperti kamus biasa. Dengan cara ini, kita tidak lagi perlu mengekalkan objek rujukan yang lemah dengan diri kita sendiri, dan logik kod adalah lebih ringkas dan jelas:
import threading import weakref # 数据缓存 class Cacher: def __init__(self): self.pool = weakref.WeakValueDictionary() self.lock = threading.Lock() def get(self, key): with self.lock: data = self.pool.get(key) if data: return data self.pool[key] = data = Data(key) return data
modul weakref juga mempunyai banyak kelas alat dan fungsi alat yang berguna. Sila rujuk kepada rasmi dokumentasi untuk butiran khusus, yang tidak akan diterangkan di sini. - Bagaimana ia berfungsiJadi, apakah sebenarnya rujukan yang lemah, dan mengapa ia mempunyai kuasa ajaib seperti itu? Seterusnya, mari menanggalkan tudungnya dan lihat rupa sebenar!
>>> d = Data('fasionchan.com') # weakref.ref 是一个内置类型对象 >>> from weakref import ref >>> ref <class 'weakref'> # 调用weakref.ref类型对象,创建了一个弱引用实例对象 >>> r = ref(d) >>> r <weakref at 0x1008d5b80; to 'Data' at 0x100873d60>Selepas bab sebelumnya, kami sudah biasa membaca kod sumber objek terbina dalam Fail kod sumber yang berkaitan adalah seperti berikut: Sertakan/. Fail pengepala weakrefobject.h mengandungi struktur objek dan beberapa definisi makro; Mari kita lihat dahulu Struktur medan objek rujukan lemah ditakrifkan dalam baris 10-41 fail pengepala Include/weakrefobject.h:
typedef struct _PyWeakReference PyWeakReference; /* PyWeakReference is the base struct for the Python ReferenceType, ProxyType, * and CallableProxyType. */ #ifndef Py_LIMITED_API struct _PyWeakReference { PyObject_HEAD /* The object to which this is a weak reference, or Py_None if none. * Note that this is a stealth reference: wr_object's refcount is * not incremented to reflect this pointer. */ PyObject *wr_object; /* A callable to invoke when wr_object dies, or NULL if none. */ PyObject *wr_callback; /* A cache for wr_object's hash code. As usual for hashes, this is -1 * if the hash code isn't known yet. */ Py_hash_t hash; /* If wr_object is weakly referenced, wr_object has a doubly-linked NULL- * terminated list of weak references to it. These are the list pointers. * If wr_object goes away, wr_object is set to Py_None, and these pointers * have no meaning then. */ PyWeakReference *wr_prev; PyWeakReference *wr_next; }; #endifDapat dilihat bahawa PyWeakReference struktur ialah badan objek rujukan yang lemah. Ia ialah objek dengan panjang tetap. Selain pengepala tetap, terdapat 5 medan:
- wr_object, penunjuk objek, menunjuk kepada yang dirujuk objek, lemah Rujukan boleh mencari objek yang dirujuk berdasarkan medan ini, tetapi tiada rujukan akan dijanakan; dimusnahkan;
hash ,缓存被引用对象的哈希值;
wr_prev 和 wr_next 分别是前后向指针,用于将弱引用对象组织成双向链表;
结合代码中的注释,我们知道:
弱引用对象通过 wr_object 字段关联被引用的对象,如上图虚线箭头所示;
一个对象可以同时被多个弱引用对象关联,图中的 Data 实例对象被两个弱引用对象关联;
所有关联同一个对象的弱引用,被组织成一个双向链表,链表头保存在被引用对象中,如上图实线箭头所示;
当一个对象被销毁后,Python 将遍历它的弱引用链表,逐一处理:
将 wr_object 字段设为 None ,弱引用对象再被调用将返回 None ,调用者便知道对象已经被销毁了;
执行回调函数 wr_callback (如有);
由此可见,弱引用的工作原理其实就是设计模式中的 观察者模式( Observer )。当对象被销毁,它的所有弱引用对象都得到通知,并被妥善处理。
实现细节
掌握弱引用的基本原理,足以让我们将其用好。如果您对源码感兴趣,还可以再深入研究它的一些实现细节。
前面我们提到,对同一对象的所有弱引用,被组织成一个双向链表,链表头保存在对象中。由于能够创建弱引用的对象类型是多种多样的,很难由一个固定的结构体来表示。因此,Python 在类型对象中提供一个字段 tp_weaklistoffset ,记录弱引用链表头指针在实例对象中的偏移量。
由此一来,对于任意对象 o ,我们只需通过 ob_type 字段找到它的类型对象 t ,再根据 t 中的 tp_weaklistoffset 字段即可找到对象 o 的弱引用链表头。
Python 在 Include/objimpl.h 头文件中提供了两个宏定义:
/* Test if a type supports weak references */ #define PyType_SUPPORTS_WEAKREFS(t) ((t)->tp_weaklistoffset > 0) #define PyObject_GET_WEAKREFS_LISTPTR(o) \ ((PyObject **) (((char *) (o)) + Py_TYPE(o)->tp_weaklistoffset))
PyType_SUPPORTS_WEAKREFS 用于判断类型对象是否支持弱引用,仅当 tp_weaklistoffset 大于零才支持弱引用,内置对象 list 等都不支持弱引用;
PyObject_GET_WEAKREFS_LISTPTR 用于取出一个对象的弱引用链表头,它先通过 Py_TYPE 宏找到类型对象 t ,再找通过 tp_weaklistoffset 字段确定偏移量,最后与对象地址相加即可得到链表头字段的地址;
我们创建弱引用时,需要调用弱引用类型对象 weakref 并将被引用对象 d 作为参数传进去。弱引用类型对象 weakref 是所有弱引用实例对象的类型,是一个全局唯一的类型对象,定义在 Objects/weakrefobject.c 中,即:_PyWeakref_RefType(第 350 行)。
根据对象模型中学到的知识,Python 调用一个对象时,执行的是其类型对象中的 tp_call 函数。因此,调用弱引用类型对象 weakref 时,执行的是 weakref 的类型对象,也就是 type 的 tp_call 函数。tp_call 函数则回过头来调用 weakref 的 tp_new 和 tp_init 函数,其中 tp_new 为实例对象分配内存,而 tp_init 则负责初始化实例对象。
回到 Objects/weakrefobject.c 源文件,可以看到 PyWeakref_RefType 的 tp_new 字段被初始化成 *weakref___new_* (第 276 行)。该函数的主要处理逻辑如下:
解析参数,得到被引用的对象(第 282 行);
调用 PyType_SUPPORTS_WEAKREFS 宏判断被引用的对象是否支持弱引用,不支持就抛异常(第 286 行);
调用 GET_WEAKREFS_LISTPTR 行取出对象的弱引用链表头字段,为方便插入返回的是一个二级指针(第 294 行);
调用 get_basic_refs 取出链表最前那个 callback 为空 基础弱引用对象(如有,第 295 行);
如果 callback 为空,而且对象存在 callback 为空的基础弱引用,则复用该实例直接将其返回(第 296 行);
如果不能复用,调用 tp_alloc 函数分配内存、完成字段初始化,并插到对象的弱引用链表(第 309 行);
Jika panggilan balik kosong, masukkannya terus ke hadapan senarai terpaut untuk memudahkan penggunaan semula berikutnya (lihat titik 4); Jika panggilan balik tidak kosong, masukkannya selepas objek rujukan lemah asas (jika ada) untuk memastikan rujukan lemah asas berada di kepala senarai terpaut untuk akses mudah
- Apabila objek dikitar semula, fungsi tp_dealloc akan memanggil fungsi PyObject_ClearWeakRefs untuk membersihkan rujukannya yang lemah. Fungsi ini mengeluarkan senarai rujukan objek yang lemah, kemudian melintasinya satu demi satu, membersihkan medan wr_object dan melaksanakan fungsi panggil balik wr_callback (jika ada). Butiran khusus tidak akan dikembangkan Jika anda berminat, anda boleh menyemak kod sumber dalam Objects/weakrefobject.c, yang terletak di baris 880.
Atas ialah kandungan terperinci Bagaimana untuk menggunakan rujukan lemah dalam Python. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Untuk memaksimumkan kecekapan pembelajaran Python dalam masa yang terhad, anda boleh menggunakan modul, masa, dan modul Python. 1. Modul DateTime digunakan untuk merakam dan merancang masa pembelajaran. 2. Modul Masa membantu menetapkan kajian dan masa rehat. 3. Modul Jadual secara automatik mengatur tugas pembelajaran mingguan.

Python cemerlang dalam permainan dan pembangunan GUI. 1) Pembangunan permainan menggunakan pygame, menyediakan lukisan, audio dan fungsi lain, yang sesuai untuk membuat permainan 2D. 2) Pembangunan GUI boleh memilih tkinter atau pyqt. TKInter adalah mudah dan mudah digunakan, PYQT mempunyai fungsi yang kaya dan sesuai untuk pembangunan profesional.

Python sesuai untuk sains data, pembangunan web dan tugas automasi, manakala C sesuai untuk pengaturcaraan sistem, pembangunan permainan dan sistem tertanam. Python terkenal dengan kesederhanaan dan ekosistem yang kuat, manakala C dikenali dengan keupayaan kawalan dan keupayaan kawalan yang mendasari.

Anda boleh mempelajari konsep pengaturcaraan asas dan kemahiran Python dalam masa 2 jam. 1. Belajar Pembolehubah dan Jenis Data, 2.

Python digunakan secara meluas dalam bidang pembangunan web, sains data, pembelajaran mesin, automasi dan skrip. 1) Dalam pembangunan web, kerangka Django dan Flask memudahkan proses pembangunan. 2) Dalam bidang sains data dan pembelajaran mesin, numpy, panda, scikit-learn dan perpustakaan tensorflow memberikan sokongan yang kuat. 3) Dari segi automasi dan skrip, Python sesuai untuk tugas -tugas seperti ujian automatik dan pengurusan sistem.

Anda boleh mempelajari asas -asas Python dalam masa dua jam. 1. Belajar pembolehubah dan jenis data, 2. Struktur kawalan induk seperti jika pernyataan dan gelung, 3 memahami definisi dan penggunaan fungsi. Ini akan membantu anda mula menulis program python mudah.

Bagaimana Mengajar Asas Pengaturcaraan Pemula Komputer Dalam masa 10 jam? Sekiranya anda hanya mempunyai 10 jam untuk mengajar pemula komputer beberapa pengetahuan pengaturcaraan, apa yang akan anda pilih untuk mengajar ...

Cara mengelakkan dikesan semasa menggunakan fiddlerevery di mana untuk bacaan lelaki-dalam-pertengahan apabila anda menggunakan fiddlerevery di mana ...


Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

SublimeText3 Linux versi baharu
SublimeText3 Linux versi terkini

Dreamweaver Mac版
Alat pembangunan web visual

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

mPDF
mPDF ialah perpustakaan PHP yang boleh menjana fail PDF daripada HTML yang dikodkan UTF-8. Pengarang asal, Ian Back, menulis mPDF untuk mengeluarkan fail PDF "dengan cepat" dari tapak webnya dan mengendalikan bahasa yang berbeza. Ia lebih perlahan dan menghasilkan fail yang lebih besar apabila menggunakan fon Unicode daripada skrip asal seperti HTML2FPDF, tetapi menyokong gaya CSS dsb. dan mempunyai banyak peningkatan. Menyokong hampir semua bahasa, termasuk RTL (Arab dan Ibrani) dan CJK (Cina, Jepun dan Korea). Menyokong elemen peringkat blok bersarang (seperti P, DIV),

VSCode Windows 64-bit Muat Turun
Editor IDE percuma dan berkuasa yang dilancarkan oleh Microsoft