IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Diskuze: Rušení referencí

Aktivity
Avatar
hanpari
Člen
Avatar
hanpari:14.6.2014 6:58

Ahoj všichni,
už delší dobu řeším tento problém. Pokud mám víc odkazů na objekt, potřeboval bych tento objekt zrušit. Ale nevím jak. Snad to vysvětlí tenhle kód, snažil jsem se ho napsat co nejnázorněji.
Kdyby někdo věděl, jak na to, byl bych velice rád.

class Reference():
    _count = 0

    def __init__(self):
        print("Začátek objektu.")
        Reference._count += 1
        self.number = Reference._count

    def __del__(self):
        print("Konec objektu.")
        Reference._count -= 1

    def __repr__(self):
        return "Ref: {}".format(self.number)


    @staticmethod
    def get_count():
        return Reference._count


if __name__ == "__main__":
    r1 = Reference()
    r2 = Reference()
    r3 = Reference()
    assert Reference.get_count() == 3, "Počet instancí 3, což je v pořádku"
    del(r3)
    assert Reference.get_count() == 2, "Počet instancí 2, také dobře."
    #Teď si vytvořím list zbylých referencí
    seznam = [r1, r2]
    #...a pokusím se smazat r2,
    # ve skutečnosti se ale nevolá funkce __del__ (jak bych předpokládal!)
    del(r2)
    # Ve skutečnosti jsem jen zrušil název proměnné
    # proto zůstal jeden odkaz na bývalou r2  v seznamu
    print(seznam)
    #Doufal jsem ve vypsání [Ref: 1, None]  :)
    #Ale __del__ prostě neproběhl
    assert Reference.get_count() == 1, "Já bych chtěl, prosím, jen r1 :)"

A tohle je nečekaný výstup :)


Začátek objektu.
Začátek objektu.
Začátek objektu.
Konec objektu.
[Ref: 1, Ref: 2]
Traceback (most recent call last):
  File "C:/Users/Hanpari/SkyDrive/Dokumenty/GitHub/my_check/graphic/g2d.py", line 49, in <module>
    assert Reference.get_count() == 1, "Já bych chtěl, prosím, jen r1 :)"
AssertionError: Já bych chtěl, prosím, jen r1 :)
Editováno 14.6.2014 7:01
 
Odpovědět
14.6.2014 6:58
Avatar
coells
Tvůrce
Avatar
Odpovídá na hanpari
coells:14.6.2014 10:27

Objekt bude zrušený až ve chvíli, kdy už na něj nebudou existovat žádné reference.

import weakref

class Reference():
    _count = 0

    def __init__(self):
        print("Začátek objektu.")
        Reference._count += 1
        self.number = Reference._count

    def __del__(self):
        print("Konec objektu.")
        if Reference: Reference._count -= 1

    def __repr__(self):
        return "Ref: {}".format(self.number)


    @staticmethod
    def get_count():
        return Reference._count


if __name__ == "__main__":
    r1 = Reference()
    r2 = Reference()
    r3 = Reference()
    assert Reference.get_count() == 3, "Počet instancí 3, což je v pořádku"
    del(r3)
    assert Reference.get_count() == 2, "Počet instancí 2, také dobře."
    seznam = [weakref.ref(r1), weakref.ref(r2)]
    print([i() for i in seznam])
    del(r2)
    print([i() for i in seznam])
    assert Reference.get_count() == 1, "Já bych chtěl, prosím, jen r1 :)"

Ale abych Ti zamotal hlavu, zamysli se nad následujícím příkladem.

import weakref
import gc

class Reference():
    _count = 0

    def __init__(self):
        print("Začátek objektu.")
        Reference._count += 1
        self.number = Reference._count

    def __del__(self):
        print("Konec objektu %s" % self.number)
        if Reference: Reference._count -= 1

    def __repr__(self):
        return "Ref: {}".format(self.number)


    @staticmethod
    def get_count():
        return Reference._count


if __name__ == "__main__":
    gc.disable() # kvuli pythonu < 3.4
    r1 = Reference()
    r2 = Reference()
    r3 = Reference()
    r1.next = r2
    r2.next = r3
    r3.next = r1
    w1 = weakref.ref(r1)
    del(r1)
    del(r2)
    del(r3)
    print("prave jsem odstranil reference")
    r4 = Reference()
    r5 = Reference()
    w1().next = None

Mimochodem, Python 3.3 a 3.4 se budou chovat naprosto odlišně kvůli změně v GC.

Editováno 14.6.2014 10:28
 
Nahoru Odpovědět
14.6.2014 10:27
Avatar
hanpari
Člen
Avatar
Odpovídá na coells
hanpari:15.6.2014 0:24

Ahoj,
díky za ten tip, vypadá to, že dělá přesně to, co jsem potřeboval. I když teď musím ještě prozkoumat, co to vlastně použití weakref znamená a jak se s nimi dělá.

Co se týče toho druhého kódu, tak si buď jistý, že mi právě teď nezamotáš hlavu víc než to víno, co jsem za večer vypil :)
Slibuji, že se nad ním důkladněji zamyslím ráno, i když ti musím škodolibě sdělit, že mám python 3.3, takže jsem z obliga :)
Líbí se mi ale ta technika, kterou jsi použil. Skoro se mi chce říct, že je to dobré kung fu... :)

r1.next = r2
r2.next = r3
r3.next = r1

Po sem mi je vše jasné (za předpokladu, že gc je garbage collector), nicméně ta další část mi už tak jasná není. Hlavně proto, že nevím, co dělají ty závorky u posledního řádku:

w1().next = None #w1 by mělo být právě teď None nebo nedefinované, ne?
 
Nahoru Odpovědět
15.6.2014 0:24
Avatar
hanpari
Člen
Avatar
Odpovídá na coells
hanpari:15.6.2014 18:53

Tak a teď k té druhé části.

Chová se nečekaně, i v 3.3. Opravdu to není None, jak jsem myslel. Ve skutečnosti zůstala zachovaná weakref, i když by podle mne neměla.
Takže teď bych poprosil o vysvětlení :)

 
Nahoru Odpovědět
15.6.2014 18:53
Avatar
coells
Tvůrce
Avatar
Odpovídá na hanpari
coells:15.6.2014 19:09

retain cycle
GC jsem vypnul, aby se 3.3 chovala stejně jako 3.4 (3.3 je verze, která se chová špatně, 3.4 už je opravená)

https://docs.python.org/…rary/gc.html?…
přepni si dokumentaci na verzi 3.3

Editováno 15.6.2014 19:09
 
Nahoru Odpovědět
15.6.2014 19:09
Avatar
hanpari
Člen
Avatar
Odpovídá na coells
hanpari:15.6.2014 20:34

Počkej,
tím chceš naznačit, že to je žádoucí chování? Já myslel, že to byl nějaký vtipný bug, odstraněný v pythonu od verze 3.

Editováno 15.6.2014 20:35
 
Nahoru Odpovědět
15.6.2014 20:34
Avatar
coells
Tvůrce
Avatar
Odpovídá na hanpari
coells:15.6.2014 20:44

Je dobré rozumět tomu, proč se to tak chová.

 
Nahoru Odpovědět
15.6.2014 20:44
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 7 zpráv z 7.