How can a function access its own attributes?

前端 未结 16 1794
再見小時候
再見小時候 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 07:54

    Well, let's look at what function is:

    >>> def foo():
    ...     return x
    ... 
    >>> foo.x = 777
    >>> foo.x
    777
    >>> foo()
    Traceback (most recent call last):
      File "", line 1, in 
      File "", line 2, in foo
    NameError: global name 'x' is not defined
    >>> dir(foo)
    ['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', 
    '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', 
    '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 
    'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 
    'func_globals', 'func_name', 'x']
    >>> getattr(foo, 'x')
    777
    

    Aha! So the attribute was added to the function object but it won't see it because it is looking for global x instead.

    We can try to grab the frame of the function execution and try to look what's there (essentially what Anthony Kong suggested but w/o inspect module):

    >>> def foo():
    ...     import sys
    ...     return sys._getframe()
    ... 
    >>> fr = foo()
    >>> dir(fr)
    ['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'f_back', 'f_builtins', 'f_code', 'f_exc_traceback', 'f_exc_type', 'f_exc_value', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_restricted', 'f_trace']
    >>> fr.f_locals
    {'sys': }
    >>> fr.f_code
    ", line 1>
    >>> fr.f_code.co_code
    'd\x01\x00d\x00\x00k\x00\x00}\x00\x00|\x00\x00i\x01\x00\x83\x00\x00S'
    >>> fr.f_code.co_name
    'foo'
    

    Aha! So maybe we can get the name of the function from the name of the code block and then look in round-about way for the attribute? Sure enough:

    >>> getattr(fr.f_globals[fr.f_code.co_name], 'x')
    777
    >>> fr.f_globals[fr.f_code.co_name].x
    777
    >>> def foo():
    ...     import sys
    ...     frm = sys._getframe()
    ...     return frm.f_globals[frm.f_code.co_name].x
    ... 
    >>> foo.x=777
    >>> foo()
    777
    

    That's great! But would it stand the renaming and deletion of original function?

    >>> g = foo
    >>> g.func_name
    'foo'
    >>> g.func_code.co_name
    'foo'
    

    Ah, very doubtful. The function object and its code object still insist they are called foo. Sure enough, here is where it breaks:

    >>> g.x
    777
    >>> g.x=888
    >>> foo.x
    888
    >>> g()
    888
    >>> del foo
    >>> g()
    Traceback (most recent call last):
      File "", line 1, in 
      File "", line 4, in foo
    KeyError: 'foo'
    

    Dang! So in general it can't be done through introspection via the execution frames. The problems seems to be that there is a difference between function object and code object - code objects are what is executed and is just one attribute func_code of the function-object and as such has no access to the func_dict attribute, where our attribute x is:

    >>> g
    
    >>> type(g)
    
    >>> g.func_code
    ", line 1>
    >>> type(g.func_code)
    
    >>> g.func_dict
    {'x': 888}
    

    There is of course other chicanery you can do so that it seems as function - in particular the trick with class definition... but that is not a function per se. It all depends on what do you really need to do with that.

提交回复
热议问题