1

I was going through classes doc. And I found the following paragraph.

Notice that code passed to exec() or eval() does not consider the classname of the invoking class to be the current class; this is similar to the effect of the global statement, the effect of which is likewise restricted to code that is byte-compiled together. The same restriction applies to getattr(), setattr() and delattr(), as well as when referencing __dict__ directly.

I have doubts regarding the 'byte-compiled restriction'. I understand exec creates a new environment for code execution where locals and globals are shared to it. And the restriction for global keyword makes sense to me through the following code.

x = 2
def fun():
 exec('global x')
 x = 3
 print("Local - ", x)
fun()
print("Global - ", x)
#Output
Local - 3
Global - 2

The exec statement above will not convert the x inside the function to global.

However, for setattr, getattr and delattr, why it will be affected? Since locals and globals are shared with the exec and getattr and other commands deal will objects, why would they be restricted?

class cl:
 def nest_fun(self):
 exec("setattr(cl, 'var', 2)")
 print(cl.var)
cl().nest_fun()
#Output
2

Maybe there's something in setattr and other commands that I'm missing?

asked Aug 11, 2025 at 10:37

1 Answer 1

0

This is about name mangling in class definitions.

Consider this example :

class MyClass:
 __class_var = 2 # Mangled to _MyClass__class_var
 exec("__exec_var = 1") # NOT mangled—sets literal '__exec_var'
print(MyClass.__dict__.keys())
# Output
# dict_keys(['__module__', '__dict__', ..., '_MyClass__class_var', '__exec_var', ...])

The key __exec_var in MyClass is executed not in the context of the class, so no mangling.

That's why this code would not work :

class MyClass:
 __class_var = 2
 def method(self):
 # '__class_var' isn't mangled here
 exec("print(self.__class_var)")
MyClass().method()
Output :
# AttributeError: 'MyClass' object has no attribute '__class_var'

A direct print(self.__class_var) in the method would be mangled (to _MyClass__class_var) at compile time and work.

In the end, what about getattr ?

Consider this code :

class MyClass:
 __class_var = 2 # Sets '_MyClass__class_var'
 def get_direct(self):
 return self.__class_var # Mangled
 def get_via_getattr(self):
 return getattr(self, '__class_var') # No mangling, AttributeError
obj = MyClass()
print(obj.get_direct())
print(obj.get_via_getattr())
# Output
# 2
# AttributeError: 'MyClass' object has no attribute '__class_var'

getattr(self, '__class_var') doesn't work,
however if you followed me, you should now understand that : getattr(self, '_MyClass__class_var') works (but please never do that).

And last but not least working example code :

class MyClass:
 __class_var = 2 # Sets '_MyClass__class_var'
 exec("__exec_var=2")
 def get_direct(self):
 return self.__class_var # Mangled
 def get_exec_var_via_getattr(self):
 return getattr(self, '__exec_var')
obj = MyClass()
print(obj.get_direct())
print(obj.get_exec_var_via_getattr())

I let you guess the output!

What to remember?

  1. Essentially, these restrictions are here as a "warning" for class definitions involving name mangling. exec (and eval) expressions are not compiled at the same time.
  2. getattr/setattr/delattr are "restricted" similarly because they use runtime strings and bypass mangling entirely as you seen on the examples above. (your example works because it's runtime, non-mangled, and outside the class body)

bonus : test this code (but never do that please, it's just for the example)

class MyClass:
 __class_var = 2
 def method(self):
 exec("print(self._MyClass__class_var)")
MyClass().method()
answered Aug 11, 2025 at 15:37
Sign up to request clarification or add additional context in comments.

6 Comments

Hey @Danielc-n, thanks for the answer. It gave me a headache but now things are clear. I have few doubts. 1. Is self.__class_var converted to its mangled name at compile time? And for getattr it is not the case, as it is left to be executed dynamically? 2. I thought the restriction was in using setattr and other commands inside exec. But as you told and I want to confirm, getattr, setattr are themselves dynamic commands and are not touched by compiler, is it?
1. Yes self.__class_var is mangled (as you can see on my first example (in the keys of the __dict__) -- and Yes, the names in getattr are taken "as is" so it'll look into MyClass.__dict__ with the raw given name. --------- 2. Correct, all 3 getattr, setattr and delattr are dynamic; runtime functions. --------- See exec like a vehicle that "protects" variables from the outside, the passengers will behave exactly like people outside the vehicle, it's just that they are in a different environment. Being in the car "protects" them from being altered.
I have few more doubts. Bear with me. 1. I think that the sentence is not only about name mangling, for e.g. - super command also doesn't work inside exec. Is there any general statement the sentence is talking about? ----------- 2. The first sentence which talks about global and exec. Does it mention two different types of restrictions? For exec it is that it cannot see the context. And for global it is that it can't work inside exec? And which of these two restrictions is for setattr? Sorry, if it feels that I am asking similar questions. But, I can't seem to grasp it.
I think I grasped it. Can you confirm if the following reframing of the original paragraph is correct? Notice that code passed to exec() or eval() does not consider the classname of the invoking class to be the current class, so name_mangling, super and other things don't work inside it; this is similar to the effect of the global statement, the effect of which like name mangling and super is restricted to code that is byte-compiled together. The same restriction is also applied when in getattr(), setattr() and delattr(), as well as when referencing __dict__ directly.
You're correct! And your analogy with the global statement is spot on! Except that you mention 'super' which is another little problem. Maybe, if you want, I can edit my answer to add a little paragraph on that.
Yeah, please. More clarity is always welcomed!

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.