2015-10-20 63 views
0

我有以下幾點:Python的GETATTR調用函數

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

不沒有很大的痛苦。你爲什麼想這樣做?字符串從哪裏來?這可能是更好的方法(換句話說,這是一個[XY問題](http://xyproblem.info/))。 –

+0

@DanielRoseman我的用例:保存任何模型時,我需要一個用戶列表,每個模型都有一組不同的用戶可以更改。我曾經將用戶作爲「queryset」傳遞給用戶,但是現在我正在使用代理,這是在另一臺機器上完成的,因此我現在需要每個模型來簡單告訴我如何查詢數據庫以獲取其用戶基礎。 – MrKnotts

+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'} 
+0

對不起,我還沒有看到評價是否意味着評價? – MrKnotts

+1

不,「評估」是我在答案中定義的一個函數。使用'eval'(或'exec')是不好的,因爲它可以在沒有任何控制的情況下執行任意代碼,而通過像'evaluate'這樣的自定義函數,您可以完全控制您允許的操作以及如何執行它們。 – poke

+0

好吧,對不起,我認爲我必須把你的代碼包裹一下,讓我感到困惑,因爲你返回''evaluate(expr.value,ref)''即它本身。 – MrKnotts

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實現讓人想起。

+0

有趣的是,在這個例子中,爲什麼這會很危險?也爲什麼''.__ dict__''? – MrKnotts