suchen
HeimBackend-EntwicklungPython-TutorialSo verwenden Sie schwache Referenzen in Python

Hintergrund

Bevor wir mit der Diskussion schwacher Referenzen (weakref) beginnen, werfen wir zunächst einen Blick darauf, was eine schwache Referenz ist. Was genau macht es?

Angenommen, wir haben ein Multithread-Programm, das Anwendungsdaten gleichzeitig verarbeitet:

# 占用大量资源,创建销毁成本很高\
class Data:\
    def __init__(self, key):\
        pass

Anwendungsdaten Daten werden durch einen Schlüssel eindeutig identifiziert, und mehrere Threads können gleichzeitig auf dieselben Daten zugreifen. Da Daten viele Systemressourcen erfordern, sind die Kosten für die Erstellung und Nutzung hoch. Wir hoffen, dass Data nur eine Kopie im Programm verwaltet und diese nicht wiederholt erstellen möchte, selbst wenn mehrere Threads gleichzeitig darauf zugreifen.

Zu diesem Zweck versuchen wir, einen Caching-Middleware-Cacher zu entwerfen:

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 verwendet intern ein dict-Objekt, um die erstellte Datenkopie zwischenzuspeichern, und stellt eine Get-Methode zum Abrufen von Anwendungsdaten bereit. Wenn die Get-Methode Daten abruft, überprüft sie zunächst das Cache-Wörterbuch. Wenn die Daten bereits vorhanden sind, werden sie direkt zurückgegeben. Wenn die Daten nicht vorhanden sind, wird eines erstellt und im Wörterbuch gespeichert. Daher werden die Daten nach der ersten Erstellung in das Cache-Wörterbuch eingegeben. Wenn später andere Threads gleichzeitig darauf zugreifen, wird dieselbe Kopie im Cache verwendet.

Fühlt sich sehr gut an! Aber der Wermutstropfen ist: Bei Cacher besteht die Gefahr eines Ressourcenverlusts!

Denn sobald Daten erstellt wurden, werden sie im Cache-Wörterbuch gespeichert und niemals veröffentlicht! Mit anderen Worten: Die Ressourcen des Programms, wie zum Beispiel der Speicher, werden weiter wachsen und möglicherweise explodieren. Daher hoffen wir, dass ein Datenelement automatisch freigegeben werden kann, nachdem alle Threads nicht mehr darauf zugreifen.

Wir können die Anzahl der Datenreferenzen im Cacher verwalten und die Get-Methode akkumuliert diese Anzahl automatisch. Gleichzeitig wird eine neue Remove-Methode zum Freigeben von Daten bereitgestellt. Sie verringert zunächst die Anzahl der Referenzen und löscht die Daten aus dem Cache-Feld, wenn die Anzahl der Referenzen auf Null sinkt.

Der Thread ruft die Get-Methode auf, um die Daten abzurufen. Nachdem die Daten aufgebraucht sind, muss die Remove-Methode aufgerufen werden, um sie freizugeben. Cacher entspricht der Implementierung der Referenzzählmethode selbst, was zu mühsam ist! Verfügt Python nicht über einen integrierten Garbage-Collection-Mechanismus? Warum muss die Anwendung es selbst implementieren?

Der Hauptproblempunkt des Konflikts liegt im Cache-Wörterbuch von Cacher: Als Middleware verwendet es selbst keine Datenobjekte und sollte daher theoretisch keine Verweise auf Daten haben. Gibt es also eine schwarze Technologie, die das Zielobjekt finden kann, ohne eine Referenz zu generieren? Wir wissen, dass Aufgaben Referenzen generieren!

Typische Verwendung

Zu diesem Zeitpunkt hat schwache Referenz (schwachref) einen großen Auftritt! Eine schwache Referenz ist ein spezielles Objekt, das dem Zielobjekt zugeordnet werden kann, ohne dass eine Referenz generiert wird.

# 创建一个数据
>>> 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()

So verwenden Sie schwache Referenzen in Python

Auf diese Weise müssen wir nur das Cacher-Cache-Wörterbuch ändern, um schwache Referenzen zu speichern, und das Problem wird gelöst!

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

Da das Cache-Wörterbuch nur schwache Referenzen auf das Datenobjekt speichert, hat der Cacher keinen Einfluss auf die Referenzanzahl des Datenobjekts. Wenn alle Threads die Daten nicht mehr verwenden, sinkt der Referenzzähler auf Null und wird freigegeben.

