微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在没有 pylint 抱怨的情况下向重写的函数添加额外的参数

如何解决在没有 pylint 抱怨的情况下向重写的函数添加额外的参数

我想在具有一组必需位置参数的超类中定义一个函数 f,并允许子类提供共享所需参数的自己的 f 版本,但也有额外的命名参数。

此外,我希望以这样一种方式来执行此操作,即 mypy 能够检查所有内容的类型,而 pylint3 不会抱怨任何内容

以下函数 fg 代表了我迄今为止的最佳尝试:

from typing import Any

class Base:
    def f(self,required_arg: int,*args: Any,**kwargs: Any) -> None:
        print(self,required_arg,args,kwargs)
    
    def g(self,kwargs)

class A(Base):
    def f(self,x: int = 42,x,*,kwargs)

代码按预期工作(为基类传递 x=20 将其置于 kwargs 中,为派生类传递 x),但是运行 pylint3 会产生以下警告:>

W: 11,1: Keyword argument before variable positional arguments list in the deFinition of f function (keyword-arg-before-vararg)
W: 11,1: Parameters differ from overridden 'f' method (arguments-differ)
W: 14,1: Parameters differ from overridden 'g' method (arguments-differ)

当然,有多种方法可以抑制此类警告,通常是按文件或按行,但我想以不需要的方式声明该函数

>

如果它有所作为,我实际上并不关心基类是否可以访问任何附加参数; kwargs 只是声明的一部分,以允许它接受任意参数。此外,要求将 x 作为关键字参数传递是可以接受的,只要它以 x 而不是 kwargs['x'] 结尾。

解决方法

您可以在子类中使用父类中没有的参数,只要它们是可选的。如果没有 args 和 kwargs,这将是最简单的。

class Animal:
    def speak(self,volume: int) -> None:
        print(f"Animal speaking at volume {volume}")
        
class Parrot(Animal):
    def speak(self,volume: int,words: str ="Pretty Polly") -> None:
        print(f"Parrot says {words} at volume {volume}")
        
a = Animal()
a.speak(4)
p = Parrot()
p.speak(5) # fine,words takes the default as "Pretty Polly"
p.speak(6,"Bye") # Fine,words works from its position
p.speak(7,optional_arg="Yay") # Fine,words can be named

MyPy 对此很满意。 您需要遵循的基本规则是您的子对象可以用作父对象。在这种情况下,MyPy 很高兴 Parrot 仍然是有效的 Animal,因为它仍然可以只用 p.speak 调用 volume,就像 Animal 需要的那样。

您可以使用仅关键字参数做同样的事情。

class Parrot(Animal):
    def speak(self,required_arg: int,*,optional_kwarg: str = "Hello again") -> None:
        print(f"A {required_arg} {optional_arg}")

p = P()
p.speak(8) # Fine,as required by base class Animal
p.speak(9,optional_kwarg="Bye again") # fine,as specially allowed of Parrots
p.speak(10,"Oops") # This one won't work,because there isn't a positional argument to put it in.

这通常就是您所需要的。当你知道你有一只鹦鹉时,你可以告诉它该说什么。如果你只知道你有某种动物,因为它可能是一只狗,而狗不会说英语 [需要引用],你应该限制自己使用所有动物都可以处理的说明。


可以使用 *args**kwargs 获取您的基类。您可以使用仅位置参数标记 /。这样做的代价是您将事物提交为位置参数或关键字参数。语法如下:

class Base:
    def f(self,required_pos_arg: int,/,*args,required_kwarg,**kwargs) -> None:
        print(f"Base {required_pos_arg} {required_kwarg} {args} {kwargs}")
        
class A(Base):
    def f(self,optional_pos_arg: float=1.2,optional_kwarg: str ="Hello",**kwargs) -> None:
        print(f"A {required_pos_arg} {optional_pos_arg} {optional_kwarg} {args} {kwargs}")

b = Base()
b.f(9,0.2,"Additional_pos_arg",required_kwarg="bar",optional_kwarg="Foo",kwarg_member="Additional_kw_arg")

a = A()
a.f(9,kwarg_member="Additional_kw_arg")

通过在位置词的末尾使用 / 标记,并在关键字词的开头之前使用 *(或 *args),您可以消除混淆带来的歧义位置和关键字参数。您可以在 * 之前的列表末尾添加更多位置参数(同样,它们必须是可选的,以便裸骨调用仍然有效)。您可以在 * 之后添加更多关键字参数(相同的限制)。您会注意到子类还必须采用 argskwargs,因为子类必须接受父类可以接受的任何内容。然后 MyPy 又开心了。


但是,正如我之前提到的,这可能不是您想要做的。

使用 MyPy 的基本目的是让计算机帮助您确保您只使用有意义的东西。如果您添加 *args**kwargs,您必然会限制它的性能。它不再能阻止你让狗背诵麦克白,甚至阻止你让鹦鹉用 9.6 的 florgle 说话。

除非在非常特殊的情况下您确实希望基类处理所有事情,否则将自己限制在您真正想做的事情上会更安全。

你当然会发现如果你写了这段代码

def EnglishTest(students: List[Animal]):
    for s in students:
        s.speak(5,words="Double,double toil and trouble")

MyPy 会冲你大喊大叫。那不是错误。它正在做它的工作。它告诉你大多数动物不会说话。它会提示你改写

def EnglishTest(students: List[Animal]):
    for s in students:
        if isinstance(s,Parrot):
            s.speak(5,double toil and trouble")
        else:
            print(f"{s} failed its English test.")

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。