Class UDF
Class UDF基于面向对象编程范式实现,主要目的是为了创建有状态的处理逻辑。函数要求实现__init__、__call__等方法。
Class UDF注册
Class UDF注册传入的是Python类,对于__init__、__call__、 __del__ 3个实例方法有特殊的含义认定,详情请参见下表;其他的Python类和实例方法不做限制,用户可以任意添加。
| Python Class方法 | 是否必须 | 参数 | 含义 | 适用场景 |
|---|---|---|---|---|
| __init__(self, *args, **kwargs) | 否 | 通过with_arguments方法传入参数,只允许传入标量Scalar。 | UDF的构造方法。 | 初始化UDF属性(如保存参数、打开文件、建立连接等)。 |
| __call__(self, *args, **kwargs) | 是 | 通过UDF算子传入参数,可以传入标量Scalar和列名Column。 | UDF的业务逻辑方法入口。 | UDF处理数据、执行计算。 |
| __del__(self) | 否 | 不支持传入参数。 | UDF的析构方法。 | UDF资源清理(如关闭文件、断开网络连接等)。 |
注意事项:__init__的生命周期是每个batch调用一次,即Aura内核每处理一个batch,都会重新初始化一次变量。
示例
本示例定义一个带初始化参数的Class UDF,在__init__中接收姓名参数,在__call__中结合城市名生成欢迎语,将其注册到数据库后通过map方法对city_list表的每行数据生成问候消息。
- 定义Class UDF类Hello,在__init__中接收firstname和lastname参数,在__call__中结合城市名生成欢迎语。
- 连接Aura服务并设置UDF工作空间。
- 创建city_list表(包含city列),并插入5条城市数据。
- 删除已存在的同名UDF(支持重复执行)。
- 注册Class UDF到目标数据库。
- 加载city_list数据集,通过map方法调用UDF对city列生成问候语,传入firstname和lastname参数。
- 选择city和new_column列并执行查询打印结果。
- 关闭Aura连接。
import os import ibis from aura_frame.multimodal import ai_lake target_database = "test" #在LakeFormation中创建数据库,例如:test。具体操作,请参见创建LakeFormation实例并规划元数据 #连接AuraJobV2端点,具体参数说明,请参见建立与AuraJobV2端点的连接。 con = ai_lake.connect( aura_endpoint=os.getenv("aura_endpoint"), # AuraJobV2端点所在的终端节点(Endpoint) aura_endpoint_id=os.getenv("aura_endpoint_id"), # AuraJobV2端点ID aura_endpoint_name=os.getenv("aura_endpoint_name"),# AuraJobV2端点名称 aura_workspace_id=os.getenv("aura_workspace_id"), # 工作空间ID lf_catalog_name=os.getenv("lf_catalog_name"), # LakeFormation Catalog名称 access_key=os.getenv("access_key"), # 访问密钥ID(AK) secret_key=os.getenv("secret_key"), # 访问密钥(SK) default_database="postgres", # LakeFormation数据库名称 use_single_cn_mode=True, # 是否开启单CN模式 logging_level=logging.INFO # 设置日志级别 ) #配置UDF部署工作区,具体参数说明,请参见配置UDF部署工作区。 con.set_function_staging_workspace( obs_directory_base=os.getenv("obs_directory_base"), # 并行文件系统中UDF的存储路径 obs_bucket_name=os.getenv("obs_bucket_name"), # 并行文件系统名称 obs_server=os.getenv("obs_server"), # OBS终端节点(Endpoint) access_key=os.getenv("access_key"), # 访问密钥ID(AK) secret_key=os.getenv("secret_key"), # 访问密钥(SK) ) # 定义 Class UDF:带初始化参数的问候生成器 class Hello: def __init__(self, firstname: str, lastname: str): self.firstname = firstname self.lastname = lastname def __call__(self, location: str) -> str: return f"Hello {self.firstname} {self.lastname}! Welcome to {location}" try: # 创建测试表并写入数据 table_name = "city_list" table_schema = ibis.schema({"city": "string"}) con.sql(f'DROP TABLE IF EXISTS "{target_database}"."{table_name}";') con.create_table( table_name, schema=table_schema, database=target_database, table_format="orc", if_not_exists=True, ) con.sql(f''' INSERT INTO "{target_database}"."{table_name}" (city) VALUES ('Beijing'), ('Shanghai'), ('New York'), ('London'), ('Tokyo') ''') # 删除已存在的同名UDF(支持重复执行) con.delete_function("hello", database=target_database, if_it_exists=True) # 注册 Class UDF hello_udf = con.create_scalar_function( Hello, name="hello", database=target_database, ) # 使用 UDF 处理 city_list 表数据 ds = con.load_dataset("city_list", database=target_database) ds = ds.map( fn=hello_udf, on=[ds.city], as_col="new_column", firstname="Ethan", lastname="Carter", ) ds = ds.select_columns(ds.city, ds.new_column) print(ds.execute()) finally: con.close()