Reputation:
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
Upvotes: 3
Views: 294
Reputation: 12189
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 :)
Upvotes: 4
Reputation: 689
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.
Upvotes: 2