首页 > 解决方案 > 通过读取 XML 文件的内容创建 lambda 函数

问题描述

我需要在 Python 中基于 XML 内容动态创建 lambda 函数,以便过滤掉一些数据。

以下是 lambda 函数的条件如何以 XML 格式排列的示例:

<conditions>
    <or>
        <and>
              <eq>
                 <field>is_hazardous</field>
                 <literal>1</literal>
              </eq>
              <eq>
                 <field>is_refrigeration</field>
                 <literal>0</literal>
              </eq>
              <not-eq>
                 <field>year</field>
                 <literal>2005</literal>
              </not-eq>>
              <not-eq>
                 <field>material</field>
                 <literal>plastic</literal>
              </not-eq>>
        </and>
        <eq>
             <field>is_active</field>
             <literal>1</literal>
        </eq>>
    </or>
</conditions>

这是一组 CSV 格式的数据:

is_hazardous,is_refrigeration,year,material,is_active
1,0,2006,metal,1
1,0,2005,metal,0

我需要实现的是:

def eval_data(x):
    return (
                   (
                       (x['is_hazardous'] == 1) and
                       (x['is_refrigeration'] == 0) and
                       (x['year'] != 2005) and (x['material'] != 'plastic'))
                or (x['is_active'] == 1)
           )

df = pd.read_csv('sample.csv')
df['check'] = df.apply(lambda x: eval_data(x),
                       axis = 1)

此函数应为第一行输出True,为第二行输出False

如何通过读取 XML 文件的内容动态创建eval_data函数的返回表达式?

标签: pythonxmllambda

解决方案


这是一个相当有趣的问题。我已经实现了 eq 和 or 作为示例,您可以实现 neq 和 and。

import xml.etree.ElementTree as ET
from typing import List


class Evaluable:
    def eval(self, *args, **kwargs):
        """ returns the outcome of the operation """
        raise NotImplemented


class OR(Evaluable):
    def __init__(self, n):
        self.children: List[Evaluable] = [NODE.parse(child) for child in n]

    def eval(self, *args, **kwargs):
        """ performs or operation for the children """
        return any([c.eval(*args, **kwargs) for c in self.children])


class EQ(Evaluable):
    def __init__(self, n):
        # parse field name
        self.field = n.find("field")
        if self.field is None:
            raise ValueError("field not found")
        self.field = self.field.text

        # parse literal value as str
        self.literal = n.find("literal")
        if self.literal is None:
            raise ValueError("literal not found")
        self.literal = self.literal.text

    def eval(self, *args, evaluation_params: dict = None, **kwargs):
        """ performs eq operation for the children """
        return evaluation_params[self.field] == self.literal


class NODE(Evaluable):
    @classmethod
    def parse(cls, n) -> Evaluable:
        if n.tag == "eq":
            return EQ(n)
        if n.tag == "or":
            return OR(n)


sample_xml = """<conditions>
    <or>
        <or>
              <eq>
                 <field>is_hazardous</field>
                 <literal>1</literal>
              </eq>
              <eq>
                 <field>is_refrigeration</field>
                 <literal>0</literal>
              </eq>
              <eq>
                 <field>year</field>
                 <literal>2005</literal>
              </eq>>
              <eq>
                 <field>material</field>
                 <literal>plastic</literal>
              </eq>>
        </or>
        <eq>
             <field>is_active</field>
             <literal>1</literal>
        </eq>>
    </or>
</conditions>"""

root = ET.fromstring(sample_xml)
node = NODE.parse(root[0])
parser = lambda x: node.eval(evaluation_params=x)
print(parser({"is_hazardous": "1",
              "is_refrigeration": "0",
              "year": "2006",
              "material": "metal",
              "is_active": "1"}))

推荐阅读