2013-02-19 3 views
1

Dire que j'ai ce qui suit:référence textuelle d'un procédé

def func(): 
    print 'this is a function and not a method!!!' 

class Test: 
    def TestFunc(self): 
     print 'this is Test::TestFunc method' 

I ont les fonctions suivantes (qui sont prises à partir de https://bitbucket.org/agronholm/apscheduler/src/d2f00d9ac019/apscheduler/util.py):

def get_callable_name(func): 
    """ 
    Returns the best available display name for the given function/callable. 
    """ 
    f_self = getattr(func, '__self__', None) or getattr(func, 'im_self', None) 
    if f_self and hasattr(func, '__name__'): 
     if isinstance(f_self, type): 
      # class method 
      clsname = getattr(f_self, '__qualname__', None) or f_self.__name__ 
      return '%s.%s' % (clsname, func.__name__) 
     # bound method 
     return '%s.%s' % (f_self.__class__.__name__, func.__name__) 
    if hasattr(func, '__call__'): 
     if hasattr(func, '__name__'): 
      # function, unbound method or a class with a __call__ method 
      return func.__name__ 
     # instance of a class with a __call__ method 
     return func.__class__.__name__ 
    raise TypeError('Unable to determine a name for %s -- ' 
        'maybe it is not a callable?' % repr(func)) 


def obj_to_ref(obj): 
    """ 
    Returns the path to the given object. 
    """ 
    ref = '%s:%s' % (obj.__module__, get_callable_name(obj)) 
    try: 
     obj2 = ref_to_obj(ref) 
     if obj != obj2: 
      raise ValueError 
    except Exception: 
     raise ValueError('Cannot determine the reference to %s' % repr(obj)) 
    return ref 


def ref_to_obj(ref): 
    """ 
    Returns the object pointed to by ``ref``. 
    """ 
    if not isinstance(ref, basestring): 
     raise TypeError('References must be strings') 
    if not ':' in ref: 
     raise ValueError('Invalid reference') 
    modulename, rest = ref.split(':', 1) 
    try: 
     obj = __import__(modulename) 
    except ImportError: 
     raise LookupError('Error resolving reference %s: ' 
          'could not import module' % ref) 
    try: 
     for name in modulename.split('.')[1:] + rest.split('.'): 
      obj = getattr(obj, name) 
     return obj 
    except Exception: 
     raise LookupError('Error resolving reference %s: ' 
          'error looking up object' % ref) 

Les fonctions ci-dessus - obj_to_ref renvoie une référence textuelle à un objet fonction donné et ref_to_obj renvoie un objet pour la référence textuelle donnée. Par exemple, essayons la fonction func. La fonction func fonctionne correctement. Mais lorsqu'il a essayé d'utiliser ces fonctions sur une instance du class Test, il n'a pas pu obtenir de référence textuelle.

>>> 
>>> t = Test() 
>>> 
>>> t 
<__main__.Test instance at 0xb771b28c> 
>>> 
>>> t.TestFunc 
<bound method Test.TestFunc of <__main__.Test instance at 0xb771b28c>> 
>>> 
>>> 
>>> obj_to_ref(t.TestFunc) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 11, in obj_to_ref 
ValueError: Cannot determine the reference to <bound method Test.TestFunc of <__main__.Test instance at 0xb771b28c>> 
>>> 
>>> 

La fonction obj_to_ref pour l'entrée donnée t.TestFunc arrive avec __main__:Test.TestFunc comme une représentation textuelle, mais le même texte ne peut pas être utilisé pour générer l'objet.

Question:

Est-il possible dans Python où l'on peut représenter un objet comme

>>> t.TestFunc 
<bound method Test.TestFunc of <__main__.Test instance at 0xb771b28c>> 
>>> 

dans une chaîne et de reconstruire l'objet de la chaîne?

Serait-il possible si nous sauvegardions l'adresse 0xb771b28c comme une partie de la chaîne et régénérons l'objet en déréférencant cette adresse ?!

+1

Quel est le cas d'utilisation réel? Pour quoi l'utiliserez-vous? Comme pour la reconstruction actuelle: Vous pouvez tester 'eval ('t.TestFunc')' ou 'ref_to_obj ('__ main __: t.TestFunc')' – JCash

+0

Bonjour. Que doit faire la ligne '' for name in modulename.split ('.') [1:] + rest.split ('.'): ''? Dans votre code '' modulename.split ('.') [1:] '' donne '' [] '' – eyquem

+0

Le problème est dans 'get_callable_name', je pense. 'get_callable_name (t.TestFunc)' donne '' Test.TestFunc''. C'est clairement faux, car il semble que cela pointe vers une méthode de classe au lieu d'une méthode liée. – entropy

Répondre

1

Votre question est intéressante mais embrouillée.

1) Vous ne devez pas appeler func le paramètre de get_callable_name(func)
Dans ma réponse je l'ai remplacé par X.

