Python 3.x 中的类型隐式转换(强制)是否可能?

bba*_*ker 5 python python-3.x mypy

是否可以在 Python 3.6+ 中实现自定义的自动/隐式转换(又名强制转换),而不会让mypy其他静态分析器感到难过?一个例子是 a def(foo: A),并给出def b_to_a(b: B) -> A,有没有办法我可以写foo(some_b)(where some_b: B) 而不是foo(b_to_a(some_b))

我认为在 Python 的动态中肯定有一些很好的方法可以做到这一点(例如,将成员添加到包含转换器的类),甚至将此类转换器添加到函数对象本身,以便它可以处理选定类型的转换,但我目前对 Python 类型的理解让我觉得它不会满足mypy等等。

要进行比较,请参阅 Scala 的隐式转换

Pat*_*ugh 6

这是我想出的此功能的实现。我们为我们知道“隐式”转换的类型保留了一个单调度转换器的字典。我们使用@implicit装饰器为此添加转换器。

然后我们有一个@coerce装饰器,可以在运行时检查函数注释,获取适当的转换器并应用转换。下面是框架:

from functools import wraps, singledispatch
from inspect import signature
from collections import OrderedDict

converters = {}

def implicit(func):
    ret = func.__annotations__.get('return', None)
    if not ret or len(func.__annotations__) != 2:
        raise ValueError("Function not annotated properly or too many params")
    if ret not in converters:    
        @singledispatch
        def default(arg):
            raise ValueError("No such converter {} -> {}".format(type(arg).__name__, ret.__name__))    
        converters[ret] = default
    else:
        default = converters[ret]
    t = next(v for k, v in func.__annotations__.items() if k != 'return')
    default.register(t)(func)
    return wraps(func)(default)

def convert(val, t):
    if isinstance(val, t):
        return t
    else:
        return converters[t](val)


def coerce(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        sig = signature(func)
        bound = sig.bind(*args, **kwargs)
        bound.apply_defaults()
        bound.arguments = OrderedDict(
            (param, convert(val, sig.parameters[param].annotation)) 
            for param, val in bound.arguments.items())
        return func(*bound.args, **bound.kwargs)    
    return wrapper
Run Code Online (Sandbox Code Playgroud)

和一个例子:

from typing import Tuple, Type


@implicit
def str_to_int(a: str) ->  int:
    return int(a)

@implicit
def float_to_int(a: float) -> int:
    return int(a)

@coerce
def make_ints(a: int, b: int) -> Tuple[Type, Type]:
    return (type(a), type(b))

print(make_ints("20", 5.0))
# (<class 'int'>, <class 'int'>)
Run Code Online (Sandbox Code Playgroud)


Sra*_*raw 1

我不认为这是一个转换问题。但看起来像是注释问题。

第一,如果foo只能处理A,怎么可能接受B?如果也foo可以处理B,为什么它应该只接受A

其次,如果你想注释fooaccept Aor B,你可以使用def(foo: Union[A, B]).

最后,如果您的意思B是应该有一些方法使其可以由只能处理A. 它仍然是 的一个实例B。如果没有正确的注释,您的静态分析器仍然会警告您。