How can a function access its own attributes?

前端 未结 16 1740
再見小時候
再見小時候 2020-11-28 07:49

is it possible to access the python function object attributes from within the function scope?

e.g. let\'s have

def f():
    return          


        
相关标签:
16条回答
  • 2020-11-28 08:08

    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
    
    0 讨论(0)
  • 2020-11-28 08:11

    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.

    0 讨论(0)
  • 2020-11-28 08:13

    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)
    
    0 讨论(0)
  • 2020-11-28 08:14

    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.

    0 讨论(0)
提交回复
热议问题