从字符串创建函数时,exec无法正常工作

时间:2019-03-26 18:24:39

标签: python python-3.x properties setter getter

长话短说,我正在对python方法中的一些动态属性注入进行测试。

现在我有一个问题,当我在我的getter和setter字符串上调用exec()将它们转换为动态创建的函数时,它们会保留字符串。

require_once './vendor/autoload.php';

use \Microsoft\Graph\Graph;

$accessToken = "--YOUR-ACCESS-TOKEN-GOES-HERE--";

$graph = new Graph();
$graph->setAccessToken($accessToken);

$user = $graph->createRequest("GET", "/users")
    ->setReturnType(\Microsoft\Graph\Model\User::class)
    ->execute();

因此,基本上在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

在这种情况下,为什么在我调用setattr(cls, name, property(fget=getter, fset=setter, fdel=destructor, doc=docstring)) fnGet之后fnSetexec(fnGet)变量仍然引用get和set函数的字符串表示形式?

2 个答案:

答案 0 :(得分:1)

可以使用__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

答案 1 :(得分:1)

您没有在问题中提供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>)