Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is __code__ for a function(Python) mutable

Tags:

In a previous question yesterday, in comments, I came to know that in python __code__ atrribute of a function is mutable. Hence I can write code as following

def foo():     print "Hello"  def foo2():     print "Hello 2"  foo() foo.__code__ = foo2.__code__ foo() 

Output

Hello Hello 2 

I tried googling, but either because there is no information(I highly doubt this), or the keyword (__code__) is not easily searchable, I couldn't find a use case for this.

It doesn't seem like "because most things in Python are mutable" is a reasonable answer either, because other attributes of functions — __closure__ and __globals__ — are explicitly read-only (from Objects/funcobject.c):

static PyMemberDef func_memberlist[] = {     {"__closure__",   T_OBJECT,     OFF(func_closure),      RESTRICTED|READONLY},     {"__doc__",       T_OBJECT,     OFF(func_doc), PY_WRITE_RESTRICTED},     {"__globals__",   T_OBJECT,     OFF(func_globals),      RESTRICTED|READONLY},     {"__module__",    T_OBJECT,     OFF(func_module), PY_WRITE_RESTRICTED},     {NULL}  /* Sentinel */ }; 

Why would __code__ be writable while other attributes are read-only?

like image 537
Akshat Harit Avatar asked Jul 23 '15 22:07

Akshat Harit


People also ask

Are Python functions mutable?

Giving your functions defaults to work off ofObjects of built-in types like int , float , bool , str , tuple , and Unicode are immutable. Objects of built-in types like list , set , and dict are mutable. A mutable object can change its state or contents, whereas immutable objects cannot.

Are lists mutable in Python?

So, lists are pretty unique. They are mutable. 00:33 Once you create it, elements can be modified, individual values can be replaced, even the order of the elements can be changed. And lists are also dynamic, meaning that you can add elements to the list or remove elements from a list completely.

Can a mutable object be passed a parameter to a function?

The object and the calling environment is unaffected by anything we might do to it inside the function. If you pass a mutable object, like a list or a dictionary, it's like pass-by-reference. Again, you can't reassign the parameter to something else, but you can modify the object that you get.

Is none mutable in Python?

None is not mutable. The problem occurs when you modify the object that the parameter has as a default.


1 Answers

The fact is, most things in Python are mutable. So the real question is, why are __closure__ and __globals__ not?

The answer initially appears simple. Both of these things are containers for variables which the function might need. The code object itself does not carry its closed-over and global variables around with it; it merely knows how to get them from the function. It grabs the actual values out of these two attributes when the function is called.

But the scopes themselves are mutable, so this answer is unsatisfying. We need to explain why modifying these things in particular would break stuff.

For __closure__, we can look to its structure. It is not a mapping, but a tuple of cells. It doesn't know the names of the closed-over variables. When the code object looks up a closed-over variable, it needs to know its position in the tuple; they match up one-to-one with co_freevars which is also read-only. And if the tuple is of the wrong size or not a tuple at all, this mechanism breaks down, probably violently (read: segfaults) if the underlying C code isn't expecting such a situation. Forcing the C code to check the type and size of the tuple is needless busy-work which can be eliminated by making the attribute read-only. If you try to replace __code__ with something taking a different number of free variables, you get an error, so the size is always right.

For __globals__, the explanation is less immediately obvious, but I'll speculate. The scope lookup mechanism expects to have access to the global namespace at all times. Indeed, the bytecode may be hard-coded to go straight to the global namespace, if the compiler can prove no other namespace will have a variable with a particular name. If the global namespace was suddenly None or some other non-mapping object, the C code could, once again, violently misbehave. Again, making the code perform needless type checks would be a waste of CPU cycles.

Another possibility is that (normally-declared) functions borrow a reference to the module's global namespace, and making the attribute writable would cause the reference count to get messed up. I could imagine this design, but I'm not really sure it's a great idea since functions can be constructed explicitly with objects whose lifetimes might be shorter than that of the owning module, and these would need to be special-cased.

like image 175
Kevin Avatar answered Sep 19 '22 18:09

Kevin