2) Vous mettez une partie du code à un mauvais endroit.

try: 
    obj2 = ref_to_obj(ref) 
    print 'obj != obj2 : ',obj != obj2 
    if obj != obj2: 
     raise ValueError 
except Exception: 
    raise ValueError('Cannot determine the reference to %s' % repr(obj)) 
return ref 

n'a rien à faire à l'intérieur obj_to_ref()

Dans ma réponse, je l'ai déplacé en dehors de cette fonction.

3) La raison visible du problème de votre code est que la référence obtenue pour objet t.TestFunc (passé au paramètre X dans mon code) est '__main__:Test.TestFunc', pas '__main__:t.TestFunc' comme « devrait » être.

L'étape secrète où cela est décidé est dans la fonction get_callable_name() comme indiqué par entropie.
Depuis f.self est t et X a un nom (TestFunc) mais n'est pas une classe de type type (depuis t est une instance),
l'instruction return '%s.%s' % (f_self.__class__.__name__, X.__name__) est exécutée.

Mais vous avez tort de mettre l'expression f_self.__class__.__name: il est le nom de la classe de t, pas le nom de t lui-même.

. Le problème est que, peu probable à une classe (qui a un attribut __name__), rien n'est prévu dans le langage Python pour fournir le nom d'une instance à la demande: une instance n'a pas le même type d'attribut __name__ en tant que class, qui donnerait le nom de l'instance. Donc, étant mal à l'aise pour l'obtenir, une sorte de dérivation doit être utilisée.
Chaque fois qu'un nom non référencé est requis, le contournement consiste à rechercher parmi tous les noms d'un espace de noms et à tester l'objet correspondant par rapport à l'objet concerné.
C'est ce que fait la fonction get__callable_name() de entropie.

.

Avec cette fonction, cela fonctionne. Mais je tiens à souligner que c'est seulement un contournement délicat qui n'a pas de véritable fondement.
Je veux dire que le nom t.TestFunc pour la méthode est une illusion. Il y a une subtilité: il n'y a pas de méthode appartenant à une instance. Cela semble une prétention bizarre, mais je suis sûr que c'est la vérité.
Le fait que nous appelions une méthode grâce à une expression comme t.TestFunc conduit à croire que TestFunc appartient à l'instance. En réalité, il appartient à la classe et Python va d'une instance à sa classe pour trouver la méthode.

Je n'invente rien, je l'ai lu:

Une instance de classe a un espace de noms mis en œuvre comme un dictionnaire qui est le premier lieu où les références sont recherchées attribut. Lorsqu'un attribut est introuvable et que la classe de l'instance a un attribut correspondant à ce nom, la recherche continue avec les attributs de classe .Si un attribut de classe est un objet fonction défini par l'utilisateur ou un objet de méthode défini par l'utilisateur non lié dont la classe associée est la classe (appelez C) de l'instance pour laquelle la référence d'attribut a été initiée ou l'une de ses bases, il est transformé en un objet de méthode lié défini par l'utilisateur dont l'attribut im_class est C et dont l'attribut im_self est l'instance.

http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes

Mais bien, il est une autre histoire sur laquelle je serai contesté, je pense, et je « ai pas le temps de se livrer à cela.

Il suffit de vérifier le point suivant:
malgré le fait que getattr(t,"TestFunc") donne:
<bound method Test.TestFunc of <__main__.Test instance at 0x011D8DC8>>
la méthode TestFunc est pas dans l'espace de noms t: le résultat de t.__dict__ est { }! Je voulais juste le souligner parce que la fonction get_callable_name() ne fait que reproduire et imiter le comportement apparent et l'implémentation de Python.
Cependant le comportement réel et la mise en œuvre sous le capot est différent.

.

Dans le code suivant, je reçois le bon résultat en utilisant le isntruction
return '%s.%s' % ('t', X.__name__) au lieu de
return '%s.%s' % (f_self.__class__.__name__, func.__name__) ou
return '%s.%s' % (variable_name_in_module(__import__(f_self.__module__), f_self), func.__name__)
parce qu'il est essentiellement ce qui fait la fonction get_callanle_name() (il n'utilise pas un processus normal, il utilise une roublardise)

