【问题标题】:Python - Execute getter chains from stringPython - 从字符串执行 getter 链
【发布时间】:2021-06-24 19:20:18
【问题描述】:
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 字符串中的方法暂时不会有任何参数,以防止这变得不必要的复杂。

【问题讨论】:

  • 您需要做一些解析来检测[0] 并将其转换为索引而不是属性getter。

标签: python python-3.x getter chaining method-chaining


【解决方案1】:

让我们以属性树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,所以可能有更简单的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-09
    • 1970-01-01
    • 2013-05-07
    • 2011-07-27
    • 2016-09-07
    • 2015-11-08
    • 2023-03-28
    • 1970-01-01
    相关资源
    最近更新 更多