有没有可能检查两个Python函数是否可以互换?例如,如果我有:
def foo(a, b):
pass
def bar(x, y):
pass
def baz(x,y,z):
pass
我希望有一个函数
is_compatible(a,b)
,当传入foo和bar时返回True,但传入bar和baz时返回False,这样我就可以在调用它们之前检查它们是否可互换。有没有可能检查两个Python函数是否可以互换?例如,如果我有:
def foo(a, b):
pass
def bar(x, y):
pass
def baz(x,y,z):
pass
is_compatible(a,b)
,当传入foo和bar时返回True,但传入bar和baz时返回False,这样我就可以在调用它们之前检查它们是否可互换。inspect.getargspec()
:
inspect.getargspec(func)
获取函数参数的名称和默认值。返回一个包含四个元素的元组:(args, varargs, varkw, defaults)。其中,args 是参数名称的列表(它可能包含嵌套列表)。varargs 和 varkw 是 * 和 ** 参数的名称,或者是 None。defaults 是默认参数值的元组,如果没有默认参数,则为 None;如果该元组有 n 个元素,则它们对应于 args 中列出的最后 n 个元素。
从版本 2.6 开始更改:返回一个命名元组 ArgSpec(args、varargs、keywords、defaults)。
Callable[[], int]
是 Callable[[], float]
的子类型(协变)。
这很直观:最终求值为 int
的可调用对象可以替换求值为 float
的函数(暂时忽略参数列表)。
Callable[[float], None]
是 Callable[[int], None]
的子类型(逆变)。
这有点令人困惑,但请记住,对整数进行操作的可调用对象可能执行在浮点数上未定义的操作,例如 >>
<<
,而在浮点数上执行操作的可调用对象肯定不会执行在整数上未定义的任何操作(因为整数是浮点数的子类型)。因此,操作浮点数的可调用对象可以替换操作整数的可调用对象,但反之则不行(忽略返回类型)。
由此我们得出结论:要替换可调用对象 c1
为可调用对象 c2
,必须满足以下条件:
c2
的返回类型应该是c1
返回类型的子类型。c1
的参数列表:(a1,a2,...an)
和c2
的参数列表:(b1,b2,...bn)
,a1
应该是b1
的子类型,a2
应该是b2
的子类型,以此类推。实现
简单的实现方法是(忽略kwargs
和可变长度参数列表):
from inspect import getfullargspec
def issubtype(func1, func2):
"""Check whether func1 is a subtype of func2, i.e func1 could replce func2"""
spec1, spec2 = getfullargspec(func1), getfullargspec(func2)
if not issubclass(spec1.annotations['return'], spec2.annotations['return']):
return False
return all((issubclass(spec2.annotations[arg2], spec1.annotations[arg1]) for (arg1, arg2) in zip(spec1.args, spec2.args)))
例子:
from numbers import Integral, Real
def c1(x :Integral) -> Real:
pass
def c2(x: Real) -> Integral:
pass
print(issubtype(c2, c1))
print(issubtype(c1, c2))
class Employee:
pass
class Manager(Employee):
pass
def emp_salary(emp :Employee) -> Integral:
pass
def man_salary(man :Manager) -> Integral:
pass
print(issubtype(emp_salary, man_salary))
print(issubtype(man_salary, emp_salary))
输出:
True
False
True
False
def quux(*args)
在哪里适用? - S.Lott