def get_callable_name(X): 
    """ 
    Returns the best available display name for the given function/callable. 
    """ 
    print '- inside get_callable_name()' 
    print ' object X arriving in get_callable_name() :\n ',X 
    f_self = getattr(X, '__self__', None) or getattr(X, 'im_self', None) 
    print ' X.__call__ ==',X.__call__ 
    print ' X.__name__ ==',X.__name__ 
    print '\n X.__self__== X.im_self ==',f_self 
    print ' isinstance(%r, type) is %r' % (f_self,isinstance(f_self, type)) 
    if f_self and hasattr(X, '__name__'): # it is a method 
     if isinstance(f_self, type): 
      # class method 
      clsname = getattr(f_self, '__qualname__', None) or f_self.__name__ 
      return '%s.%s' % (clsname, X.__name__) 
     # bound method 
     print '\n f_self.__class__   ==',f_self.__class__ 
     print ' f_self.__class__.__name__ ==',f_self.__class__.__name__ 
     return '%s.%s' % ('t', X.__name__) 
    if hasattr(X, '__call__'): 
     if hasattr(X, '__name__'): 
      # function, unbound method or a class with a __call__ method 
      return X.__name__ 
     # instance of a class with a __call__ method 
     return X.__class__.__name__ 
    raise TypeError('Unable to determine a name for %s -- ' 
        'maybe it is not a callable?' % repr(X)) 


def obj_to_ref(obj): 
    """ 
    Returns the path to the given object. 
    """ 
    print '- obj arriving in obj_to_ref :\n %r' % obj 

    ref = '%s:%s' % (obj.__module__, get_callable_name(obj)) 

    return ref 


def ref_to_obj(ref): 
    """ 
    Returns the object pointed to by ``ref``. 
    """ 
    print '- ref arriving in ref_to_obj == %r' % ref 

    if not isinstance(ref, basestring): 
     raise TypeError('References must be strings') 
    if not ':' in ref: 
     raise ValueError('Invalid reference') 
    modulename, rest = ref.split(':', 1) 

    try: 
     obj = __import__(modulename) 
    except ImportError: 
     raise LookupError('Error resolving reference %s: ' 
          'could not import module' % ref) 

    print ' we start with dictionary obj == ',obj 
    try: 
     for name in modulename.split('.')[1:] + rest.split('.'): 
      print ' object of name ',name,' searched in',obj 
      obj = getattr(obj, name) 
      print ' got obj ==',obj 
     return obj 
    except Exception: 
     raise LookupError('Error resolving reference %s: ' 
          'error looking up object' % ref) 

class Test: 
    def TestFunc(self): 
     print 'this is Test::TestFunc method' 


t = Test() 

print 't ==',t 

print '\nt.TestFunc ==',t.TestFunc 

print "getattr(t,'TestFunc') ==",getattr(t,'TestFunc') 

print ('\nTrying to obtain reference of t.TestFunc\n' 
     '----------------------------------------') 

print '- REF = obj_to_ref(t.TestFunc) done' 
REF = obj_to_ref(t.TestFunc) 
print '\n- REF obtained: %r' % REF 

print ("\n\nVerifying what is ref_to_obj(REF)\n" 
     "---------------------------------") 
try: 
    print '- obj2 = ref_to_obj(REF) done' 
    obj2 = ref_to_obj(REF) 
    if obj2 != t.TestFunc: 
     raise ValueError 
except Exception: 
     raise ValueError('Cannot determine the object of reference %s' % REF) 
print '\n- object obtained : ',obj2 

résultat

t == <__main__.Test instance at 0x011DF5A8> 

t.TestFunc == <bound method Test.TestFunc of <__main__.Test instance at 0x011DF5A8>> 
getattr(t,'TestFunc') == <bound method Test.TestFunc of <__main__.Test instance at 0x011DF5A8>> 

Trying to obtain reference of t.TestFunc 
---------------------------------------- 
- REF = obj_to_ref(t.TestFunc) done 
- obj arriving in obj_to_ref : 
    <bound method Test.TestFunc of <__main__.Test instance at 0x011DF5A8>> 
- inside get_callable_name() 
    object X arriving in get_callable_name() : 
    <bound method Test.TestFunc of <__main__.Test instance at 0x011DF5A8>> 
    X.__call__ == <method-wrapper '__call__' of instancemethod object at 0x011DB990> 
    X.__name__ == TestFunc 

    X.__self__== X.im_self == <__main__.Test instance at 0x011DF5A8> 
    isinstance(<__main__.Test instance at 0x011DF5A8>, type) is False 

    f_self.__class__   == __main__.Test 
    f_self.__class__.__name__ == Test 

- REF obtained: '__main__:t.TestFunc' 


Verifying what is ref_to_obj(REF) 
--------------------------------- 
- obj2 = ref_to_obj(REF) done 
- ref arriving in ref_to_obj == '__main__:t.TestFunc' 
    we start with dictionary obj == <module '__main__' (built-in)> 
    object of name t searched in <module '__main__' (built-in)> 
    got obj == <__main__.Test instance at 0x011DF5A8> 
    object of name TestFunc searched in <__main__.Test instance at 0x011DF5A8> 
    got obj == <bound method Test.TestFunc of <__main__.Test instance at 0x011DF5A8>> 

