python弱参照

60860 ワード

文書ディレクトリ
  • 弱参照
  • を作成
  • プロキシオブジェクトを作成する
  • 循環参照
  • キャッシュオブジェクト(WeakValue Dictionary)
  • ゴミ回収は他の多くの高度な言語と同様に、Pythonはゴミ回収器を使用して、使用されなくなったオブジェクトを自動的に廃棄します.各オブジェクトには参照カウントがあり、この参照カウントが0の場合、Pythonはこのオブジェクトを安全に破棄することができます.
    参照カウント参照カウントは、指定されたオブジェクトの参照数を記録し、参照数がゼロの場合に収集します.一度に1つのオブジェクトしか回収できないため、リファレンスカウントはループリファレンスのオブジェクトを回収できません.
    循環参照問題相互参照の一連のオブジェクトは、他のオブジェクトに直接参照されず、アクセスできない場合、永続的に生存します.1つのアプリケーションがこのようなアクセス不可能なオブジェクトグループを持続的に生成すると、メモリ漏洩が発生します.
    弱いリファレンスの存在価値オブジェクトグループ内で弱いリファレンス(すなわち、リファレンスカウントでカウントされないリファレンス)を使用すると、リファレンスリングが回避される場合があるため、弱いリファレンスはループリファレンスの問題を解決するために使用できます. コンピュータプログラム設計において、弱い参照とは、強い参照とは対照的に、その参照の対象がゴミ回収器によって回収されないことを確保できない参照を指す.1つのオブジェクトが弱い参照でのみ参照される場合、いつでも回収される可能性があります.弱いリファレンスの主な役割は、ループリファレンスを減らし、メモリ内の不要なオブジェクトの数を減らすことです.
    弱参照の作成weakrefモジュールを使用すると、オブジェクトへの弱参照を作成できます.Pythonは、オブジェクトの参照数が0またはオブジェクトのみの弱参照が存在する場合にこのオブジェクトを回収します.
    弱い参照の作成
    Weakrefモジュールのref(obj[,callback])を呼び出すことで弱参照を作成できます.objは弱参照したいオブジェクトです.callbackはオプションの関数で、参照がないためPythonがこのオブジェクトを破棄しようとしたときに呼び出されます.コールバック関数callbackでは、単一のパラメータ(弱い参照オブジェクト)が必要です.
    オブジェクトの弱い参照があれば、弱い参照を呼び出すことで、弱い参照されたオブジェクトを取得できます.
    >>> import sys
    >>> import weakref
    >>> class Man():
    ...     def __init__(self, name):
    ...             self.name = name
    ...
    >>> man0 = Man('zhe')    #         count = 1 
    >>> sys.getrefcount(man0)
    2
    >>> r = weakref.ref(man0)   #          count = 1  
    >>> sys.getrefcount(man0)
    2
    >>> r   #            
    <weakref at 0x0000026AF3974688; to 'Man' at 0x0000026AF398C710>
    >>> man1 = r()
    >>> man0 is man1
    True
    >>> sys.getrefcount(man0)
    3
    >>> man0 = None
    >>> man1 = None
    >>> r   #           ,     。
    <weakref at 0x0000026AF3974688; dead>
    

    注意:
  • の上のコードでは、sysパッケージのgetrefcount()を使用して、オブジェクトの参照カウントを表示します.パラメータとして参照を使用してgetrefcount()に渡すと、パラメータは実際に一時的な参照を作成します.したがって,getrefcount()で得られた結果は,期待よりも多くなる.
  • このオブジェクトに対する他の参照がなくなると、Pythonがこのオブジェクトを破棄したため、弱い参照を呼び出すとNoneが返されます.注:ほとんどのオブジェクトは弱引用でアクセスできません.
  • weakrefモジュールのgetweakrefcount(obj)およびgetweakrefs(obj)は、それぞれ弱い参照数と、与えられたオブジェクトに関する参照リストを返します.
  • 弱参照は、オブジェクト(リソースがかかるオブジェクト)のキャッシュを作成するのに役立ちます.
  • 弱参照の使用は、元のオブジェクトにアクセスするためにweakref()の形式である.実際には、プロキシオブジェクトを作成して元のオブジェクトにアクセスできます.

  • プロキシオブジェクトの作成
    プロキシオブジェクトは弱いリファレンスオブジェクトであり、リファレンスされたオブジェクトのように動作します.これにより、まず弱いリファレンスを呼び出して背後のオブジェクトにアクセスする必要がなくなります.Weakrefモジュールのproxy(obj[,callback])関数によりプロキシオブジェクトを作成します.プロキシオブジェクトを使用するのは、オブジェクト自体を使用するのと同じです.弱いリファレンスを作成するよりも呼び出しの方が完全に同じです.
    >>> import sys
    >>> import weakref
    >>> class Man():
    ...     def __init__(self, name):
    ...             self.name = name
    ...
    >>> def callback_ref(self):
    ...     print (self)
    ...     print ("callback_ref")
    ...
    >>> def callback_proxy(self):
    ...     print (self)
    ...     print ("callback_proxy")
    ...
    >>> man = Man('zhe')   #      +1
    >>> sys.getrefcount(man)
    2
    >>> man_ref = weakref.ref(man, callback_ref)  #           
    >>> sys.getrefcount(man)
    2
    >>> man_ref   #      
    <weakref at 0x0000019A63664638; to 'Man' at 0x0000019A6367C668>
    >>> man_ref.name   #            
    Traceback (most recent call last):
      File "", line 1, in <module>
    AttributeError: 'weakref' object has no attribute 'name'
    >>> man_ref().name    #             
    'zhe'
    >>> man_proxy = weakref.proxy(man, callback_proxy)   #            
    >>> sys.getrefcount(man)
    2
    >>> man_proxy  #     
    <weakproxy at 0x0000019A634D6BD8 to Man at 0x0000019A6367C668>
    >>> man_proxy.name   #           
    'zhe'
    >>> del man   #  
    Exception ignored in: <function callback_proxy at 0x0000019A636807B8>
    Traceback (most recent call last):
      File "", line 2, in callback_proxy
    ReferenceError: weakly-referenced object no longer exists
    <weakref at 0x0000019A63664638; dead>
    callback_ref
    

    注意:
  • 元のオブジェクトを削除し、そのエージェントへのアクセスを行うとweakrefが発生する.ReferenceErrorエラー.
  • 元のオブジェクトを削除し、弱い参照へのアクセスを行います.

  • 循環参照
    前述したように,弱引用を用いることで,循環引用がごみに回収されないという問題を解決できる.まず、通常のループ参照を見て、簡単なGraphクラスを作成し、3つのGraphインスタンスを作成します.
    import gc
    from pprint import pprint
    import os
    os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
    
    class Graph(object):
        def __init__(self, name):
            self.name = name
            self.other = None
    
        def set_next(self, other):
            print ("%s.set_next(%r)" % (self.name, other))
            self.other = other
    
        def all_nodes(self):
            yield self
            n = self.other
            while n and n.name !=self.name:
                yield n
                n = n.other
            if n is self:
                yield n
            return
    
        def __str__(self):
            return "->".join(n.name for n in self.all_nodes())
    
        def __repr__(self):
            return "" % (self.__class__.__name__, id(self), self.name)
    
        def __del__(self):
            print ("(Deleting %s)" % self.name)
    
    def collect_and_show_garbage():
        print ("Collecting...")
        n = gc.collect()
        print ("unreachable objects:", n)
        print ("garbage:",)
        pprint(gc.garbage)
    
    
    def demo(graph_factory):
        print ("Set up graph:")
        one = graph_factory("one")
        two = graph_factory("two")
        three = graph_factory("three")
        one.set_next(two)
        two.set_next(three)
        three.set_next(one)
    
        print()
        print ("Graph:")
        print (str(one))
        collect_and_show_garbage()
    
        print()
        three = None
        two = None
        print ("After 2 references removed")
        print (str(one))
        collect_and_show_garbage()
    
        print()
        print ("removeing last reference")
        one = None
        collect_and_show_garbage()
        print()
    
    
    gc.set_debug(gc.DEBUG_LEAK)
    print ("Setting up the cycle")
    print ()
    demo(Graph)
    
    gc.garbage[0].set_next(None)
    while gc.garbage:
        del gc.garbage[0]
    print (collect_and_show_garbage())
    

    結果:
    Setting up the cycle
    
    Set up graph:
    one.set_next(<Graph at 0x14496cdecc0 name=two>)
    two.set_next(<Graph at 0x14496cdeeb8 name=three>)
    three.set_next(<Graph at 0x14496cdee48 name=one>)
    
    Graph:
    one->two->three->one
    Collecting...
    unreachable objects: 0
    garbage:
    []
    
    After 2 references removed
    one->two->three->one
    Collecting...
    unreachable objects: 0
    garbage:
    []
    
    removeing last reference
    Collecting...
    (Deleting one)
    (Deleting two)
    (Deleting three)
    unreachable objects: 6
    garbage:
    [<Graph at 0x14496cdee48 name=one>,
     {'name': 'one', 'other': <Graph at 0x14496cdecc0 name=two>},
     <Graph at 0x14496cdecc0 name=two>,
     {'name': 'two', 'other': <Graph at 0x14496cdeeb8 name=three>},
     <Graph at 0x14496cdeeb8 name=three>,
     {'name': 'three', 'other': <Graph at 0x14496cdee48 name=one>}]
    one.set_next(None)
    
    Collecting...
    unreachable objects: 0
    garbage:
    []
    None
    

    説明:
  • 結果から、Graphインスタンスのローカル参照を削除しても、ゴミリストに存在し、回収できないことがわかります.
  • は、次に弱い参照を使用するWeakGraphクラスを作成し、ループ参照を防止します.
  • import weakref
    import gc
    from pprint import pprint
    
    class Graph(object):
        def __init__(self, name):
            self.name = name
            self.other = None
    
        def set_next(self, other):
            print ("%s.set_next(%r)" % (self.name, other))
            self.other = other
    
        def all_nodes(self):
            yield self
            n = self.other
            while n and n.name !=self.name:
                yield n
                n = n.other
            if n is self:
                yield n
            return
    
        def __str__(self):
            return "->".join(n.name for n in self.all_nodes())
    
        def __repr__(self):
            return "" % (self.__class__.__name__, id(self), self.name)
    
        def __del__(self):
            print ("(Deleting %s)" % self.name)
    
    class WeakGraph(Graph):
        def set_next(self, other):
            if other is not None:
                if self in other.all_nodes():
                    other = weakref.proxy(other)
            super(WeakGraph, self).set_next(other)
            return
    
    
    def collect_and_show_garbage():
        print ("Collecting...")
        n = gc.collect()
        print ("unreachable objects:", n)
        print ("garbage:",)
        pprint(gc.garbage)
        print ()
    
    
    def demo(graph_factory):
        print ("Set up graph:")
        one = graph_factory("one")
        two = graph_factory("two")
        three = graph_factory("three")
        one.set_next(two)
        two.set_next(three)
        three.set_next(one)
    
        print()
        print ("Graph:")
        print (str(one))
        collect_and_show_garbage()
    
        print()
        three = None
        two = None
        print ("After 2 references removed")
        print (str(one))
        collect_and_show_garbage()
    
        print()
        print ("removeing last reference")
        one = None
        collect_and_show_garbage()
    
    demo(WeakGraph)
    

    結果:
    Set up graph:
    one.set_next(<WeakGraph at 0x29efa09ecc0 name=two>)
    two.set_next(<WeakGraph at 0x29efa09ef28 name=three>)
    three.set_next(<weakproxy at 0x0000029EFA0D6458 to WeakGraph at 0x0000029EFA09EEB8>)
    
    Graph:
    one->two->three
    Collecting...
    unreachable objects: 0
    garbage:
    []
    
    
    After 2 references removed
    one->two->three
    Collecting...
    unreachable objects: 0
    garbage:
    []
    
    
    removeing last reference
    (Deleting one)
    (Deleting two)
    (Deleting three)
    Collecting...
    unreachable objects: 0
    garbage:
    []
    

    上記のクラスでは、エージェントを使用して表示されているオブジェクトを示し、demo()がオブジェクトのすべてのローカル参照を削除するにつれてループが切断され、ゴミ回収期間でこれらのオブジェクトを削除できます.
    したがって,我々は実際の作業でループリファレンスを用いる必要がある場合は,できるだけ弱いインデックスを用いて実現する.
    キャッシュ・オブジェクト(WeakValue Dictionary)
    refとproxyは、単一のオブジェクトの弱い参照のみを維持できます.複数のオブジェクトの弱い参照を同時に作成したい場合はどうすればいいですか?この場合,WeakKeyDictionaryとWeakValueDictionaryを用いて実現できる.
    WeakValueDictionaryクラスは、その名の通り本質的に辞書タイプですが、その値タイプは弱い参照です.これらの値で参照されたオブジェクトが他の弱い参照オブジェクトで参照されなくなった場合、これらの参照されたオブジェクトはゴミ回収器で回収できます.
    次の例では、通常の辞書とWeakValueDictionaryの違いを説明します.
    import weakref
    import gc
    from pprint import pprint
    
    # gc.set_debug(gc.DEBUG_LEAK)
    
    
    class Man(object):
        def __init__(self, name):
            self.name = name
    
        def __repr__(self):
            return '' % self.name
    
        def __del__(self):
            print ("deleting %s" % self)
    
    
    def demo(cache_factory):
        all_refs = {}
        print ("cache type:", cache_factory)
        cache = cache_factory()
        for name in ["Jim", 'Tom', 'Green']:
            man = Man(name)
            cache[name] = man
            all_refs[name] = man
            del man
        print ("all_refs=",)
        pprint(all_refs)
        print()
        print ("before, cache contains:", cache.keys())
        for name, value in cache.items():
            print ("%s = %s" % (name, value))
        print ("
    cleanup"
    ) del all_refs gc.collect() print() print ("after, cache contains:", cache.keys()) for name, value in cache.items(): print ("%s = %s" % (name, value)) print ("demo returning") return demo(dict) print () print("====The end of dict process and the begin of weakref.WeakValueDictionary process:====") demo(weakref.WeakValueDictionary)

    結果:
    cache type: <class 'dict'>
    all_refs=
    {'Green': <Man name=Green>, 'Jim': <Man name=Jim>, 'Tom': <Man name=Tom>}
    
    before, cache contains: dict_keys(['Jim', 'Tom', 'Green'])
    Jim = <Man name=Jim>
    Tom = <Man name=Tom>
    Green = <Man name=Green>
    
    cleanup
    
    after, cache contains: dict_keys(['Jim', 'Tom', 'Green'])
    Jim = <Man name=Jim>
    Tom = <Man name=Tom>
    Green = <Man name=Green>
    demo returning
    deleting <Man name=Jim>
    deleting <Man name=Tom>
    deleting <Man name=Green>
    
    ====The end of dict process and the begin of weakref.WeakValueDictionary process:====
    cache type: <class 'weakref.WeakValueDictionary'>
    all_refs=
    {'Green': <Man name=Green>, 'Jim': <Man name=Jim>, 'Tom': <Man name=Tom>}
    
    before, cache contains: <generator object WeakValueDictionary.keys at 0x0000026245B74930>
    Jim = <Man name=Jim>
    Tom = <Man name=Tom>
    Green = <Man name=Green>
    
    cleanup
    deleting <Man name=Jim>
    deleting <Man name=Tom>
    
    after, cache contains: <generator object WeakValueDictionary.keys at 0x0000026245B74930>
    Green = <Man name=Green>
    demo returning
    deleting <Man name=Green>
    

    注意:
  • dictを使用すると、allがすべて削除されたことがわかります.refs参照の場合、dictには依然として強い参照が残っています.
  • WeakValueDictionaryを使用する場合、all_をすべて削除します.refs参照の場合、WeakValueDictionary containerには空、つまりWeakValueDictionaryが存在するのは弱参照です.