从字符串创建函数时 exec 不工作
exec not working when creating functions from string
长话短说,我正在 python 中的方法上测试一些动态 属性 注入。
现在我有一个问题,当我在我的 getter 和 setter 字符串上调用 exec() 将它们变成动态创建的函数时,它们仍然是字符串。
def _injectProperties(self):
"""docstring"""
for p in self.InputParameters:
index = p.Order
name = p.Name.lstrip('@')
pName = p.Name
fnGet = (
"def get(self): \n"
" rtnVal = [p.Value for p in self.InputParameters "
" if p.Name == '{0}'][0] \n"
" return rtnVal ").format(p.Name)
fnSet = (
"def set(self, value): \n"
" prop = [p for p in self.InputParameters "
" if p.Name == '{0}'][0] \n"
" prop.Value = value \n"
" return ").format(p.Name)
exec(fnGet) in locals()
exec(fnSet) in locals()
self._addprop(name, fnGet, fnSet)
return
所以基本上在上面的代码中 _addprop
是一个简单地创建 class 副本并为其设置 属性 的函数:
setattr(cls, name, property(fget=getter, fset=setter, fdel=destructor, doc=docstring))
为什么在这种情况下,在我调用 exec(fnGet)
和 exec(fnSet)
之后,fnGet
和 fnSet
变量仍然引用 get 和 set 函数的字符串表示形式?
您可以使用 __getattr__
而不是使用 exec 来注入属性。当缺少属性时调用它。
我认为这可以满足您的需求:
def __getattr__(self, attr):
for p in self.InputParameters:
if p.Name == attr:
return p.Value
def __setattr__(self, attr, value):
for p in self.InputParameters:
if p.Name == attr:
p.Value = value
break
您没有在问题中提供 MCVE。所以我做了一些可运行的东西来说明你如何做到这一点(尽管我认为@Ned Batchelder 可能有更好的建议)。
请注意,这也显示了我认为嵌入函数源代码的更好方法。
from textwrap import dedent
class InputParameter: # Mock for testing
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
class Class:
def __init__(self):
self.InputParameters = [ # For testing
InputParameter(Order=42, Name='@foobar'),
]
def _addprop(self, name, getter, setter):
print('_addprop({!r}, {}, {})'.format(name, getter, setter))
def _injectProperties(self):
"""docstring"""
for p in self.InputParameters:
index = p.Order
name = p.Name.lstrip('@')
pName = p.Name
fnGet = dedent("""
def get(self):
rtnVal = [p.Value for p in self.InputParameters
if p.Name == '{0}'][0]
return rtnVal
""").format(p.Name)
fnSet = dedent("""
def set(self, value):
prop = [p for p in self.InputParameters
if p.Name == '{0}'][0]
prop.Value = value
return
""").format(p.Name)
locals_dict = {}
exec(fnGet, globals(), locals_dict)
exec(fnSet, globals(), locals_dict)
self._addprop(name, locals_dict['get'], locals_dict['set'])
return
cls = Class()
cls._injectProperties()
输出:
_addprop('foobar', <function get at 0x00270858>, <function set at 0x00572C00>)
长话短说,我正在 python 中的方法上测试一些动态 属性 注入。
现在我有一个问题,当我在我的 getter 和 setter 字符串上调用 exec() 将它们变成动态创建的函数时,它们仍然是字符串。
def _injectProperties(self):
"""docstring"""
for p in self.InputParameters:
index = p.Order
name = p.Name.lstrip('@')
pName = p.Name
fnGet = (
"def get(self): \n"
" rtnVal = [p.Value for p in self.InputParameters "
" if p.Name == '{0}'][0] \n"
" return rtnVal ").format(p.Name)
fnSet = (
"def set(self, value): \n"
" prop = [p for p in self.InputParameters "
" if p.Name == '{0}'][0] \n"
" prop.Value = value \n"
" return ").format(p.Name)
exec(fnGet) in locals()
exec(fnSet) in locals()
self._addprop(name, fnGet, fnSet)
return
所以基本上在上面的代码中 _addprop
是一个简单地创建 class 副本并为其设置 属性 的函数:
setattr(cls, name, property(fget=getter, fset=setter, fdel=destructor, doc=docstring))
为什么在这种情况下,在我调用 exec(fnGet)
和 exec(fnSet)
之后,fnGet
和 fnSet
变量仍然引用 get 和 set 函数的字符串表示形式?
您可以使用 __getattr__
而不是使用 exec 来注入属性。当缺少属性时调用它。
我认为这可以满足您的需求:
def __getattr__(self, attr):
for p in self.InputParameters:
if p.Name == attr:
return p.Value
def __setattr__(self, attr, value):
for p in self.InputParameters:
if p.Name == attr:
p.Value = value
break
您没有在问题中提供 MCVE。所以我做了一些可运行的东西来说明你如何做到这一点(尽管我认为@Ned Batchelder 可能有更好的建议)。
请注意,这也显示了我认为嵌入函数源代码的更好方法。
from textwrap import dedent
class InputParameter: # Mock for testing
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
class Class:
def __init__(self):
self.InputParameters = [ # For testing
InputParameter(Order=42, Name='@foobar'),
]
def _addprop(self, name, getter, setter):
print('_addprop({!r}, {}, {})'.format(name, getter, setter))
def _injectProperties(self):
"""docstring"""
for p in self.InputParameters:
index = p.Order
name = p.Name.lstrip('@')
pName = p.Name
fnGet = dedent("""
def get(self):
rtnVal = [p.Value for p in self.InputParameters
if p.Name == '{0}'][0]
return rtnVal
""").format(p.Name)
fnSet = dedent("""
def set(self, value):
prop = [p for p in self.InputParameters
if p.Name == '{0}'][0]
prop.Value = value
return
""").format(p.Name)
locals_dict = {}
exec(fnGet, globals(), locals_dict)
exec(fnSet, globals(), locals_dict)
self._addprop(name, locals_dict['get'], locals_dict['set'])
return
cls = Class()
cls._injectProperties()
输出:
_addprop('foobar', <function get at 0x00270858>, <function set at 0x00572C00>)