让我们以属性树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 字符串。在这种情况下,我认为 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,所以可能有更简单的方法。