跳转到主要内容

功能分析描述语言基础包

项目描述

func_adl

使用Python中类似于SQL的概念构建层次数据查询。

GitHub Actions Status Code Coverage

PyPI version Supported Python versions

func_adl 使用类似SQL的语言,并从ROOT文件或ATLAS xAOD文件中提取数据和计算值,并以列格式返回。它目前被用作ServiceX转换器的一部分。

这是一个具有后端无关代码以查询层次数据的基包。很可能会安装以下包之一

  • func_adl_xAOD: 用于在ServiceX中运行的ATLAS & CMS实验xAOD文件
  • func_adl_uproot: 用于运行平面root文件
  • func_adl.xAOD.backend: 用于使用Docker在本地文件上运行

有关每个后端可能的表达式和功能的信息,请参阅文档。

捕获的变量

Python支持在lambda值和函数中支持闭包。此库将在调用select方法时解析这些闭包。例如(其中ds是数据集)

met_cut = 40
good_met_expr = ds.Where(lambda e: e.met > met_cut).Select(lambda e: e.met)
met_cut = 50
good_met = good_met_expr.value()

裁剪将在40处应用,因为当调用Where函数时met_cut的值是40。这也适用于函数内部捕获的变量。

语法糖

有几个Python表达式和习惯用法在您的背后被转换为func_adl。请注意,这些必须位于ObjectStream方法之一的lambda函数内部,例如SelectSelectManyWhere

名称 Python表达式 func_adl 翻译
列表推导式 [j.pt() for j in jets] jets.Select(lambda j: j.pt())
列表推导式 [j.pt() for j in jets if abs(j.eta()) < 2.4] jets.Where(lambda j: abs(j.eta()) < 2.4).Select(lambda j: j.pt())
列表推导式 [j.pt()+e.pt() for j in jets for e in electrons] jets.Select(lambda j: electrons.Select(lambda e: j.pt()+e.pt()))

注意:对于列表推导式有效的一切也适用于生成器表达式。

可扩展性

有两个可扩展点

  • EventDataset 应该被派生以提供执行器。
  • EventDataset 可以使用 Python 的类型提示系统,允许编辑器和其他智能输入系统对表达式进行类型检查。类型数据越多,系统可以帮助得越多。
  • 定义一个可以在 LINQ 表达式中调用的函数
  • 定义新的流方法
  • 可以在函数或方法调用点插入回调,这将允许修改 ObjectStream 或调用点的 ast

EventDataSet

一个 EventDataSet 的示例

class events(EventDataset):
    async def execute_result_async(self, a: ast.AST, title: Optional[str] = None):
        await asyncio.sleep(0.01)
        return a

以及一些使用它的 func_adl 代码

r = (events()
        .SelectMany(lambda e: e.Jets('jets'))
        .Select(lambda j: j.eta())
        .value())
  • 当调用 .value() 方法时,将调用带有表示查询的完整 astexecute_result_async。这是将查询发送到后端进行实际处理的地方。
  • 通常,events 的构造函数将接受要处理的数据集名称,然后可以在 execute_result_async 中使用。

类型化 EventDataset

上述声明的一个小变化,查询没有变化

class dd_jet:
    def pt(self) -> float:
        ...

    def eta(self) -> float:
        ...

class dd_event:
    def Jets(self, bank: str) -> Iterable[dd_jet]:
        ...
    
    def EventNumber(self, bank='default') -> int
        ...

class events(EventDataset[dd_event]):
    async def execute_result_async(self, a: ast.AST, title: Optional[str] = None):
        await asyncio.sleep(0.01)
        return a

这不是必需的,但当这样做时

  • 使用类型提供选项/猜测的编辑器现在会亮起来,只要它们有合理的类型检查功能。
  • 如果遗漏了必需的参数,将生成错误
  • 如果遗漏了默认参数,它将自动填充。

应该注意的是,类型和表达式跟随者并不十分复杂!虽然它可以跟随方法调用,但不会跟随其他很多内容!

代码应在 Python 3.11 或使用 from __future__ import annotations 时正常工作。

基于类型的回调

通过在类型系统中添加一个函数和一个引用,可以在遍历 func_adl 时执行任意代码。保持查询和 events 定义相同,我们可以使用类定义的装饰器直接将信息添加到 Python 类型声明中

