Say I have the following:
def func():
print 'this is a function and not a method!!!'
class Test:
def TestFunc(self):
print 'this is Test::TestFunc method'
I have the following functions (which are taken from 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)
The above functions - obj_to_ref
returns a textual reference to a given function object and ref_to_obj
returns an object for the given textual reference. For example, lets try the func
function.
>>>
>>> func
<function func at 0xb7704924>
>>>
>>> obj_to_ref(func)
'__main__:func'
>>>
>>> ref_to_obj('__main__:func')
<function func at 0xb7704924>
>>>
The func
function is working fine. But when tried to use these function on an instance of the class Test
, it couldn't get a textual reference.
>>>
>>> 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>>
>>>
>>>
The obj_to_ref
function for the given input t.TestFunc
comes up with __main__:Test.TestFunc
as a textual representation but the same text cannot be used to generate the object.
Question:
Is there a way in Python
where we can represent an object like
>>> t.TestFunc
<bound method Test.TestFunc of <__main__.Test instance at 0xb771b28c>>
>>>
in a string and reconstruct the object from the string?
Would it be possible if we save the address 0xb771b28c
as a part of the string and regenerate the object by dereferencing this address?!
Your question is interesting but tangled.
1) You shouldn't call
func
the parameter ofget_callable_name(func)
In my answer I replaced it by
X
.2) You put a part of the code at a wrong place.
has nothing to do inside
obj_to_ref()
In my answer, I moved it outside of this function.
3) The visible reason of the problem of your code is that the reference obtained for object
t.TestFunc
(passed to parameterX
in my code) is'__main__:Test.TestFunc'
, not'__main__:t.TestFunc'
as it "should" be.The secretive step where this is decided is in the function
get_callable_name()
as said by entropy.Since
f.self
ist
andX
has a name (TestFunc) but is not a class of typetype
(sincet
is an instance) ,the instruction
return '%s.%s' % (f_self.__class__.__name__, X.__name__)
is executed.But you're wrong to put the expression
f_self.__class__.__name
: it is the name of the class oft
, not the name oft
itself..
The problem is that , unlikely to a class (that has an attribute
__name__
), nothing is intended in the Python language to furnish the name of an instance on demand: an instance has not the same kind of attribute__name__
as a class, that would give the instance's name.So , being uneasy to get it, a sort of bypass must be employed.
Every time an unreferenced name is needed, the bypass is to search among all the names of a namespace and to test the corresponding object against the concerned object.
That's what does the function
get__callable_name()
of entropy..
With this function, it works.
But I want to underline that it's only a tricky bypass that has not a real fundament.
I mean that the name
t.TestFunc
for the method is an illusion. There's a subtlety: there is no method belonging to an instance. That seems a weird pretention, but I'm sure it's the truth.The fact that we call a method thanks to an expression like
t.TestFunc
leads to the believing thatTestFunc
belongs to the instance. In reality it belongs to the class and Python goes from an instance to it's class to find the method.I do not invent anything, I've read it:
But well, it's another story on which I will be disputed, I think, and I 've no time to engage in that.
Just verify the following point:
despite the fact that
getattr(t,"TestFunc")
gives :<bound method Test.TestFunc of <__main__.Test instance at 0x011D8DC8>>
the method TestFunc is not in the namespace of
t
: the result oft.__dict__
is{ }
!I just wanted to point it out because the function
get_callable_name()
only reproduces and mimics the apparent behavior and implementation of Python.However the real behavior and implementation under the hood is different.
.
In the following code, I get the good result by using the isntruction
return '%s.%s' % ('t', X.__name__)
instead ofreturn '%s.%s' % (f_self.__class__.__name__, func.__name__)
orreturn '%s.%s' % (variable_name_in_module(__import__(f_self.__module__), f_self), func.__name__)
because it's essentially what does the function
get_callanle_name()
(it doesn't uses a normal process, it uses a craftiness)result
As I said in my comment above, the problem is in
get_callable_name
.get_callable_name(t.TestFunc)
yields'Test.TestFunc'
which is obviously wrong. It should be 't.TestFunc'. I addedvariable_name_in_module
and used that inget_callable_name
and now the code works. The check at the bottom returnsTrue
. However,variable_name_in_module
is very hackish and I couldn't find a way to do this in a cleanly.If you only need this for smallish stuff then this should be fine, but be aware that
variable_name_in_module
incurs a N dictionary lookup for every call toget_callable_name
where N is the number of variables in the module.Code follows:
Edit: PS:
variable_name_in_module
should probably throw an exception if it can't find anything, though I don't see how that can happen.