You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现可接收调用者传入tokenizer参数的Python装饰器?

解决方案:支持动态传入Tokenizer的装饰器

你已经摸到了核心思路,现在的问题就是让装饰器既能封装分词/解码逻辑,又允许调用时灵活替换tokenizer,而不是装饰时就把它固定死。下面给你一个更完善的方案,兼顾灵活性、兼容性和代码简洁性:

先梳理核心需求

  • 被装饰的函数只需要负责处理token列表,不用管分词和解码
  • 支持在装饰时设置默认tokenizer,也能在调用时传入自定义tokenizer
  • 同时兼容你定义的Tokenizer抽象基类,以及原始的函数式tokenizer(比如str.split)

完整实现代码

首先保留你定义的Tokenizer抽象类和具体实现,再加上一个适配器兼容函数式tokenizer,最后写核心装饰器:

import random
import string
from abc import ABC, abstractmethod
from typing import List, Callable, Any

# 你的Tokenizer抽象基类(保留原有逻辑)
class Tokenizer(ABC):
    """ Base class for Tokenizer which provides the encode and decode methods """
    def __init__(self, tokenizer: Any) -> None:
        self.tokenizer = tokenizer

    @abstractmethod
    def encode(self, text: str) -> List[str]:
        """ Tokenize a string into list of strings """

    @abstractmethod
    def decode(self, token_list : List[str]) -> str:
        """ Creates a string from a tokens list using the tokenizer """

    def encode_many(self, texts: List[str]) -> List[List[str]]:
        return [self.encode(text) for text in texts]

    def decode_many(self, token_lists: List[List[str]]) -> List[str]:
        return [self.decode(token_list) for token_list in token_lists]

# 你的SingleSpaceTokenizer实现
class SingleSpaceTokenizer(Tokenizer):
    """ Simple tokenizer that splits on single spaces """
    def __init__(self, tokenizer=None) -> None:
        super().__init__(tokenizer)

    def encode(self, text: str) -> List[str]:
        return text.split()

    def decode(self, token_list: List[str]) -> str:
        return ' '.join(token_list)

# 你的AtTokenizer实现
class AtTokenizer(Tokenizer):
    def __init__(self, tokenizer=None):
        super().__init__(tokenizer)

    def encode(self, text):
        return text.split('@')

    def decode(self, token_list):
        return '@'.join(token_list)

# 适配器:把普通Callable(比如str.split)转换成Tokenizer类实例
class CallableTokenizer(Tokenizer):
    def __init__(self, tokenizer_func: Callable[[str], List[str]], separator: str = ' '):
        super().__init__(tokenizer_func)
        self.separator = separator

    def encode(self, text: str) -> List[str]:
        return self.tokenizer(text)

    def decode(self, token_list: List[str]) -> str:
        return self.separator.join(token_list)

# 核心装饰器
def tokenize(default_tokenizer=None):
    # 处理默认值:如果没传,默认用str.split对应的Tokenizer
    if default_tokenizer is None:
        default_tokenizer = CallableTokenizer(str.split)
    # 如果传入的是普通函数,自动转换成CallableTokenizer
    elif isinstance(default_tokenizer, Callable):
        default_tokenizer = CallableTokenizer(default_tokenizer)

    def decorator(f):
        def wrapper(text: str, tokenizer=None):
            # 调用时优先用传入的tokenizer,否则用装饰时的默认值
            current_tokenizer = tokenizer or default_tokenizer
            # 封装分词→处理token→解码的流程
            tokens = current_tokenizer.encode(text)
            processed_tokens = f(tokens)
            return current_tokenizer.decode(processed_tokens)
        return wrapper
    return decorator

多种场景下的使用示例

1. 使用默认tokenizer(str.split)

@tokenize()
def change_first_letter(token_list):
    return [random.choice(string.ascii_letters) + token[1:] for token in token_list]

@tokenize()
def spellcheck(token_list):
    from spellchecker import SpellChecker
    checker = SpellChecker()
    return [checker.correction(word) for word in token_list]

# 直接调用,用默认分词器
print(change_first_letter("hello world"))
print(spellcheck("helo wrld"))

2. 装饰时指定默认Tokenizer,调用时替换

# 装饰时默认用SingleSpaceTokenizer
@tokenize(SingleSpaceTokenizer())
def change_first_letter(token_list):
    return [random.choice(string.ascii_letters) + token[1:] for token in token_list]

# 调用时换成AtTokenizer
new_tokenizer = AtTokenizer()
print(change_first_letter("a@b@c", tokenizer=new_tokenizer))

3. 兼容原始函数式tokenizer

# 装饰时默认用按'-'分割的函数
@tokenize(lambda x: x.split('-'))
def change_first_letter(token_list):
    return [random.choice(string.ascii_letters) + token[1:] for token in token_list]

print(change_first_letter("test-hello-world"))

这个方案的优势

  • 完全满足你的需求:既封装了重复的分词/解码逻辑,又支持调用时动态传入自定义tokenizer
  • 兼容性强:同时支持你定义的Tokenizer类和普通函数式tokenizer
  • 职责清晰:被装饰的函数只需要专注于token列表的处理,代码更简洁易维护
  • 扩展性好:后续新增Tokenizer实现,直接继承抽象基类即可,不需要修改装饰器

内容的提问来源于stack exchange,提问作者shaun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 16:57:47