Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a function have an attribute?

Tags:

python

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

2 Answers

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:

  • getattr(object, name[, default])
  • setattr(object, name, value) (for assignment)
  • delattr(object, name) (for deletion)

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 :)

like image 77
Peter Badida Avatar answered May 16 '26 10:05

Peter Badida


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.

like image 20
pgorecki Avatar answered May 16 '26 10:05

pgorecki



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!