is it possible to access the python function object attributes from within the function scope?
e.g. let\'s have
def f():
return
This uses a bit of a hackish approach, but it's possibly the most correct so far given that it works with the g()
call as well. It works because it's relying on whatever bytecode inspection is performed by the dis module, as a shortcut.
It looks more hackish than it really is partly because the dis.disassemble()
call prints to stdout, so I redirect that into a StringIO. I use disassemble()
for its feature of highlighting the last instruction (add a print text
line in there to see how it looks) and that makes it easier to grab the previous LOAD_NAME
and the variable it used.
It would be possible to use a cleaner bytecode inspection library to do this without using the dis
module at all, but this proves that it's possible. This might not be the most robust approach, but then again maybe it will work in most cases. I haven't spent enough time poking into Python internals or bytecode to know whether most CALL_FUNCTION
bytecodes are preceded immediately by instructions that the regex trick would pick out.
import inspect
import dis
import re
import sys
import StringIO
def f():
caller = inspect.stack()[1][0]
sys.stdout = StringIO.StringIO()
dis.disassemble(caller.f_code, caller.f_lasti)
text = sys.stdout.getvalue()
sys.stdout = sys.__stdout__
match = re.search(r'LOAD_NAME.*\((.*?)\)\s+-->', text)
name = match.group(1)
try:
func = caller.f_locals[name]
except KeyError:
func = caller.f_globals[name]
return func._x
f._x = 'foo'
print 'call f():', f()
g = f
del f
print 'call g():', g()
This generates the following output:
call f(): foo
call g(): foo
How about using a class instead of a function and abusing the __new__
method to make the class callable as a function? Since the __new__
method gets the class name as the first parameter, it can access all the class attributes
like in
class f(object):
def __new__(cls, x):
print cls.myattribute
return x
this works as in
f.myattribute = "foo"
f(3)
foo
3
then you can do
g=f
f=None
g(3)
foo
3
The issue is that even if the object behaves like a function, it is not. Hence IDEs fail to provide you with the signature.
I like this alot.
from functools import update_wrapper
def dictAsGlobals(f):
nf = type(f)(f.__code__, f.__dict__, f.__name__, f.__defaults__, f.__closure__)
try: nf.__kwdefaults__ = f.__kwdefaults__
except AttributeError: pass
nf.__dict__ = f.__dict__
nf.__builtins__ = f.__globals__["__builtins__"]
return update_wrapper(nf, f)
@dictAsGlobals
def f():
global timesCalled
timesCalled += 1
print(len.__doc__.split("\n")[0])
return factor0 * factor1
vars(f).update(timesCalled = 0, factor0 = 3, factor1 = 2)
print(f())
print(f())
print(f.timesCalled)
Sorry for the late reply but I just stumbled upon this. I would have to argue that the way that “g” is asked to work is non-Pythonic. Inside function, the name “f“ refers to the value of a global variable at the time the function is called. Given that, consider the following:
def f():
print(f)
f, g = 42, f
g() # prints 42
del f
g() # raises an exception
Hopefully, no one argues that this is incorrect behavior. Given that fact, I cam only vote for any answer that requires the use of a different variable name (e.g. “self”) inside the function.