在没有 pylint 抱怨的情况下向重写函数添加额外的参数
Adding additional arguments to overridden functions without pylint complaining
我想在具有一组必需的位置参数的 superclass 中定义一个函数 f
,并允许 subclasses 提供它们自己的 f
共享所需的参数,但也有额外的命名参数。
此外,我想以 mypy 能够检查所有类型的方式来执行此操作,而 pylint3 不会抱怨任何事情。
下面的函数 f
和 g
代表了我迄今为止的最佳尝试:
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, required_arg: int, **kwargs: Any) -> None:
print(self, required_arg, kwargs)
class A(Base):
def f(self, required_arg: int, x: int = 42, *args: Any, **kwargs: Any) -> None:
print(self, required_arg, x, args, kwargs)
def g(self, required_arg: int, *, x: int = 42, **kwargs: Any) -> None:
print(self, required_arg, x, kwargs)
此代码按预期工作(传递 x=20
将其置于 kwargs
中用于基 class,而 x
用于派生 class),但是 运行 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)
当然,有多种方法可以抑制 class 警告,通常是通过文件或行,但我想以这样的方式声明函数没有必要。
如果它有所作为,我实际上并不关心基数 class 是否可以 访问 任何附加参数; kwargs
只是声明的一部分,以允许它接受任意参数。此外,要求 x
作为关键字参数 传递 是可以接受的,只要它以 x
而不是 kwargs['x']
结束。
您可以在 child class 中使用 parent class 中没有的参数,只要它们是可选的。如果没有 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 对此很满意。 您需要遵循的基本规则是您的 child objects 可以用作 parent objects。在这种情况下,MyPy 很高兴 Parrot
仍然是有效的 Animal
,因为它仍然可以只用 volume
调用 p.speak
,就像 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.
这通常就是您所需要的。当你知道你有一只 Parrot 时,你可以告诉它该说什么。如果你只知道你有某种动物,因为它可能是一只狗,而狗不会说英语 [需要引证],你应该限制自己使用所有动物都能处理的指令。
可以使用 *args
和 **kwargs
获得您的基础 class。您可以使用仅位置参数标记 /
。这样做的代价是您将事情提交给位置参数或关键字参数。语法是这样的:
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, required_pos_arg: int, optional_pos_arg: float=1.2, /, *args, required_kwarg, 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, 0.2, "Additional_pos_arg", required_kwarg="bar", optional_kwarg="Foo", kwarg_member="Additional_kw_arg")
通过在位置标记末尾使用 /
标记,并在关键字标记开始之前使用 *
(或 *args
),您可以消除随之而来的歧义从混淆位置和关键字参数。您可以在 *
之前的列表末尾添加更多位置参数(同样,它们必须是可选的,以便基本调用仍然有效)。你可以在 *
之后添加更多的关键字参数(同样的限制)。你会注意到 child class 也必须接受 args
和 kwargs
因为 child 必须接受 parent 会接受的任何东西。然后 MyPy 又开心了。
但是,正如我之前提到的,这可能不是您想要的。
使用 MyPy 的根本目的是让计算机帮助您确保您只在有意义的地方使用它们。如果您添加 *args
和 **kwargs
,您必然会限制它可以完成的工作。它不再能够阻止你让狗背诵麦克白,甚至无法阻止你让鹦鹉用 9.6 的 florgle
说话。
除非在非常特殊的情况下您确实希望基地 class 处理所有事情,否则将自己限制在您真正想做的事情上会更安全。
你当然会发现,如果你写这段代码
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, words="Double, double toil and trouble")
else:
print(f"{s} failed its English test.")
我想在具有一组必需的位置参数的 superclass 中定义一个函数 f
,并允许 subclasses 提供它们自己的 f
共享所需的参数,但也有额外的命名参数。
此外,我想以 mypy 能够检查所有类型的方式来执行此操作,而 pylint3 不会抱怨任何事情。
下面的函数 f
和 g
代表了我迄今为止的最佳尝试:
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, required_arg: int, **kwargs: Any) -> None:
print(self, required_arg, kwargs)
class A(Base):
def f(self, required_arg: int, x: int = 42, *args: Any, **kwargs: Any) -> None:
print(self, required_arg, x, args, kwargs)
def g(self, required_arg: int, *, x: int = 42, **kwargs: Any) -> None:
print(self, required_arg, x, kwargs)
此代码按预期工作(传递 x=20
将其置于 kwargs
中用于基 class,而 x
用于派生 class),但是 运行 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)
当然,有多种方法可以抑制 class 警告,通常是通过文件或行,但我想以这样的方式声明函数没有必要。
如果它有所作为,我实际上并不关心基数 class 是否可以 访问 任何附加参数; kwargs
只是声明的一部分,以允许它接受任意参数。此外,要求 x
作为关键字参数 传递 是可以接受的,只要它以 x
而不是 kwargs['x']
结束。
您可以在 child class 中使用 parent class 中没有的参数,只要它们是可选的。如果没有 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 对此很满意。 您需要遵循的基本规则是您的 child objects 可以用作 parent objects。在这种情况下,MyPy 很高兴 Parrot
仍然是有效的 Animal
,因为它仍然可以只用 volume
调用 p.speak
,就像 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.
这通常就是您所需要的。当你知道你有一只 Parrot 时,你可以告诉它该说什么。如果你只知道你有某种动物,因为它可能是一只狗,而狗不会说英语 [需要引证],你应该限制自己使用所有动物都能处理的指令。
可以使用 *args
和 **kwargs
获得您的基础 class。您可以使用仅位置参数标记 /
。这样做的代价是您将事情提交给位置参数或关键字参数。语法是这样的:
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, required_pos_arg: int, optional_pos_arg: float=1.2, /, *args, required_kwarg, 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, 0.2, "Additional_pos_arg", required_kwarg="bar", optional_kwarg="Foo", kwarg_member="Additional_kw_arg")
通过在位置标记末尾使用 /
标记,并在关键字标记开始之前使用 *
(或 *args
),您可以消除随之而来的歧义从混淆位置和关键字参数。您可以在 *
之前的列表末尾添加更多位置参数(同样,它们必须是可选的,以便基本调用仍然有效)。你可以在 *
之后添加更多的关键字参数(同样的限制)。你会注意到 child class 也必须接受 args
和 kwargs
因为 child 必须接受 parent 会接受的任何东西。然后 MyPy 又开心了。
但是,正如我之前提到的,这可能不是您想要的。
使用 MyPy 的根本目的是让计算机帮助您确保您只在有意义的地方使用它们。如果您添加 *args
和 **kwargs
,您必然会限制它可以完成的工作。它不再能够阻止你让狗背诵麦克白,甚至无法阻止你让鹦鹉用 9.6 的 florgle
说话。
除非在非常特殊的情况下您确实希望基地 class 处理所有事情,否则将自己限制在您真正想做的事情上会更安全。
你当然会发现,如果你写这段代码
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, words="Double, double toil and trouble")
else:
print(f"{s} failed its English test.")