为什么在Python中选择模块级函数而不是@staticmethod(根据谷歌风格指南)



根据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))

最新更新