Tatsächlich werden Wörterbücher häufig zum Zwischenspeichern von Datenobjekten verwendet. Aus diesem Grund stellt das Weakref-Modul auch zwei Wörterbuchobjekte bereit, die nur schwache Referenzen speichern:

  • weakref.WeakKeyDictionary, der Schlüssel speichert nur die Zuordnungsklasse von schwachen Referenzen (sobald der Schlüssel keine starke Referenz mehr hat, verschwindet der Schlüssel-Wert-Paar-Eintrag automatisch);

  • Daher kann unser Daten-Cache-Wörterbuch mit schwachref.WeakValueDictionary implementiert werden, und seine Schnittstelle ist genau die gleiche wie ein gewöhnliches Wörterbuch. Auf diese Weise müssen wir schwache Referenzobjekte nicht mehr selbst verwalten und die Codelogik ist prägnanter und klarer: Das

    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
  • weakref-Modul verfügt auch über viele nützliche Toolklassen und Toolfunktionen. Weitere Informationen finden Sie in der offiziellen Dokumentation Details und werden hier nicht wiederholt.

Wie es funktioniert

Wer genau ist also eine schwache Referenz und warum hat sie so magische Kraft? Als nächstes nehmen wir seinen Schleier ab und sehen sein wahres Aussehen!

>>> d = Data(&#39;fasionchan.com&#39;)

# weakref.ref 是一个内置类型对象
>>> from weakref import ref
>>> ref
<class &#39;weakref&#39;>

# 调用weakref.ref类型对象,创建了一个弱引用实例对象
>>> r = ref(d)
>>> r
<weakref at 0x1008d5b80; to &#39;Data&#39; at 0x100873d60>

Nach den vorherigen Kapiteln sind wir bereits mit dem Lesen des Quellcodes integrierter Objekte vertraut. Die relevanten Quellcodedateien lauten wie folgt:

Include/weakrefobject.h Die Header-Datei enthält die Objektstruktur und einige Makrodefinitionen;

  • Objects/weakrefobject Die .c-Quelldatei enthält schwache Referenztypobjekte und ihre Methodendefinitionen.

  • Schauen wir uns zunächst die Feldstruktur des schwachen Referenzobjekts an, die in Zeile 10 definiert ist -41 in der Header-Datei 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&#39;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&#39;s hash code.  As usual for hashes, this is -1
         * if the hash code isn&#39;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;
    };
    #endif
  • Es ​​ist ersichtlich, dass die PyWeakReference-Struktur der physische Körper des schwachen Referenzobjekts ist. Es handelt sich um ein Objekt mit fester Länge. Zusätzlich zum festen Header gibt es 5 Felder:

So verwenden Sie schwache Referenzen in Pythonwr_object, Objektzeiger, der auf das referenzierte Objekt zeigt, schwache Referenz kann das referenzierte Objekt basierend auf diesem Feld finden, wird aber nicht generiert Referenz;

  • wr_callback, zeigt auf ein aufrufbares Objekt, das aufgerufen wird, wenn das referenzierte Objekt zerstört wird

  • hash ,缓存被引用对象的哈希值;

  • wr_prev 和 wr_next 分别是前后向指针,用于将弱引用对象组织成双向链表;

结合代码中的注释,我们知道:

So verwenden Sie schwache Referenzen in Python

  • 弱引用对象通过 wr_object 字段关联被引用的对象,如上图虚线箭头所示;

  • 一个对象可以同时被多个弱引用对象关联,图中的 Data 实例对象被两个弱引用对象关联;

  • 所有关联同一个对象的弱引用,被组织成一个双向链表,链表头保存在被引用对象中,如上图实线箭头所示;

  • 当一个对象被销毁后,Python 将遍历它的弱引用链表,逐一处理:


    • 将 wr_object 字段设为 None ,弱引用对象再被调用将返回 None ,调用者便知道对象已经被销毁了;

    • 执行回调函数 wr_callback (如有);

由此可见,弱引用的工作原理其实就是设计模式中的 观察者模式( Observer )。当对象被销毁,它的所有弱引用对象都得到通知,并被妥善处理。

实现细节

掌握弱引用的基本原理,足以让我们将其用好。如果您对源码感兴趣,还可以再深入研究它的一些实现细节。

