web-dev-qa-db-fra.com

Quand le nombre de références est-il pour une variable locale dans A python fonction a-t-elle diminué?

J'ai la fonction suivante:

def myfn():
    big_obj = BigObj()
    result = consume(big_obj)
    return result

Quand le nombre de références est-il pour la valeur de Bigobj () augmenté/diminué: est-ce:

  1. lorsque consume(big_obj) est appelé (puisque BIG_OBJ n'est pas référencé par la suite dans MYFN)
  2. quand la fonction retourne
  3. un point, je ne sais pas encore

Cela ferait-il une différence pour changer la dernière ligne pour:

return consume(big_obj)

Modifier (Clarification des commentaires):

  • Une variable locale existe jusqu'à ce que la fonction renvoie
  • la référence peut être supprimée avec Del Obj

Mais qu'avez-vous avec les temporaires (E.G F1 (F2 ())?

J'ai vérifié des références aux temporaires avec ce code:

import sys
def f2(c):
    print("f2: References to c", sys.getrefcount(c))


def f0():
    print("f0")
    f2(object())

def f1():
    c = object()
    print("f1: References to c", sys.getrefcount(c))
    f2(c)

f0()
f1()

Ceci imprime:

f0
f2: References to c 3
f1: References to c 2
f2: References to c 4

Il semble que les références à des variables temporaires ont lieu. Ce n'est pas que Getrefcount en donne une plus que vous ne vous attendez pas parce que cela tient une référence aussi.

2
Hatatister

Disclaimer: La plupart des informations proviennent des commentaires. Alors crédit pour tout le monde qui a participé à la discussion.

Lorsqu'un objet est supprimé est un détail de mise en œuvre en général. Je vais me référer à CPHON, qui est basé sur le comptage de référence. J'ai exécuté les exemples de code avec CPPHON 3.10.0.

  • Un objet est supprimé lorsque le nombre de références frappe zéro.
  • Le retour d'une fonction supprime toutes les références locales.
  • L'attribution d'un nom à une nouvelle valeur diminue le nombre de références de l'ancienne valeur
  • le passage d'un local augmente le nombre de références. La référence est sur la pile (cadre)
  • Le retour d'une fonction supprime la référence de la pile

Le dernier point est même valable pour des références temporaires telles que f(g()). La dernière référence à g() est supprimée, lorsque F retourne (en supposant que g n'enregistre pas une référence quelque part) voir ici

Donc, pour l'exemple de la question:

def myfn():
    big_obj = BigObj() # reference 1                     
    result = consume(big_obj) # reference 2 on the stack frame for  
                              # consume. Not yet counting any 
                              # reference inside of consume
                              # after consume returns: The stack frame 
                              # and reference 2 are deleted. Reference  
                              # 1 remains
    return result             # myfn returns reference 1 is deleted. 
                              # BigObj is deleted
def consume(big_obj):
    pass # consume is holding reference 3

Si nous changerions cela pour:

def myfn():
    return consume(BigObj()) # reference is still saved on the stack 
                             # frame and will be only deleted after  
                             # consume returns
def consume(big_obj):
    pass # consume is holding reference 2

Comment puis-je vérifier de manière fiable, si un objet a été supprimé?

Vous ne pouvez pas compter sur gc.get_Objects (). GC est utilisé pour détecter et recycler les cycles de référence. Chaque référence n'est pas suivie par le GC. Vous pouvez créer une référence faible et vérifiez si la référence est toujours valide.

class BigObj:
    pass

import weakref
ref = None

def make_ref(obj):
    global ref
    ref = weakref.ref(obj)
    return obj

def myfn():
    return consume(make_ref(BigObj()))

def consume(obj):
    obj = None # remove to see impact on ref count
    print(sys.getrefcount(ref()))
    print(ref()) # There is still a valid reference. It is the one from consume stack frame

myfn ()

Comment transmettre une référence à une fonction et supprimer toutes les références de la fonction d'appel?

Vous pouvez encadrer la référence, passer à la fonction et effacer la référence en boîte de l'intérieur de la fonction:

class Ref:
    def __init__(ref):
        self.ref = ref
    def clear():
        self.ref = None

def f1(ref):
    r = ref.ref
    ref.clear()

def f2():
    f1(Ref(object())
1
Hatatister