指定通过输入接受的特定函数。Literal

Jay*_*ley 6 python typing python-typing

如何指定typing我的函数仅接受特定的可调用对象?例如,我想要类似于这样的功能:

import typing

def accepted_function1():
    pass
def accepted_function2():
    pass

def function_accepting_functions(foo: Literal[accepted_function1, accepted_function2]):
    foo()
Run Code Online (Sandbox Code Playgroud)

SrP*_*nda -2

这是可能的,但不是typing(或者至少不那么容易),因为每个函数都继承自内置类function,如果您认为函数是 lambda,则更容易获得它。

要知道两个函数是否相同,您只需比较它们即可,is最好使用;但这与类型提示有什么关系呢?好吧,Python 为您提供了对该函数的“完全”访问权限,这意味着您可以读取任何给定参数的类型并实现逻辑来处理特定需求。

一个缺点是它不会给你 ide 中任何现有的错误。

在这里,我与确切的函数进行比较,但以同样的方式,您可以通过正则表达式或父类进行过滤。另请注意, useenumerate因为__annotations__不包含没有类型的参数,并且它确实包含**kwargs函数的提示和返回类型(如果给定),因此需要进行额外的检查。

def fake_assert(valid, message):
    if not valid:
        print(message)
    return not valid

def ensure_call_hint(foo):
    hints = foo.__annotations__
    var_names = foo.__code__.co_varnames
    def wrapper(*args, **kwargs):
        for i, arg in enumerate(var_names):
            if arg in hints:
                if isinstance(hints[arg], list | tuple):
                    if callable(args[i]):
                        # assert args[i] in hints[arg], 'Invalid function'
                        if fake_assert(args[i] in hints[arg], '(1) Invalid function'):
                            return lambda *a, **b: None
                    else:
                        # assert type(args[i]) in hints[arg], 'Invalid type'
                        if fake_assert(type(args[i]) in hints[arg], '(2) Invalid type'):
                            return lambda *a, **b: None
                elif callable(args[i]) and callable(hints[arg]):
                    # assert args[i] is hints[arg], 'Invalid function'
                    if fake_assert(args[i] is hints[arg], '(3) Invalid function'):
                        return lambda *a, **b: None
                else:
                    # assert type(args[i]) is hints[arg], 'Invalid type'
                    if fake_assert(type(args[i]) is hints[arg], '(4) Invalid type'):
                        return lambda *a, **b: None

        print('(0) Valid call')
        return foo(*args, **kwargs)
    return wrapper


def faa():
    pass
def foo():
    pass
def fii():
    pass

class Impostor:
    @staticmethod
    def faa():
        pass


@ensure_call_hint
def func(func_arg: (faa, foo, int), var_b: fii):
    if callable(func_arg):
        func_arg()


print('func_arg valid    ', end=''); func(faa, fii)
print('func_arg invalid  ', end=''); func(fii, fii)
print('func_arg impostor ', end=''); func(Impostor.faa, fii)
print('func_arg int      ', end=''); func(0, fii)
print('func_arg str      ', end=''); func('0', fii)

print('var_b valid       ', end=''); func(faa, fii)
print('var_b invalid     ', end=''); func(faa, faa)
print('var_b int         ', end=''); func(faa, 0)
Run Code Online (Sandbox Code Playgroud)
func_arg valid    (0) Valid call
func_arg invalid  (1) Invalid function
func_arg impostor (1) Invalid function
func_arg int      (0) Valid call
func_arg str      (2) Invalid type
var_b valid       (0) Valid call
var_b invalid     (3) Invalid function
var_b int         (4) Invalid type
Run Code Online (Sandbox Code Playgroud)