前面我们提到,对同一对象的所有弱引用,被组织成一个双向链表,链表头保存在对象中。由于能够创建弱引用的对象类型是多种多样的,很难由一个固定的结构体来表示。因此,Python 在类型对象中提供一个字段 tp_weaklistoffset ,记录弱引用链表头指针在实例对象中的偏移量。

So verwenden Sie schwache Referenzen in Python

由此一来,对于任意对象 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 行)。

So verwenden Sie schwache Referenzen in Python

根据对象模型中学到的知识,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 行);


    • Wenn der Rückruf leer ist, fügen Sie ihn direkt am Anfang der verknüpften Liste ein, um die spätere Wiederverwendung zu erleichtern (siehe Punkt 4); # 🎜🎜#

      Wenn der Rückruf nicht leer ist, fügen Sie ihn nach dem grundlegenden schwachen Referenzobjekt (falls vorhanden) ein, um sicherzustellen, dass sich die grundlegende schwache Referenz für einen einfachen Zugriff an der Spitze der verknüpften Liste befindet.
    • # 🎜🎜#

      Wenn ein Objekt recycelt wird, ruft die Funktion tp_dealloc die Funktion PyObject_ClearWeakRefs auf, um seine schwachen Referenzen zu bereinigen. Diese Funktion entnimmt die schwache Referenzliste des Objekts, durchläuft sie dann einzeln, bereinigt das Feld wr_object und führt die Rückruffunktion wr_callback aus (falls vorhanden). Die spezifischen Details werden nicht erweitert. Wenn Sie interessiert sind, können Sie den Quellcode in Objects/weakrefobject.c in Zeile 880 überprüfen.

    • Okay, nachdem wir diesen Abschnitt studiert haben, beherrschen wir das Wissen über schwache Referenzen gründlich. Schwache Referenzen können das Zielobjekt verwalten, ohne einen Referenzzähler zu generieren, und werden häufig in Frameworks und Middleware verwendet. Schwache Referenzen sehen magisch aus, aber tatsächlich ist das Gestaltungsprinzip ein sehr einfaches Beobachtermuster. Nachdem das schwache Referenzobjekt erstellt wurde, wird es in eine verknüpfte Liste eingefügt, die vom Zielobjekt verwaltet wird, und das Zerstörungsereignis des Objekts wird beobachtet (abonniert).

Das obige ist der detaillierte Inhalt vonSo verwenden Sie schwache Referenzen in Python. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme
Dieser Artikel ist reproduziert unter:亿速云. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen
Der Hauptzweck von Python: Flexibilität und BenutzerfreundlichkeitDer Hauptzweck von Python: Flexibilität und BenutzerfreundlichkeitApr 17, 2025 am 12:14 AM

Die Flexibilität von Python spiegelt sich in Multi-Paradigm-Unterstützung und dynamischen Typsystemen wider, während eine einfache Syntax und eine reichhaltige Standardbibliothek stammt. 1. Flexibilität: Unterstützt objektorientierte, funktionale und prozedurale Programmierung und dynamische Typsysteme verbessern die Entwicklungseffizienz. 2. Benutzerfreundlichkeit: Die Grammatik liegt nahe an der natürlichen Sprache, die Standardbibliothek deckt eine breite Palette von Funktionen ab und vereinfacht den Entwicklungsprozess.

Python: Die Kraft der vielseitigen ProgrammierungPython: Die Kraft der vielseitigen ProgrammierungApr 17, 2025 am 12:09 AM

Python ist für seine Einfachheit und Kraft sehr beliebt, geeignet für alle Anforderungen von Anfängern bis hin zu fortgeschrittenen Entwicklern. Seine Vielseitigkeit spiegelt sich in: 1) leicht zu erlernen und benutzten, einfachen Syntax; 2) Reiche Bibliotheken und Frameworks wie Numpy, Pandas usw.; 3) plattformübergreifende Unterstützung, die auf einer Vielzahl von Betriebssystemen betrieben werden kann; 4) Geeignet für Skript- und Automatisierungsaufgaben zur Verbesserung der Arbeitseffizienz.

Python in 2 Stunden am Tag lernen: Ein praktischer LeitfadenPython in 2 Stunden am Tag lernen: Ein praktischer LeitfadenApr 17, 2025 am 12:05 AM

