在Python3.9上是否有关于Typing.Optional的更新,还是我做错了什么?
当我在Discord.py中为我的bot编写帮助命令时,这个函数并没有像我预期的那样工作在Python3.9上是否有关于Typing.Optional的更新,还是我做错了什么?,python,discord.py,Python,Discord.py,当我在Discord.py中为我的bot编写帮助命令时,这个函数并没有像我预期的那样工作 def syntax(command): cmd_and_aliases = '|'.join([str(command), *command.aliases]) params = [] for key, value in command.params.items(): if key not in ('self', 'ctx'): params
def syntax(command):
cmd_and_aliases = '|'.join([str(command), *command.aliases])
params = []
for key, value in command.params.items():
if key not in ('self', 'ctx'):
params.append(f'[{key}]' if 'NoneType' in str(value) else f'<{key}>')
params = ' '.join(params)
return f'`{cmd_and_aliases} {params}`'
例如,对于该命令,结果如下所示
slap|hit <Member> [reason]
slap | hit[原因]
但我最终得到的是:
slap|hit <Member> <reason>.
slap | hit。
我是否做错了什么,或者是否有任何更新会导致此功能中断?
如果我做错了什么,请帮助我,说明我能做些什么。为什么
这种行为在3.9中确实发生了变化;我相信这就是原因。看起来这些变化是从commit开始的,但我相信您的特定问题的表现是由于前者
我对打字模块的管道不太熟悉,但据我所知,可选的表示现在是特殊情况,返回原样,即打字。可选[x]
而不是“扩展”到打字。联合[x,非类型]
。但是,它们仍然具有相同的“类型”,但这里重要的一点是,它们的“类型”已更改为,其\uuuu repr\uuu
为:
def __repr__(self):
args = self.__args__
if len(args) == 2:
if args[0] is type(None):
return f'typing.Optional[{_type_repr(args[1])}]'
elif args[1] is type(None):
return f'typing.Optional[{_type_repr(args[0])}]'
return super().__repr__()
可能的解决方案
首先,我想指出,您可以使用而不是command.params
(据我所知,奇怪的是,它没有文档记录)。它的功能相同,只是它会自动排除self
和ctx
,因此您不必检查它。我将在下面的解决方案中使用它
N.B.clean_params
将参数作为对象返回,因此我们有一个额外的层
我想出了两个可能的解决办法。第一个与您的非常接近,因为我们对键入的进行简单的字符串检查。可选的:
for key, value in command.clean_params.items():
optional = str(value.annotation).startswith('typing.Optional')
params.append(f'[{key}]' if optional else f'<{key}>')
对于键,command.clean_params.items()中的值:
optional=str(value.annotation).startswith('typing.optional')
params.append(如果可选,则为f'[{key}]',否则为f')
现在我个人不喜欢在确定类型时进行字符串检查(在本例中为泛型),所以我会选择一些不同的东西。如中所述,我们可以对泛型进行运行时内省,所以让我们利用它
for key, value in command.clean_params.items():
anno = value.annotation
try:
optional = anno.__origin__ is Union and anno.__args__[1] is type(None)
except AttributeError:
optional = False
params.append(f'[{key}]' if optional else f'<{key}>')
对于键,command.clean_params.items()中的值:
anno=value.annotation
尝试:
可选=anno.\uuuuu origin\uuuuuu是并集,anno.\uuuuu args\uuuuuu[1]是类型(无)
除属性错误外:
可选=错误
params.append(如果可选,则为f'[{key}]',否则为f')
这种方法的另一个好处是它可以在Python3.8中工作(我认为也是3.7),而字符串检查版本显然只能在3.9中工作。您可以利用sys.version\u info
进行多个字符串检查,但相比之下,这似乎太麻烦了。尝试了您得到的两个建议。第一个并没有解决问题。它的工作原理与原始代码相同。有趣的是,您得到的str(value)
的值是多少?第二种方法呢?嗯…我忘了说第二部分了。我的错。第二部分出现了一个错误,名为:AttributeError:“Parameter”对象没有属性“origin”,我将链接尝试第二种解决方案时收到的整个错误消息:在看到您的评论后,我想“为什么我没有首先检查str(value)”,然后继续执行此操作。str(value)是“key:type”的格式。所以,可选部分类似于'key:Optional[type]',所以,我只是将params.append(如果str(value)else f''中的'NoneType',则为f'[{key}]')
中的NoneType改为Optional,解决了这个问题。谢谢你的帮助。但是,我还是想知道如何使用你给我的第二个解决方案来解决这个问题。因为我对编码了解得越多,我就越擅长。啊,我错过了文档中关于clean_params
的部分,它说它返回OrderedDict[str,inspect.Parameter]
<代码>参数
来自库,用于对对象进行自省。我会更新答案以考虑到这一点。对不起,你能试着把这一点缩减到问题的要点吗?看起来大部分部件都是用来处理discord包装器的。我是否正确地阅读了您的代码/问题,即您正在尝试根据类型的字符串表示形式格式化类型,并且Optional似乎已从表示为并集更改为其他形式?
for key, value in command.clean_params.items():
optional = str(value.annotation).startswith('typing.Optional')
params.append(f'[{key}]' if optional else f'<{key}>')
for key, value in command.clean_params.items():
anno = value.annotation
try:
optional = anno.__origin__ is Union and anno.__args__[1] is type(None)
except AttributeError:
optional = False
params.append(f'[{key}]' if optional else f'<{key}>')