I'm trying to create static variables in a class with exec and Comprehensions list:
class DictTranslator:
def __init__(self):
self.create_static_variables()
def get_value(self, key):
return self.dict_.get(key)
def create_static_variables(self):
[exec("self." str(value) " = " str(key)) for key, value in self.dict_.items()]
class AppImportanceTranslator(DictTranslator):
dict_ = {
-1: 'Unknown',
0: 'Gone',
1: 'Foreground',
2: 'ForegroundService'
}
but I've this error: NameError: name 'self' is not defined
but I can change the code to:
class DictTranslator:
def __init__(self):
self.create_static_variables()
def get_value(self, key):
return self.dict_.get(key)
def create_static_variables(self):
for key, value in self.dict_.items():
exec("self." str(value) " = " str(key))
class AppImportanceTranslator(DictTranslator):
dict_ = {
-1: 'Unknown',
0: 'Gone',
1: 'Foreground',
2: 'ForegroundService'
}
And this code is correct.
Any idea?
My python version is 3.8.10
CodePudding user response:
First of all:
Don't use list comprehensions for side effects. You are creating a list object with
None
references, then discarding it again. That's a waste of computer resources.Always use a
for
loop if all you needed was a loop.You can set attributes on
self
with thesetattr()
function.exec()
should be a last resort, as it opens you to all sorts of performance and security issues.
You can use:
for key, value in self.dict_.items():
setattr(self, value, str(key))
to achieve the same thing.
Alternatively, give your class a __getattr__
hook and have it look up attributes lazily:
def __getattr__(self, name):
try:
return str(self.dict_[name])
except KeyError:
# raise an AttributeError instead
raise AttributeError(name) from None
The __getattr__
hook is called for any attribute not explicitly set on your instance.
Now, for what actually goes wrong:
exec()
can't see self
because a list comprehension is a new scope, as if you ran:
def create_static_variables(self):
def inner_function():
for key, value in self.dict_.items():
exec("self." str(value) " = " str(key))
inner_function()
At compile time, Python can see the self
in self.dict_.items()
and bind it as a closure (taking it from the local namespace of the create_static_variables()
call. But exec()
doesn't run at compile time and so can only see globals or locals.
You could use exec(..., globals(), {"self": self})
to pass along the self
binding in a new local namespace, to work around that.