Python弱引用学習

10109 ワード

参照1.weakref – Garbage-collectable references to objects2.Python弱引用紹介
他の多くの高度な言語と同様に、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):
    print self.name = name
    
>>> o = Man('Jim')
>>> sys.getrefcount(o)   
2
>>> r = weakref.ref(o) #        
>>> sys.getrefcount(o) #          
2
>>> r
 #            
>>> o2 = r() #            
>>> o is o2
True
>>> sys.getrefcount(o)
3
>>> o = None
>>> o2 = None
>>> r #           ,     。
de>

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

class Man:
    def __init__(self, name):
        self.name = name
    def test(self):
        print "this is a test!"

def callback(self):
    print "callback"
    
o = Man('Jim')
p = weakref.proxy(o, callback)
p.test()
o=None
p.test()

callbackパラメータの役割はref関数のcallbackと同じです.Pythonが参照するオブジェクトを削除すると、エージェントを使用するとweakrefになります.ReferenceErrorエラー.
循環参照
前述したように,弱引用を用いることで,循環引用がごみに回収されないという問題を解決できる.まず、通常のループ参照を見て、簡単なGraphクラスを作成し、3つのGraphインスタンスを作成します.
# -*- coding:utf-8 -*-
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

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()


gc.set_debug(gc.DEBUG_LEAK)
print "Setting up the cycle"
print 
demo(Graph)
print
print "breaking the cycle and cleaning up garbage"
print
gc.garbage[0].set_next(None)
while gc.garbage:
    del gc.garbage[0]
print collect_and_show_garbage()

ここではpythonのgcライブラリのいくつかの方法を使用し、以下のように説明します.
  • gc.collect()収集ごみ
  • gc.garbage取得ごみリスト
  • gc.set_debug(gc.DBEUG_LEAK)は、見えないオブジェクト情報
  • を印刷する.
    実行結果は次のとおりです.
    Setting up the cycle
    
    Set up graph:
    one.set_next()
    two.set_next()
    three.set_next()
    
    Graph:
    one->two->three->one
    Collecting...
    unreachable objects:g 0
    garbage:[]
    
    After 2 references removed
    one->two->three->one
    Collecting...
    unreachable objects: 0
    garbage:[]
    
    removeing last reference
    Collecting...
    unreachable objects: 6
    garbage:[,
     ,
     ,
     {'name': 'one', 'other': },
     {'name': 'two', 'other': },
     {'name': 'three', 'other': }]
    
    breaking the cycle and cleaning up garbage
    
    one.set_next(None)
    (Deleting two)
    (Deleting three)
    (Deleting one)
    Collecting...
    unreachable objects: 0
    garbage:[]
    None
    [Finished in 0.4s]c: uncollectable 
    gc: uncollectable 
    gc: uncollectable 
    gc: uncollectable 
    gc: uncollectable 
    gc: uncollectable 

    結果から、Graphインスタンスのローカルリファレンスを削除しても、ゴミリストに存在し、回収できないことがわかります.次に、弱い参照を使用するWeakGraphクラスを作成します.
    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
    demo(WeakGraph)

    結果は次のとおりです.
    Setting up the cycle
    
    Set up graph:
    one.set_next()
    two.set_next()
    three.set_next()
    
    Graph:
    one->two->three
    Collecting...
    unreachable objects:Traceback (most recent call last):
      File "D:\apps\platform\demo\demo.py", line 87, in 
        gc.garbage[0].set_next(None)
    IndexError: list index out of range
     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:[]
    
    breaking the cycle and cleaning up garbage
    
    [Finished in 0.4s with exit code 1]

    上記のクラスでは、エージェントを使用して表示されているオブジェクトを示し、demo()がオブジェクトのすべてのローカル参照を削除するにつれてループが切断され、ゴミ回収期間でこれらのオブジェクトを削除できます.
    したがって,我々は実際の作業でループリファレンスを用いる必要がある場合は,できるだけ弱いインデックスを用いて実現する.
    キャッシュ・オブジェクトrefproxyは、単一のオブジェクトの弱い参照を維持するためにのみ使用できます.複数のオブジェクトの弱い参照を同時に作成したい場合はどうすればいいですか?この場合、WeakKeyDictionaryおよびWeakValueDictionaryを用いて実現することができる.WeakValueDictionaryクラスは、その名の通り、本質的には辞書タイプですが、その値タイプは弱い参照です.これらの値で参照されたオブジェクトが他の弱い参照オブジェクトで参照されなくなった場合、これらの参照されたオブジェクトはゴミ回収器で回収できます.次の例では、従来の辞書とWeakValueDictionaryの違いを説明する.
    # -*- coding:utf-8 -*-
    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 demo(weakref.WeakValueDictionary)

    結果は次のとおりです.
    cache type: 
    all_refs={'Green': , 'Jim': , 'Tom': }
    
    before, cache contains: ['Jim', 'Green', 'Tom']
    Jim = 
    Green = 
    Tom = 
    
    cleanup
    
    after, cache contains: ['Jim', 'Green', 'Tom']
    Jim = 
    Green = 
    Tom = 
    demo returning
    deleting 
    deleting 
    deleting 
    
    cache type: weakref.WeakValueDictionary
    all_refs={'Green': , 'Jim': , 'Tom': }
    
    before, cache contains: ['Jim', 'Green', 'Tom']
    Jim = 
    Green = 
    Tom = 
    
    cleanup
    deleting 
    deleting 
    
    after, cache contains: []
    demo returning
    
    [Finished in 0.3s]