2013-02-19 5 views
1

Скажем, у меня есть следующие:Текстология ссылка метода

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

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

У меня есть следующие функции (которые взяты из 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) 

Вышеуказанные функции - obj_to_ref возвращает текстовую ссылку к заданному объекту функции и ref_to_obj возвращает объект для данной текстовой ссылки. Например, попробуйте функцию func.

>>> 
>>> func 
<function func at 0xb7704924> 
>>> 
>>> obj_to_ref(func) 
'__main__:func' 
>>> 
>>> ref_to_obj('__main__:func') 
<function func at 0xb7704924> 
>>> 

func функция работает нормально. Но когда вы пытались использовать эту функцию в экземпляре class Test, он не смог получить текстовую ссылку.

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

obj_to_ref функция для данного входа t.TestFunc приходит с __main__:Test.TestFunc как текстовое представление, но тот же текст не может быть использован для генерации объекта.

Вопрос:

Есть ли способ в Python, где мы можем представить объект как

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

в строке и восстановить объект из строки?

Возможно ли сохранение адреса 0xb771b28c в качестве части строки и восстановление объекта путем разыменования этого адреса ?!

+1

Каков фактический прецедент? Для чего вы его используете? Что касается фактической реконструкции: вы можете проверить 'eval ('t.TestFunc')' или 'ref_to_obj ('__ main __: t.TestFunc')' – JCash

+0

Здравствуйте. Что должно делать строка '' для имени в modulename.split ('.') [1:] + rest.split ('.'): ''? В вашем коде '' modulename.split ('.') [1:] '' дает '' [] '' – eyquem

+0

Проблема в 'get_callable_name', я думаю. 'get_callable_name (t.TestFunc)' дает '' Test.TestFunc''. Это явно неправильно, поскольку похоже, что это указывает на метод класса, а не на связанный метод. – entropy

ответ

1

Ваш вопрос интересный, но запутанный.

1) Вы не должны вызывать func параметр get_callable_name(func)
В моем ответе я заменил его X.

2) Вы помещаете часть кода в неправильное место.

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 

не имеет ничего общего внутри obj_to_ref()

В своем ответе я переместил его вне этой функции.

3) Видимая причина проблемы вашего кода является то, что ссылка на объект, полученный t.TestFunc (передается параметр X в моем коде) является '__main__:Test.TestFunc', не '__main__:t.TestFunc' как это «должно» быть.

Секретный шаг, на котором это определено, находится в функции get_callable_name(), как указано энтропия.
Поскольку f.self является t и X имеет имя (TestFunc), но не является классом типа type (поскольку t является экземпляром),
инструкция return '%s.%s' % (f_self.__class__.__name__, X.__name__) выполняется.

Но вы ошибаетесь поставить выражение f_self.__class__.__name: это имя класса t, а не имя самого t.

.

Проблема заключается в том, что, вряд ли к классу (то есть атрибут __name__), ничего не предназначено на языке Python предоставить имя экземпляра по требованию: экземпляр не имеет такой же атрибут __name__ как класс, который дал бы имя экземпляра.

Таким образом, будучи неспокойным для его получения, необходимо использовать своего рода шунтирование.
Каждый раз, когда требуется имя без ссылки, байпас должен искать среди всех имен пространства имен и проверять соответствующий объект на соответствующий объект.
Вот что делает функция get__callable_name() от энтропия.

.

Эта функция работает.

Но я хочу подчеркнуть, что это всего лишь сложный обход, который не имеет реального фундамента.
Я имею в виду, что имя t.TestFunc для метода - иллюзия. Есть тонкость: нет метода, принадлежащего экземпляру. Это кажется странным притворством, но я уверен, что это правда.
Тот факт, что мы называем метод благодаря выражению типа t.TestFunc, приводит к выводу, что TestFunc принадлежит экземпляру. В действительности это принадлежит классу, и Python переходит от экземпляра к его классу, чтобы найти метод.

Я ничего не придумывал, я прочитал:

Экземпляр класса имеет пространство имен, выполненный в виде словаря, который является первое место, в котором атрибут ссылки ищутся. Когда атрибут не найден там, и класс экземпляра имеет атрибут этим именем, поиск продолжается с атрибутами класса .Если обнаружен атрибут класса, который является определяемым пользователем 0 функциональным объектом или несвязанным определяемым пользователем объектом метода, ассоциированным классом является класс (называет его C) экземпляра, для которого была инициирована ссылка на атрибут или одна из ее оснований, это , преобразованный в связанный пользовательский объект метода, чей атрибут im_class является C и чей im_self является экземпляром.

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

Но хорошо, это другая история, на которой я буду оспаривать, я думаю, и я не ве нет времени, чтобы заниматься этим.

Просто проверьте следующий пункт:
несмотря на то, что getattr(t,"TestFunc") дает:
<bound method Test.TestFunc of <__main__.Test instance at 0x011D8DC8>>
метод TestFunc не в пространстве имен t: результат t.__dict__ является { }!

Я просто хотел указать на это, потому что функция get_callable_name() воспроизводит и имитирует кажущееся поведение и реализацию Python.
Однако реальное поведение и реализация под капотом различны.

.

В следующем коде, я получаю хороший результат, используя isntruction
return '%s.%s' % ('t', X.__name__) вместо
return '%s.%s' % (f_self.__class__.__name__, func.__name__) или
return '%s.%s' % (variable_name_in_module(__import__(f_self.__module__), f_self), func.__name__)
, потому что это в основном то, что делает эта функция get_callanle_name() (она не использует нормальный процесс, он использует лукавство)

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 

результат

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

Как я уже говорил в своем комментарии выше, проблема заключается в get_callable_name. get_callable_name(t.TestFunc) дает 'Test.TestFunc', что, очевидно, неверно. Это должен быть «t.TestFunc». Я добавил variable_name_in_module и использовал это в get_callable_name и теперь код работает. Проверка внизу возвращает True. Однако variable_name_in_module очень хакерский, и я не мог найти способ сделать это в чистоте.

Если вам это нужно только для небольших вещей, то это должно быть хорошо, но имейте в виду, что variable_name_in_module несет поиск словаря N для каждого вызова get_callable_name где N - количество переменных в модуле.

код следующим образом:

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, вероятно, следует бросить исключение, если он не может найти что-нибудь, хотя я не понимаю, как это может произойти.

+0

В некотором смысле, вы правы. Проблема в том, что функция '' get_callable_name() '' не возвращает ссылку, которая была бы хорошей, из которой был бы получен тот же объект, что и исходный объект ('' t.TestFunc'', то есть скажем, связанный метод. – eyquem

+0

Однако ваша функция '' variable_name_in_module() '' - это трюк, основанный на обманчивом функционировании Python: я имею в виду, что когда вызов 't.TestFunc'' завершен, Python не находит метод с именем' ' TestFunc'' в экземпляре '' t'', он входит в класс экземпляра, чтобы взять и выполнить метод в нем. Хотя метод, полученный таким образом, описывается как «связанный метод», иначе как «несвязанный метод», когда тот же самый достигается через «Test.TestFunc'», это единственный и уникальный метод. – eyquem

+0

Тогда нужно уважать обманчивый способ Python перейти от '' t.TestFunc'' к методу, присутствующему в классе '' test.TestFunc'', что обязательным является '' t.TestFunc'' возвращается '' get_callable_name() ''! – eyquem