- object obtained : <bound method Test.TestFunc of <__main__.Test instance at 0x011DF5A8>> 
>>> 
1

Comme je l'ai dit dans mon commentaire ci-dessus, le problème est dans get_callable_name. get_callable_name(t.TestFunc) donne 'Test.TestFunc' ce qui est évidemment faux. Cela devrait être 't.TestFunc'. J'ai ajouté variable_name_in_module et utilisé cela dans get_callable_name et maintenant le code fonctionne. La vérification en bas renvoie True. Cependant, variable_name_in_module est très hackish et je ne pouvais pas trouver un moyen de le faire dans un cleanly.

Si vous n'en avez besoin que pour les petites choses, alors cela devrait être correct, mais sachez que variable_name_in_module effectue une recherche de dictionnaire N pour chaque appel à get_callable_name où N est le nombre de variables dans le module.

Code suit:

def variable_name_in_module(module, var): 
    for name in dir(module): 
     if getattr(module, name) == var: 
      return name 

def get_callable_name(func): 
    """ 
    Returns the best available display name for the given function/callable. 
    """ 
    f_self = getattr(func, '__self__', None) or getattr(func, 'im_self', None) 
    if f_self and hasattr(func, '__name__'): 
     if isinstance(f_self, type): 
      # class method 
      clsname = getattr(f_self, '__qualname__', None) or f_self.__name__ 
      return '%s.%s' % (clsname, func.__name__) 
     # bound method 
     return '%s.%s' % (variable_name_in_module(__import__(f_self.__module__), f_self), func.__name__) 
    if hasattr(func, '__call__'): 
     if hasattr(func, '__name__'): 
      # function, unbound method or a class with a __call__ method 
      return func.__name__ 
     # instance of a class with a __call__ method 
     return func.__class__.__name__ 
    raise TypeError('Unable to determine a name for %s -- ' 
        'maybe it is not a callable?' % repr(func)) 


def obj_to_ref(obj): 
    """ 
    Returns the path to the given object. 
    """ 
    ref = '%s:%s' % (obj.__module__, get_callable_name(obj)) 
    try: 
     obj2 = ref_to_obj(ref) 
     if obj != obj2: 
      raise ValueError 
    except Exception: 
     raise ValueError('Cannot determine the reference to %s' % repr(obj)) 
    return ref 


def ref_to_obj(ref): 
    """ 
    Returns the object pointed to by ``ref``. 
    """ 
    if not isinstance(ref, basestring): 
     raise TypeError('References must be strings') 
    if not ':' in ref: 
     raise ValueError('Invalid reference') 
    modulename, rest = ref.split(':', 1) 
    try: 
     obj = __import__(modulename) 
    except ImportError: 
     raise LookupError('Error resolving reference %s: ' 
          'could not import module' % ref) 
    try: 
     for name in modulename.split('.')[1:] + rest.split('.'): 
      obj = getattr(obj, name) 
     return obj 
    except Exception: 
     raise LookupError('Error resolving reference %s: ' 
          'error looking up object' % ref) 

class Test: 
    def TestFunc(self): 
     print "test" 

t = Test() 
print t.TestFunc == ref_to_obj(obj_to_ref(t.TestFunc)) 

Edit: PS: variable_name_in_module devrait probablement lancer une exception si elle ne trouve rien, mais je ne vois pas comment cela peut arriver.

+0

Dans un sens, vous avez raison. Le problème est que la fonction '' get_callable_name() '' ne renvoie pas la référence qui serait la bonne à partir de laquelle le même objet que l'objet de départ ('' t.TestFunc'') serait obtenu, c'est-à-dire Dites une méthode liée. – eyquem

+0

Cependant votre fonction '' nom_variable_in_module() '' est une astuce basée sur un fonctionnement trompeur de Python: je veux dire, quand un appel '' t.TestFunc'' est fait, Python ne trouve pas une méthode nommée '' TestFunc'' dans l'instance '' t'', il va dans la classe de l'instance pour y prendre et exécuter la méthode. Bien que la méthode ainsi atteinte soit décrite comme "méthode liée", différemment d'une "méthode non liée" quand la même méthode est atteinte par "Test.TestFunc", c'est seulement une méthode unique. – eyquem

+0

Ensuite, il faut respecter la manière trompeuse de Python de passer de '' t.TestFunc'' à la méthode présente dans la classe, '' test.TestFunc'', qu'il est obligatoire que '' t.TestFunc'' soit retourné par '' get_callable_name() ''! – eyquem