3

I was reading about __dict__. The author of the article wrote this somewhere in his code.

def func():
    pass

func.temp = 1
print(func.temp)

and I don't understand that. Can functions have attributes? I thought it was only possible when writing classes.

i changed the question a little bit. Thank you for explaining this too....

def func():
    x=1
    def inner_func():
        pass
    

print(func.__dict__) #nothing to input

func.temp=1 #what's it ? Attribute?  
func.x=2   #Why it doesn't change the x inside the func
print()

print(func.__dict__) #Why temp and x are within dict
3
  • 1
    Yes, you can add custom attributes to most Python objects. (Doesn't mean that you necessarily should, though.) Many built-in types are excerpt from this; you can't e.g. add new attributes onto lists or dicts or integers... Commented Jun 2, 2021 at 9:01
  • See the update. Commented Jun 2, 2021 at 13:51
  • Thanks... I've learned a lot. Commented Jun 2, 2021 at 15:54

2 Answers 2

4

In Python most of the stuff consist of a dictionary (have __dict__ attribute), therefore it is possible to (mis-)use the language even in this way.

You can modify a function, because:

def myfunc(): pass

type(myfunc)
# <class 'function'>

a function is still an instance of a function class and part of the builtins (injected, because implemented in C, also available as symtable) and in that you can find __dict__ attribute which is storing the properties.

Similarly you can create an empty class or simply use an object that contains a modifiable __dict__ and by using the "dot" you call in the background:

which then modify it, thus providing you a way to add/remove/modify attrs approximately like this:

object.__dict__["key"]
object.__dict__["key"] = value
del object.__dict__["key"]

Edit: As @MegaIng mentioned, it can be used for various purposes, one of which is functools.lru_cache() to store the cache to remove an expensive function call. (implementation here).

Edit 2: Regarding the changing of a variable within such function - that won't work, because x for you in that case is an attribute of the function stored in __dict__ dictionary. It is not a variable.

Python 3.8.5 (default, Jan 27 2021, 15:41:15) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def myfun(): x=1;print(x)
... 
>>> myfun()
1
>>> 
>>> dir(myfun)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> myfun.__globals__
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'myfun': <function myfun at 0x7fd5594a7670>}

Nothing in these sections, but there's one which contains the value you want to edit and that's __code__ i.e.:

>>> myfun.__code__
<code object myfun at 0x7fd5594afc90, file "<stdin>", line 1>

That you can disassemble - it's already been compiled to Python's "virtual" / emulated CPU's (as if you've taken CPU and its instruction set and abstracted it; converted it into a code) bytecode:

>>> import dis
>>> dis.dis(myfun.__code__)
  1           0 LOAD_CONST               1 (1)
              # here is an assignment of integer `1` into variable `x`
              # via the instruction called `STORE_FAST`
              2 STORE_FAST               0 (x)
              4 LOAD_GLOBAL              0 (print)
              6 LOAD_FAST                0 (x)
              8 CALL_FUNCTION            1
             10 POP_TOP
             12 LOAD_CONST               0 (None)
             14 RETURN_VALUE
>>> 

Now how to modify that? That's already answered in a different question :)

Sign up to request clarification or add additional context in comments.

1 Comment

I would mention that there are good uses for adding an attribute to a function: Most notably a cache.
2

In Python functions are first class objects, which means that function is an object. You can create function using standard syntax:

def foo(x):
  return x+1

or using lambdas:

bar = lambda x: x+1

In both cases foo and bar are object instances, which may have attributes. Therefore you can create new attributes like you can with regular objects.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.