Ja, lernen Sie Python in zwei Stunden am Tag. 1. Entwickeln Sie einen angemessenen Studienplan, 2. Wählen Sie die richtigen Lernressourcen aus, 3. Konsolidieren Sie das durch die Praxis erlernte Wissen. Diese Schritte können Ihnen helfen, Python in kurzer Zeit zu meistern.

Python gegen C: Vor- und Nachteile für EntwicklerPython gegen C: Vor- und Nachteile für EntwicklerApr 17, 2025 am 12:04 AM

Python eignet sich für eine schnelle Entwicklung und Datenverarbeitung, während C für hohe Leistung und zugrunde liegende Kontrolle geeignet ist. 1) Python ist einfach zu bedienen, mit prägnanter Syntax, und eignet sich für Datenwissenschaft und Webentwicklung. 2) C hat eine hohe Leistung und eine genaue Kontrolle und wird häufig bei der Programmierung von Spielen und Systemen verwendet.

Python: zeitliches Engagement und LerntempoPython: zeitliches Engagement und LerntempoApr 17, 2025 am 12:03 AM

Die Zeit, die zum Erlernen von Python erforderlich ist, variiert von Person zu Person, hauptsächlich von früheren Programmiererfahrungen, Lernmotivation, Lernressourcen und -methoden und Lernrhythmus. Setzen Sie realistische Lernziele und lernen Sie durch praktische Projekte am besten.

Python: Automatisierung, Skript- und AufgabenverwaltungPython: Automatisierung, Skript- und AufgabenverwaltungApr 16, 2025 am 12:14 AM

Python zeichnet sich in Automatisierung, Skript und Aufgabenverwaltung aus. 1) Automatisierung: Die Sicherungssicherung wird durch Standardbibliotheken wie OS und Shutil realisiert. 2) Skriptschreiben: Verwenden Sie die PSUTIL -Bibliothek, um die Systemressourcen zu überwachen. 3) Aufgabenverwaltung: Verwenden Sie die Zeitplanbibliothek, um Aufgaben zu planen. Die Benutzerfreundlichkeit von Python und die Unterstützung der reichhaltigen Bibliothek machen es zum bevorzugten Werkzeug in diesen Bereichen.

Python und Zeit: Machen Sie das Beste aus Ihrer StudienzeitPython und Zeit: Machen Sie das Beste aus Ihrer StudienzeitApr 14, 2025 am 12:02 AM

Um die Effizienz des Lernens von Python in einer begrenzten Zeit zu maximieren, können Sie Pythons DateTime-, Zeit- und Zeitplanmodule verwenden. 1. Das DateTime -Modul wird verwendet, um die Lernzeit aufzuzeichnen und zu planen. 2. Das Zeitmodul hilft, die Studie zu setzen und Zeit zu ruhen. 3. Das Zeitplanmodul arrangiert automatisch wöchentliche Lernaufgaben.

Python: Spiele, GUIs und mehrPython: Spiele, GUIs und mehrApr 13, 2025 am 12:14 AM

Python zeichnet sich in Gaming und GUI -Entwicklung aus. 1) Spielentwicklung verwendet Pygame, die Zeichnungen, Audio- und andere Funktionen bereitstellt, die für die Erstellung von 2D -Spielen geeignet sind. 2) Die GUI -Entwicklung kann Tkinter oder Pyqt auswählen. Tkinter ist einfach und einfach zu bedienen. PYQT hat reichhaltige Funktionen und ist für die berufliche Entwicklung geeignet.

See all articles

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Chat -Befehle und wie man sie benutzt
1 Monate vorBy尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

MinGW – Minimalistisches GNU für Windows

MinGW – Minimalistisches GNU für Windows

Dieses Projekt wird derzeit auf osdn.net/projects/mingw migriert. Sie können uns dort weiterhin folgen. MinGW: Eine native Windows-Portierung der GNU Compiler Collection (GCC), frei verteilbare Importbibliotheken und Header-Dateien zum Erstellen nativer Windows-Anwendungen, einschließlich Erweiterungen der MSVC-Laufzeit zur Unterstützung der C99-Funktionalität. Die gesamte MinGW-Software kann auf 64-Bit-Windows-Plattformen ausgeführt werden.

EditPlus chinesische Crack-Version

EditPlus chinesische Crack-Version

Geringe Größe, Syntaxhervorhebung, unterstützt keine Code-Eingabeaufforderungsfunktion

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

SublimeText3 Linux neue Version

SublimeText3 Linux neue Version

SublimeText3 Linux neueste Version

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung