如何解决Python - 从字符串执行 getter 链
getter_string = 'getName().attr.full_name()[0]'
如何将上面给定的 getter 字符串应用于任何对象?
我需要一个函数 f
,这样 f(obj,getter_string)
会返回 f.getName().attr.full_name()[0]
我看过 Python Chain getattr as a string 但它似乎只用于链接属性。我希望链接方法和索引。
我知道这可以通过编写一个仔细处理所有情况的解析器来完成,但是有没有更紧凑的方法来做到这一点?
可以安全地假设 getter 字符串中的方法暂时没有任何参数,以防止这变得不必要地复杂。
解决方法
让我们以属性树 getName().attr.full_name()[0]
为例。
首先,我们需要创建一个拥有这棵树的虚拟对象:
class Dummy:
def __init__(self,value):
self.value = value
class A:
def __init__(self,value):
self.value = value
def full_name(self):
return [self.value]
class B:
def __init__(self,value):
self.value = value
@property
def attr(self):
return Dummy.A(self.value)
def getName(self):
return Dummy.B(self.value)
要创建 Dummy
对象,您必须向其构造函数传递一个值。访问属性树时将返回此值:
obj = Dummy(3.14)
print(obj.getName().attr.full_name()[0])
# Outputs 3.14
我们将仅使用 Dummy
来证明我们的代码正在运行。我假设您已经有一个具有此属性树的对象。
现在,您可以使用 ast
模块来解析 getter-string。在这种情况下,我考虑 getter-string 只包含属性、方法和索引:
import ast
def parse(obj,getter_str):
# Store the current object for each iteration. For example,# - in the 1st iteration,current_obj = obj
# - in the 2nd iteration,current_obj = obj.getName()
# - in the 3rd iteration,current_obj = obj.getName().attr
current_obj = obj
# Store the current attribute name. The ast.parse returns a tree that yields
# - a ast.Subscript node when finding a index access
# - a ast.Attribute node when finding a attribute (either property or method)
# - a ast.Attribute and a ast.Call nodes (one after another) when finding a method
#
# Therefore,it's not easy to distinguish between a method and a property.
# We'll use the following approach for each node:
# 1. if a node is a ast.Attribute,save its name in current_attr
# 2. if the next node is a ast.Attribute,the current_attr is an attribute
# 3. otherwise,if the next node is a ast.Call,the current_attr is a method
current_attr = None
# Parse the getter-string and return only
# - the attributes (properties and methods)
# - the callables (only methods)
# - the subscripts (index accessing)
tree = reversed([node
for node in ast.walk(ast.parse('obj.' + getter_str))
if isinstance(node,(ast.Attribute,ast.Call,ast.Subscript))])
for node in tree:
if isinstance(node,ast.Call):
# Method accessing
if current_attr is not None:
current_obj = getattr(current_obj,current_attr)()
current_attr = None
elif isinstance(node,ast.Attribute):
# Property or method accessing
if current_attr is not None:
current_obj = getattr(current_obj,current_attr)
current_attr = node.attr
elif isinstance(node,ast.Subscript):
# Index accessing
current_obj = current_obj[node.slice.value.value]
return current_obj
现在,让我们创建一个 Dummy
对象,看看当使用给定的属性树调用 parse
时,它是否会返回在其构造函数中传递的值:
obj = Dummy(2.71)
print(parse(obj,'getName().attr.full_name()[0]'))
# Outputs 2.71
所以 parse
函数能够正确解析给定的属性树。
我不熟悉 ast
,所以可能有更简单的方法。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。