IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

[python 3] [weakref] Une référence devient None alors qu'elle est encore référencée


Sujet :

Python

  1. #1
    Membre très actif
    Avatar de ProgVal
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2006
    Messages
    636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2006
    Messages : 636
    Par défaut [python 3] [weakref] Une référence devient None alors qu'elle est encore référencée
    Bonjour,

    J'ai le programme suivant : (désolé, c'est un gros paté non commenté, mais il n'est pas de moi)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    def ref(obj, weak, _plain=0):
        if not _plain and is_callable(obj):
            return CallableReference(obj, weak)
        if weak:
            return WeakReference(obj)
        else:
            return StrongReference(obj)
     
    def is_callable(obj):
        if isinstance(obj, collections.Callable): return 1
        try:
            return isinstance(obj[1], collections.Callable)
        except:
            return 0
     
    def ref_is(ref1,ref2):
        if ref1 is ref2: return 1
        o1 = ref1 and ref1()
        o2 = ref2 and ref2()
     
        # DEBUG:
        print(repr(o1))
        print(repr(o2))
        #if o1 is None or o2 is None: return 0
     
        return o1 is o2
     
    class Reference:
        def callback(self, obj):
            for cb in self.callbacks:
                cb(obj, self)
            self.callbacks = []
        def __init__(self, obj):
            self.obj = self.ref(obj, self.callback)
            self.callbacks = []
            self.hash = generic_hash(obj)
        def __call__(self):
            return self.deref(self.obj)
        def __hash__(self):
            return self.hash
        __eq__ = ref_is
     
    class WeakReference(Reference):
        def ref(self, obj, cb):
            global weakref
            if not weakref: import weakref
            return weakref.ref(obj, cb)
        def deref(self, obj):
            return obj()
     
    class StrongReference(Reference):
        def ref(self, obj, cb):
            return obj
        def deref(self, obj):
            return obj
     
    class CallableWrapper:
        def __init__(self, obj, func):
            self.obj = obj
            self.func = func
        def __call__(self, *args, **kwds):
            if self.obj is None:
                return self.func(*args, **kwds)
            else:
                return self.func(self.obj, *args, **kwds)
     
    def is_callable_instance(obj):
        return type(obj) is object and hasattr(obj, '__call__')
     
    def is_method(obj):
        return hasattr(obj, 'im_self')
     
    def unwrap(func):
        try:
            obj, func = func
        except:
            obj = None
        if is_callable_instance(func):
            func = func.__call__
        if is_method(func):
            if func.__self__ is not None:
                if obj is None: obj = func.__self__
                else: assert obj is func.__self__
            func = func.__func__
        return obj, func
     
    class CallableReference:
     
        # RefKeyDictionnary wants it...
        callbacks = []
     
        def __init__(self, func, weak):
            obj, func = unwrap(func)
     
            self.hash = hash((obj, func))
     
            if obj is not None:
                obj = ref(obj, weak, _plain=1)
            self.obj = obj
     
            self.func = ref(func, weak, _plain=1)
     
        def is_dead(self):
            return self.obj is not None and self.obj() is None \
                   or self.func() is None
     
        def __call__(self):
            if self.is_dead(): return None
            obj = self.obj
            if obj is not None: obj = obj()
            func = self.func()
            return CallableWrapper(obj, func)
     
        def __eq__(self,other):
            if self.__class__ != other.__class__: return 0
            return (ref_is(self.obj,other.obj) and
                    ref_is(self.func,other.func))
        def __hash__(self):
            return self.hash
     
    ##############################################
    class Test:
        def func2(self):
            print(self)
        def __repr__(self):
            return '<Test>'
        def __call__(self, x):
            print(x)
     
    test = Test()
    sc3 = ref(test.func2, weak=0)
    wc3 = ref(test.func2, weak=1)
    print(sc3==wc3)
    Avec pour sortie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <bound method Test.func2 of <Test>>
    None
    False
    J'aimerai savoir pourquoi la seconde ligne de la sortie est None, alors qu'elle devrait, à mon humble avis, être <bound method Test.func2 of <Test>>.

    Merci d'avance,
    ProgVal

    PS : Désolé si le titre n'est pas très explicite, mais à part "ça marche paaaaaas", je ne voyais pas vraiment quoi mettre

  2. #2
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 307
    Par défaut
    Bonjour,

    Idéalement,il faudrait enlever du code ce qui ne participe pas au problème.

    Histoire de faciliter la tâche du lecteur.

    De plus, on ne peut pas le tester, mais j'ai bien lu que tu n'en est pas l'auteur.

    Bref, une version testable et on regarde.

  3. #3
    Membre très actif
    Avatar de ProgVal
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2006
    Messages
    636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2006
    Messages : 636
    Par défaut
    Oups, désolé.

    Voilà, un code qui peut être lancé, et sans les parties inutiles :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    import collections
     
    weakref = None
     
    def ref(obj, weak, _plain=0):
        if not _plain and is_callable(obj):
            return CallableReference(obj, weak)
        if weak:
            return WeakReference(obj)
        else:
            return StrongReference(obj)
     
    def is_callable(obj):
        if isinstance(obj, collections.Callable): return 1
        try:
            return isinstance(obj[1], collections.Callable)
        except:
            return 0
     
    class WeakReference():
        def callback(self, obj):
            for cb in self.callbacks:
                cb(obj, self)
            self.callbacks = []
        def __init__(self, obj):
            self.obj = self.ref(obj, self.callback)
            self.callbacks = []
            self.hash = id(obj)
        def __call__(self):
            return self.deref(self.obj)
        def ref(self, obj, cb):
            global weakref
            if not weakref: import weakref
            return weakref.ref(obj, cb)
        def deref(self, obj):
            return obj()
     
    def is_callable_instance(obj):
        return type(obj) is object and hasattr(obj, '__call__')
     
    def unwrap(func):
        try:
            obj, func = func
        except:
            obj = None
        if is_callable_instance(func):
            func = func.__call__
        return obj, func
     
    class CallableReference:
     
        # RefKeyDictionnary wants it...
        callbacks = []
     
        def __init__(self, func, weak):
            obj, func = unwrap(func)
     
            self.hash = hash((obj, func))
     
            if obj is not None:
                obj = ref(obj, weak, _plain=1)
            self.obj = obj
     
            self.func = ref(func, weak, _plain=1)
     
    ##############################################
    class Test:
        def func2(self):
            print(self)
     
    test = Test()
    wc3 = ref(test.func2, weak=1)
    print(repr(wc3.obj))

  4. #4
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonjour,

    Et bien si on regarde ce qu'est wc3 on trouve une instance de CallableReference, et pour CallableReference self.obj est None.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    def ref(obj, weak, _plain=0):
        if not _plain and is_callable(obj):
            return CallableReference(obj, weak)
        if weak:
            return WeakReference(obj)
        else:
            return StrongReference(obj)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    def unwrap(func):
        try:
            obj, func = func
        except:
            obj = None
        if is_callable_instance(func):
            func = func.__call__
        return obj, func
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class CallableReference:
        # RefKeyDictionnary wants it...
        callbacks = []
    
        def __init__(self, func, weak):
            obj, func = unwrap(func)
            # None, fonction
            self.hash = hash((obj, func))
            if obj is not None: # obj = None. pass
                obj = ref(obj, weak, _plain=1)
            self.obj = obj # self.obj = None
            self.func = ref(func, weak, _plain=1)
    Pour le code donné du moins.

    @+

    Edit: Rajout de ref

  5. #5
    Membre très actif
    Avatar de ProgVal
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2006
    Messages
    636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2006
    Messages : 636
    Par défaut
    Finalement, comme le code me reste obscur (notamment cette histoire de self.obj initialisé à None, comme tu l'as pointé du doigt), j'ai décidé de réimplémenter le module .
    Cependant, j'ai à nouveau des None qui apparaissent.

    Voici le nouveau code : (c'est le code complet du module, plus un extrait des tests)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    import weakref
     
    def ref(obj, weak):
        cls = WeakReference if weak else StrongReference
     
        return cls(obj)
     
    class CallableWrapper:
        """Fakes a weakref object (ie. returns the object when called), used by
        StrongReference."""
        def __init__(self, obj):
            self.obj = obj
     
        def __call__(self):
            return self.obj
     
    class Reference:
        def __init__(self, obj):
            self.hash = hash(obj)
            self.obj = self.ref(obj)
            self.callbacks = []
     
        def callback(self, obj):
            """Called by weakref when the object is about to be finalized.
            Calls all the registered callbacks."""
            for cb in self.callbacks:
                cb(obj, self)
            self.callbacks = []
     
        def __call__(self):
            return self.obj()
     
        def __eq__(self, other):
            if isinstance(other, Reference):
                print(repr(self.obj())) # <bound method Test.func2 of <__main__.Test object at 0x1559810>>
                print(repr(other.obj())) # None
                return self.obj() == other.obj()
            else:
                # Note aux forumeurs : cette partie du code n'est pas utilisée.
                return self.obj() == other
     
        def __hash__(self):
            return self.hash
     
    class WeakReference(Reference):
        def ref(self, obj):
            return weakref.ref(obj, self.callback)
     
    class StrongReference(Reference):
        def ref(self, obj):
            return CallableWrapper(obj)
     
    ##################
    # Test
     
    class Test:
        def func2(self):
            print(self)
     
    test = Test()
    sc3 = ref(test.func2, weak=0)
    wc3 = ref(test.func2, weak=1)
    print(repr(sc3 == wc3)) # False
    Alors que je me serais attendu à ce que print(repr(other.obj())) affiche la même chose que print(repr(self.obj())), il affiche None...

  6. #6
    Membre très actif
    Avatar de ProgVal
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2006
    Messages
    636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2006
    Messages : 636
    Par défaut
    À mon avis, le problème est le même ici :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import weakref
     
    class Test:
        def func2(self):
            print('spam')
     
    test = Test()
    egg = weakref.ref(test.func2)
    print(repr(egg())) # None
    EDIT : autre version :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import weakref
     
    class Test:
        def func2(self):
            print('spam')
     
    test = Test()
    foo = test.func2
    egg = weakref.ref(foo)
    print(repr(egg)) # <weakref at 0x284fe68; to 'method' at 0x27c1e18 (func2)>
    egg = weakref.ref(test.func2)
    print(repr(egg)) # <weakref at 0x284fec0; dead>

  7. #7
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 715
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 715
    Par défaut
    Salut,

    "test.func2" est une "bound method". Elle est évaluée à chaque fois.
    => weakref.ref(test.func2) retournera fort justement None parce que çà n'existe plus.

    La construction:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    foo = test.func2
    egg = weakref.ref(foo)
    fonctionne parce qu'on passe par un "stongref" mais ce n'est pas ce qu'on veut.

    => impossible de construire un weakref "directement" vers la méthode!
    voir les exemples de solution sur active state.

    Bon courage
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  8. #8
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonjour,

    Tout à fait, et c'est bien ce qui est dit dans la doc.

    Ceci dit l'erreur du départ ne viens pas de la mais du fait du portage partiel d'un code Python 2.x vers Python 3.
    Comment voulez vous que (code initial)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    def is_method(obj):
        return hasattr(obj, 'im_self')
    retourne True sous Python 3 ? (return hasattr(obj, '__self__'). J'ai même pas tilter...)

    Sans compter certains détails
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    >>> def is_callable_instance(obj):
    ...     return type(obj) is object and hasattr(obj, '__call__')
    ...
    >>> class Foo(object):
    ...     def __call__(self):
    ...          pass
    ... 
    >>> f = Foo()
    >>> is_callable_instance(f)
    False
    >>> f()
    >>> def is_callable_instance(obj):
    ...     if hasattr(obj, '__class__'):
    ...         return type(obj) != type and \
    ...             isinstance(obj, obj.__class__) and \
    ...             hasattr(obj, '__call__')
    ...     else:
    ...         return False # Needed ?
    ... 
    >>> is_callable_instance(f)
    True
    >>> class Foo(object):
    ...     pass
    ... 
    >>> f1 = Foo()
    >>> is_callable_instance(f1)
    False
    (Il y a sans doute mieux)
    Pas étonnant que Python 3 ne s'y retrouve pas, le plus étonnant c'est que cela ne plante pas.

    La suite de la discussion reviens à ce que dit wiztricks.

    Vous pouvez par contre jeter un œil au code donné en lien . Voici ce que donne l'exemple suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Test:
        def func2(self):
            print(self)
        def __repr__(self):
            return '<Test>'
        def __call__(self, x):
            print(x)
     
     
    import sys
    print(sys.version)
    test = Test()
    sc3 = ref(test.func2, weak=0)
    wc3 = ref(test.func2, weak=1)
    print(sc3)
    print(wc3)
    print(sc3==wc3)
    print(repr(wc3.obj))
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    2.6.6 (r266:84292, Mar 25 2011, 19:24:58) 
    [GCC 4.5.2]
    <__main__.CallableReference instance at 0xa29542c>
    <__main__.CallableReference instance at 0xa29548c>
    True
    <__main__.WeakReference instance at 0xa2954ac>
    @+

  9. #9
    Membre très actif
    Avatar de ProgVal
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2006
    Messages
    636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2006
    Messages : 636
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Salut,

    "test.func2" est une "bound method". Elle est évaluée à chaque fois.
    => weakref.ref(test.func2) retournera fort justement None parce que çà n'existe plus.
    Ah d'accord. Je comprend enfin.

    Citation Envoyé par wiztricks Voir le message
    => impossible de construire un weakref "directement" vers la méthode!
    voir les exemples de solution sur active state.
    Merci pour le code.

    Citation Envoyé par PauseKawa Voir le message
    Ceci dit l'erreur du départ ne viens pas de la mais du fait du portage partiel d'un code Python 2.x vers Python 3.
    Oui, effectivement, c'est moi qui l'ai porté. Et visiblement, j'ai fait ça n'importe comment (en même temps, je n'avais jamais utilisé Python 3 auparavant).

    Citation Envoyé par PauseKawa Voir le message
    Comment voulez vous que (code initial)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    def is_method(obj):
        return hasattr(obj, 'im_self')
    retourne True sous Python 3 ? (return hasattr(obj, '__self__'). J'ai même pas tilter...)
    Je ne savais même pas que im_self était particulier sous Python 2, et je n'avais pas du tout fait attention à cette fonction. Je me coucherais moins bête ce soir.

    Merci à tous les deux pour votre aide.

  10. #10
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonjour,

    Citation Envoyé par ProgVal Voir le message
    Je ne savais même pas que im_self était particulier sous Python 2, et je n'avais pas du tout fait attention à cette fonction.
    C'était pourtant en bonne voie
    Citation Envoyé par ProgVal Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def unwrap(func):
        try:
            obj, func = func
        except:
            obj = None
        if is_callable_instance(func):
            func = func.__call__
        if is_method(func):
            if func.__self__ is not None:
                if obj is None: obj = func.__self__
                else: assert obj is func.__self__
            func = func.__func__
        return obj, func
    C'est un bon exercice en fait que de traduire le code mais weakref demande une première approche je pense (surtout le proxy et __del__).

    Bon code et @+

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 13
    Dernier message: 31/07/2013, 11h25
  2. Réponses: 6
    Dernier message: 18/07/2012, 09h08
  3. Réponses: 5
    Dernier message: 23/08/2011, 21h19
  4. Réponses: 6
    Dernier message: 01/06/2007, 17h39
  5. [Zlib] Dll injoignable alors qu'elle est bien installée
    Par Jamming Ed dans le forum Installation, Déploiement et Sécurité
    Réponses: 4
    Dernier message: 07/11/2005, 15h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo