根据Google Python样式指南,静态方法(几乎)永远不应该使用:
除非为了与在现有库中定义的API。编写模块级函数而是
这种建议背后的原因是什么?
这是谷歌独有的,还是在Python中使用静态方法有其他(更普遍的)缺点?
特别是,如果我想在一个类内部实现一个实用函数,该函数将被该类的其他公共成员函数调用,那么最佳实践是什么?
class Foo:
.......
def member_func(self):
some_utility_function(self.member)
谷歌python风格指南
如何理解谷歌Python风格指南,其中写道:
除非为了与现有库中定义的API集成而被迫使用@staticmethod,否则永远不要使用。编写模块级函数,而不是
好吧,你应该把它理解为谷歌的风格指南。如果你正在为谷歌编写Python代码,或者为一个符合该风格指南的项目做出贡献,或者选择将其用于自己的项目,答案很简单:除非API强制使用,否则不要使用@staticmethod
。
这意味着不存在判断调用的情况:API不会强制类内部的实用函数为@staticmethod
,因此它不应该是@staticmethod
。
对于@staticmethod
的一些其他常见的1原因也是如此。如果您想要一个用于保存回调函数的实例属性的默认值……太糟糕了,请找到另一种编写方法(例如,在__init__
中定义的本地函数)。如果你想要一个看起来像@classmethod
但明确不与子类共变异的东西……太糟糕了,它就是看起来不像@classmethod
。
当然,如果你没有遵循谷歌的风格指南,那么你应该把它理解为众多观点中的一种。许多Python开发人员对@staticmethod
并不像该指南那样强硬。当然,谷歌是一个非常出色的Python代码开发人员。另一方面,谷歌的风格指南是在导入Java主义比现在更成问题的时候编写的2但你可能不想过多考虑每个观点的分量;相反,在重要的时候,了解问题并提出自己的观点。
至于你的具体例子,正如我在评论中所说:事实上,你很自然地发现自己在写some_utility_function(self.member)
而不是self.some_utility_function(self.member)
或Foo.some_utility_function(self.member)
,这意味着直觉上,你已经把它看作是一个函数,而不是一个@staticmethod
。在这种情况下,您肯定应该将该函数写成函数,而不是@staticmethod
。
这可能只是互联网上一个人的观点,但我认为大多数Python开发人员都会同意这种情况。这是当你做时,你会自然而然地发现自己在每次通话前都会在self.
前加上前缀,这时需要做出判断。
1。嗯,不完全是常见。但它们并不是罕见到永远不会出现。它们非常常见,以至于当讨论为Python3弃用@staticmethod
时,有人很快提出了这两个案例,以及标准库中的示例,这足以让Guido扼杀讨论
2.在Java中,没有模块级函数,您必须编写静态方法来模拟它们。有几年,大多数大学的CS程序都专注于Java,而且很多软件都是由Java编写的,所以很多人在编写带有太多@staticmethod
(以及getter和setter,以及其他Java主义)的Python类
按照编写对some_utility_function()
的调用的方式,它无论如何都没有在类上定义。如果是的话,你会用self.some_utility_function()
或可能是Foo.some_utility_function()
来命名它。所以你已经按照风格指南建议的方式进行了命名。
@classmethod
和@staticmethod
装饰器主要用于告诉Python将什么作为第一个参数传递给方法,而不是通常的self
:要么是类型,要么什么都不传递。但是,如果您使用@staticmethod
,并且既不需要实例也不需要其类型,那么它真的应该是类的成员吗?这就是他们在这里要求你考虑的:当实用函数实际上没有以任何方式与类绑定时,它们是否应该是类的方法?谷歌拒绝
但这只是谷歌的风格指南。他们决定让程序员更喜欢模块级的函数。他们的话不是法律。显然,Python的设计者看到了@staticmethod
的用途,否则他们就不会实现它!如果你能为一个类附加一个实用函数提供一个理由,请随意使用它
My 2¢
重点是,当你想做鸭子类型多态的事情时,定义模块级函数是过度的,尤其是当你的定义很短的时候。例如,定义
class StaticClassA:
@staticmethod
def maker(i: int) -> int:
return 2*i
class StaticClassB:
@staticmethod
def maker(i: int) -> float:
return pow(i, 2)
#[...] say, 23 other classes definitions
class StaticClassZ:
@staticmethod
def maker(i: int) -> float:
return 2*pow(i, 2)
显然比在26个模块中定义26个(从A到Z)类更聪明。
我对"多态性"一词的含义的一个实际例子?有了上面的类定义,你就可以进行
for class_ in [StaticClassA, StaticClassB, StaticClassZ]:
print(class_.maker(6))