Python 从字符串创建函数时exec不工作

Python 从字符串创建函数时exec不工作,python,python-3.x,properties,setter,getter,Python,Python 3.x,Properties,Setter,Getter,长话短说,我正在测试python中方法的一些动态属性注入 现在的问题是,当我对getter和setter字符串调用exec()以将它们转换为动态创建的函数时,它们仍然是字符串 def _injectProperties(self): """docstring""" for p in self.InputParameters: index = p.Order name = p.Name.lstrip('@')

长话短说,我正在测试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
因此,基本上在上面的代码中,
\u addprop
是一个函数,它只创建类的一个副本,并将属性设置为:

setattr(cls, name, property(fget=getter, fset=setter, fdel=destructor, doc=docstring))

为什么在这种情况下,
fnGet
fnSet
变量在调用
exec(fnGet)
exec(fnSet)
后仍然引用get和set函数的字符串表示形式?

您可以使用
\uu getattr\uuu
而不是使用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

您可以使用
\uu getattr\uuu
而不是使用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
你没有在你的问题中提供答案。因此,我编写了一些runnable来说明如何做到这一点(尽管我认为@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()
输出:

\u addprop('foobar',)
您没有在问题中提供答案。因此,我编写了一些runnable来说明如何做到这一点(尽管我认为@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()
输出:

\u addprop('foobar',)


将exec更改为setattr(object,funname,anonymous_function)为什么希望
exec
fnGet
fnSet
变量转换为(我假设)函数?顺便说一句,中的
exec
语法在python 3中已被删除。您应该使用
exec(
,)
。但是,只有当字符串变量与函数同名时,才会出现这种情况。在这种情况下,函数定义将覆盖变量。但是您的函数名为
get
/
set
,而不是
fnGet
/
fnSet
@jamiemashall,反正也不需要这么麻烦。您可以定义
\uu getattr\uuuu
来处理未定义的属性,而不是执行添加方法。将exec更改为setattr(object,funname,anonymous_函数)为什么希望
exec
fnGet
fnSet
变量转换为(我假设)函数,Python3中
语法中的
exec
。您应该使用
exec(
,)
。但是,只有当字符串变量与函数同名时,才会出现这种情况。在这种情况下,函数定义将覆盖变量。但是您的函数名为
get
/
set
,而不是
fnGet
/
fnSet
@jamiemashall,反正也不需要这么麻烦。您可以定义
\uu getattr\uuu
来处理未定义的属性,而不是执行添加方法。是的,就是这样,我的语法错误
locals\u dict['get']
是吸引我的部分。在交互式环境中测试时,getter的名称与保存字符串表示的变量的名称相同。在我的测试/问题中,情况并非如此。杰米:很高兴听到它解决了你的问题。还请注意,在使用
exec()
时,如果正在执行的代码创建了您希望在以后检索的任何变量,则通常应向其传递一个空的“locals”字典。这是因为它使用的默认值是
locals()
字典,不应该(不能可靠地)修改它。@JamieMarshall甚至这个答案的作者都认为“@Ned Batchelder可能有更好的建议”。)这是很多你不需要的代码…@NedBatchelder,你的解决方案是非常相关的,但有几个原因我认为这对我的构建来说是优越的。我已经对你的回答发表了评论,想进一步解释。是的,就是这样,我犯了一个愚蠢的语法错误
locals\u dict['get']
是吸引我的部分。在交互式环境中测试时,getter的名称与保存字符串表示的变量的名称相同。在我的测试/问题中,情况并非如此。杰米:很高兴听到它解决了你的问题。还请注意,在使用
exec()
时,如果正在执行的代码创建了您希望在以后检索的任何变量,则通常应向其传递一个空的“locals”字典。这是因为它使用的默认值是
locals()
字典,不应该(不能可靠地)修改它。@JamieMarshall甚至这个答案的作者都认为“@Ned Batchelder可能有更好的建议”。)这是很多你不需要的代码…@NedBatchelder,你的解决方案是非常相关的,但有几个原因我认为这对我的构建来说是优越的。我已经对您的回答进行了评论,以进一步解释。我对该解决方案的担忧是intellisense不会获取属性,因此我团队中的其他程序员必须在self.InputParameters列表中的对象可用之前知道其内容。此外,这是附加到对象的,所以如果我想跨多个类使用注入,它就不是特别模块化的。这就是我将@martineau的答案标记为“答案”的原因。我可以很容易地将代码放入助手类中,并在我不想注入属性的任何其他类上调用它。我对该解决方案的担心是intellisense不会获取属性,因此我团队中的其他程序员必须在self.InputParameters列表中的对象内容可用之前知道它的内容。此外,这是附加到对象,因此它不是特别模块