跳转到主要内容

数据抽象调度

项目描述

databackend

databackend包允许您注册一个子类,而无需导入该子类本身。这对于实现可选依赖项上的操作非常有用。

示例

在这个示例中,我们将实现一个函数fill_na(),该函数用于填充DataFrame中的缺失值。它支持来自两个流行库的DataFrame对象:pandaspolars。重要的是,这两个库都不需要安装。

设置

下面的代码为两个库中的每个DataFrame类定义了“抽象”父类。

from databackend import AbstractBackend

class AbstractPandasFrame(AbstractBackend):
    _backends = [("pandas", "DataFrame")]


class AbstractPolarsFrame(AbstractBackend):
    _backends = [("polars", "DataFrame")]

请注意,抽象类可以用作在issubclass()isinstance中替换实际对象的占位符。

from pandas import DataFrame

issubclass(DataFrame, AbstractPandasFrame)
isinstance(DataFrame(), AbstractPandasFrame)
True

📝 注意,您可以使用AbstractPandasFrame.register_backend("pandas", "DataFrame")作为注册后端的另一种方式。

简单的fill_na:使用isinstance切换行为

下面的fill_na()函数针对pandas和polars进行了自定义处理。

def fill_na(data, x):
    if isinstance(data, AbstractPolarsFrame):
        return data.fill_nan(x)
    elif isinstance(data, AbstractPandasFrame):
        return data.fillna(x)
    else:
        raise NotImplementedError()

请注意,在定义fill_na()时不需要导入pandaspolars

以下是调用fill_na()的两个类型DataFrame的示例。

# test polars ----

import polars as pl

df = pl.DataFrame({"x": [1, 2, None]})
fill_na(df, 3)


# test pandas ----

import pandas as pd

df = pd.DataFrame({"x": [1, 2, None]})
fill_na(df, 3)
     x
0  1.0
1  2.0
2  3.0

关键在于,用户可能只安装了pandas或只安装了polars。重要的是,进行isinstance检查不会导入任何库!

高级fill_na:通用函数调度

databackend泛型函数分发 结合使用时表现出色。这是一种编程方法,你首先声明一个函数(例如 fill_na()),然后在函数上注册每个后端特定的实现。

Python 内置了一个名为 functools.singledispatch 的函数来实现这一点。

以下是使用它的前一个 fill_na() 函数的示例。

from functools import singledispatch

@singledispatch
def fill_na2(data, x):
    raise NotImplementedError(f"No support for class: {type(data)}")


# handle polars ----

@fill_na2.register
def _(data: AbstractPolarsFrame, x):
    return data.fill_nan(x)


# handle pandas ----

@fill_na2.register
def _(data: AbstractPandasFrame, x):
    return data.fillna(x)

注意两个重要的装饰器

  • @singledispatch 定义了一个默认函数。如果没有找到特定实现,则会调用此函数。
  • @fill_na2.register 定义了函数的特定版本。

以下是一个示例。

# example ----

import pandas as pd
import polars as pl

df = pl.DataFrame({"x": [1, 2, None]})
fill_na2(df, 3)

df = pd.DataFrame({"x": [1, 2, None]})
fill_na2(df, 3)
     x
0  1.0
1  2.0
2  3.0

工作原理

在底层,AbstractBackend 类的行为与 Python 内置的 abc.ABC 类似。

from abc import ABC

class MyABC(ABC):
    pass

from io import StringIO

MyABC.register(StringIO)


# StringIO is a "virtual subclass" of MyABC
isinstance(StringIO("abc"), MyABC)
True

关键区别在于,你可以使用元组 ("<mod_name>", "<class_name>") 来指定虚拟子类。

当运行 issubclass(SomeClass, AbstractBackend) 时...

  • 检查标准 ABC 缓存机制,并可能立即返回答案。
  • 否则,子类挂钩将遍历注册的后端。
  • 挂钩将运行任何已导入的后端的子类检查(例如,位于 sys.modules 中)。

技术上,AbstractBackendabc.ABCMeta 继承了所有有用的元类功能,因此也可以使用这些功能。

项目详情


下载文件

下载适合您平台的应用程序。如果您不确定要选择哪一个,请了解更多关于 安装包 的信息。

源分布

databackend-0.0.3.tar.gz (12.4 kB 查看哈希值)

上传时间

构建分布

databackend-0.0.3-py3-none-any.whl (7.0 kB 查看哈希值)

上传时间 Python 3

支持者

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面