我有以下内容:
instance = Service.objects.get(pk=1)
attr = "members.all"
t = functools.reduce(getattr, attr.split("."), instance)()
print(t)
评估为instance.members.all()
使用Python 3.x如何使用相同的方法评估以下内容?
attr = "members.filter(gender='Male')"
评估为instance.members.filter(gender='Male')
答案 0 :(得分:4)
您应解析表达式并使用语法树来解决此问题。以下是与示例表达式一起使用的不完整示例。您可以将其展开以支持更多语法结构:
import ast
def evaluate (expr, ref):
if isinstance(expr, ast.Module):
return evaluate(expr.body[0], ref)
elif isinstance(expr, ast.Expr):
return evaluate(expr.value, ref)
elif isinstance(expr, ast.Call):
args = [evaluate(x, ref) for x in expr.args]
kwargs = {kw.arg: evaluate(kw.value, ref) for kw in expr.keywords}
return evaluate(expr.func, ref)(*args, **kwargs)
elif isinstance(expr, ast.Attribute):
return getattr(evaluate(expr.value, ref), expr.attr)
elif isinstance(expr, (ast.Str, ast.Num)):
return ast.literal_eval(expr)
# special case; look up names on the reference object
elif isinstance(expr, ast.Name):
return getattr(ref, expr.id)
else:
print('Unknown type', type(expr))
你可以像这样使用它:
expr = "members.filter(gender='Male')"
evaluate(ast.parse(expr), referenceObject)
举个例子,我创建了以下虚拟对象:
>>> def debug (*args, **kwargs):
print(args, kwargs)
>>> from types import SimpleNamespace
>>> ref = SimpleNamespace()
>>> ref.members = SimpleNamespace()
>>> ref.members.filter = debug
>>> evaluate(ast.parse("members.filter(gender='Male')"), ref)
() {'gender': 'Male'}
答案 1 :(得分:1)
以下是一种可能的方法,使用exec()
:
class Members():
def filter(self, gender):
print("Gender is", gender)
class Foo():
def __init__(self):
self.members = Members()
>>> instance = Foo()
>>> exec("members.filter(gender='Male')", instance.__dict__)
Gender is Male
请注意,将exec
与未经验证的输入结合使用会很危险,但有些地方可能会有用。
编辑 - 一些额外的解释:
当您调用exec
时,您传递一个字典,该字典提供将在其中执行代码的上下文或命名空间。上面我传递instance.__dict__
,以便代码将在此命名空间中执行:即members
存在的命名空间
如果字符串来自未知来源,则在Python中使用exec
会很危险:这意味着您正在执行任意(可能是恶意的)代码。这是经常建议不要使用它的一个原因;但是有些地方可以有效地使用它:想到Python标准库中的NamedTuple实现。