from func_adl import ObjectStream
from typing import TypeVar

# Generic type is required in order to preserve type checkers ability to see
# changes in the type
T = TypeVar('T')

def add_md_for_type(s: ObjectStream[T], a: ast.Call) -> Tuple[ObjectStream[T], ast.AST]:
    return s.MetaData({'hi': 'there'}), a


@func_adl_callback(add_md_for_type)
class dd_event:
    def Jets(self, bank: str) -> Iterable[dd_jet]:
        ...
  • 当处理 .Jets() 方法时,会调用 add_md_for_type,带有当前对象流和 ast。
  • 这里的 add_md_for_type 添加元数据并返回更新后的流和 ast。
  • 没有阻止函数解析 AST、删除或添加参数、添加更复杂的元数据或根据调用点的参数执行任何这些操作的限制。

参数化方法调用

这是一种非常特殊形式的回调,它是为了支持像 C++ 中模板的互操作这样的功能而实现的。它允许你写像这样的事情

result = (ds
            .SelectMany(lambda e: e.Jets())
            .Select(lambda j: j.getAttribute[float]('moment0'))
            .AsAwkward('moment0')
)

注意在 getAttribute 调用中的 [float]。这只会在 Jet 类中的属性 getAttribute 被标记为装饰器 func_adl_parameterized_call 时发生

T = TypeVar('T')
def my_callback(s: ObjectStream[T], a: ast.Call, param_1) -> Tuple[ObjectStream[T], ast.AST, Type]:
    ...

class Jet:
    @func_adl_parameterized_call()
    @property
    def getAttribute(self):
        ...

在这里,param_1 将以 float 类型调用。请注意,这意味着在调用此函数时,参数值必须解析为实际值——它们不会转换为 C++。在这种情况下,my_callback 可以注入 MetaData 来构建对 getAttribute 的模板调用。my_callback 返回的元组与上面的 add_md_for_type 相同——只是第三个参数必须返回调用的返回类型。

如果使用了多个参数(如 j.getAttribute['float','int'])['moment0']),则 param_1 是一个包含两个元素的元组。

函数定义

拥有可以在后端直接调用的函数是有用的——或者使用函数调用来人为地将某些内容插入到 func_adl 查询流中(如 MetaData)。例如,C++ 后端使用此方法插入内联 C++ 代码。使用 func_adl_callable 装饰器来完成此操作。

def MySqrtProcessor(s: ObjectStream[T], a: ast.Call) -> Tuple[ObjectStream[T], ast.Call]:
    'Can add items to the object stream'
    new_s = s.MetaData({'j': 'func_stuff'})
    return new_s, a

# Declare the typing and name of the function to func_adl
@func_adl_callable(MySqrtProcessor)
def MySqrt(x: float) -> float:
    ...

r = (events()
        .SelectMany(lambda e: e.Jets('jets'))
        .Select(lambda j: MySqrt(j.eta()))
        .value())

在上面的示例中,对 MySqrt 的调用将被传递回后端。然而,在调用之前将插入 MetaData。可以使用 C++ 定义 MySqrt 函数(或类似函数)。

请注意,如果 MySqrt 总是在后端定义,且不需要额外的数据,则可以在装饰器调用中省略 MySqrtProcessor

添加新的 Collection API

First 这样的函数不应存在于 ObjectStream 中,因为那是定义的最高级别集合。然而,在事件上下文中,它们非常有意义。后面的代码需要一种方式来跟踪这些(类型提示系统不需要修改,只需在您的 Event 对象中适当地声明您的集合)。

例如,请参阅 test_type_based_replacement 文件。类级别装饰器称为 register_func_adl_os_collection

开发

在构建新版本并通过测试后,您可以通过在 github 上创建新版本来发布它。创建发布时运行的动作将将其发送到 pypi

项目详情


发布历史 发布通知 | RSS 源

下载文件

下载适用于您平台文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

func_adl-3.3.3.tar.gz (53.7 kB 查看哈希值)

上传时间 源代码

构建分发版

func_adl-3.3.3-py3-none-any.whl (43.0 kB 查看哈希值)

上传时间 Python 3

由...