使用类型提示进行类型转换
假设我有一个函数,它需要做一些内部处理,并显示提供的文本:
def display_text(text: str):
...
print(text)
还有一个类里面有一个叫 convert()
的方法:
class String:
def __init__(self, string: str):
self.string = string
def convert(self):
return self.string
现在,你能不能在 display_text
函数的 text
参数上加上类型提示,说明它应该是 String
类型?但是如果传入的参数是 str
类型,就调用 convert
方法,并把返回的值赋给 text
?像这样:
def display_text(text: String):
...
print(text)
这个操作应该在 display_text
函数里不需要额外的代码,只用类型提示就可以。我在一些库里见过这种用法,但我还是搞不明白它是怎么工作的。
我试着查了一些库的代码(比如 discord.py 的转换器),也在 StackOverflow 上找了类似的问题,只发现了 typing.Protocol
,但还是不明白这个转换是怎么实现的。
1 个回答
1
你能在
display_text
函数中给text
参数加上String
的类型提示吗?但是如果传入的参数是str
,就调用convert
函数,并把返回的值赋给text
,而不需要在display_text
函数里写其他代码,只用类型提示来实现。
不行,单靠类型提示是做不到的,因为类型注解在程序运行时是完全无效的,根本不会起作用(而且如果使用了 from __future__ import annotations
,它们甚至不会被计算)。如果这是个技巧性的问题,而“在 display_text
函数里”是关键点,那么你可以通过装饰器来处理你的函数(或者装饰一个包含这些函数的类,或者使用一个元类),这样就可以在需要的时候用类型注解来转换参数。
下面是一个这样的装饰器的例子:
import dataclasses
import inspect
from functools import wraps
from typing import Any
def convert_args(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
sig = inspect.signature(fn)
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
for name, val in bound_args.arguments.items():
param = sig.parameters[name]
if hasattr(param.annotation, "convert"):
bound_args.arguments[name] = param.annotation.convert(val)
return fn(*bound_args.args, **bound_args.kwargs)
return wrapper
@dataclasses.dataclass
class Shouty:
val: str
@classmethod
def convert(cls, val: Any):
return Shouty(val=str(val).upper())
@dataclasses.dataclass
class Shorten:
val: str
@classmethod
def convert(cls, val: Any):
return Shorten(val=str(val)[::2])
@convert_args
def display_text(arg1: Shouty, arg2: Shorten):
print(locals())
display_text("hello", "world, this is an example")
这段代码的输出是:
{'arg1': Shouty(val='HELLO'), 'arg2': Shorten(val='wrd hsi neape')}