一、什么时候应该使用 staticmethod
staticmethod 是一个装饰器,它的作用是改变函数方法的性质,让我们可以忽略掉第一个 self 参数,并且使得可以同时使用类名和对象去调用它。
例如
class Foo:
def f(self, x):
return x * 5
f 的作用是将 x乘五倍。
在流行的 IDE 如果我们写下以上的代码,可能会收到一个提示
静态方法提示
Method may be static
哦,因为函数实现内部并没有特别的属于Foo 类对象的成员,所以IDE 提示我们应当把它改成静态方法,这表示表示方法不依赖这个类。
可以改成这样:
@staticmethod
def f(x):
return x * 5
外部可以这样用
Foo.f(10)
# or
foo = Foo()
foo.f(10)
如果跨越模块
很可能就是长成这样的代码
import foo # 假设模块名是 foo
foo.Foo.f()
下面我们要说——这样做不太好
首先我们看到使用上有一点点别扭——foo.Foo.f() 这违背我们使用内置库函数的经验——例如使用 sin cos 函数,我们是这样的
import math
math.sin(10)
math.cos(1.0)
或者
from math import sin, cos
sin(10)
cos(1.0)
单就这个case而言这不是一个好主意 ——代码不太简洁。我们可以把 staticmethod 改成模块函数
什么时候应该用它?
很少的场景必须要用staticmethod
stackoverflow观点.png事实上,静态函数的概念来自 C++ 以及Java,至少我们可以这样类比。
对于 Java 由于所有的代码 ,函数变量都放在一个 class 域内。
我们总有一些方法是不需要通过生成对象再去调用,因为一方面生成对象总是要回收,总是要调用构造函数,带来了一些不必要的开销,因此总是得想办法设计出一些函数,直接通过类名去调用的。当然 Java 的对象同样可以调它类内定义的静态函数。
而 C++ 略有区别,C++不允许通过对象的方式调用声明为static 的方法
回到 Python,Python与C++的一大区别就是,它每个文件都在诞生的时候都自动成为一个 module,具有作用域的效力,与C++中 #include<filename.h> 再联动编译不同,所以,可以认为, Python的一个模块文件本身有 class的域作用。
如果仅仅是为了封装的目的——即我们认为 f 函数是应该带个 Foo前缀,放在foo的范围内供其他工程师阅读,以示我们是明白 OO 编程,那么完全可以把这个方法写成模块级别的方法
一篇更激进的文章
这篇文章 原文链接 的观点更激进——他认为我们永远不要使用staticmethod
概括一下他的理由
- 找不到什么理由一定要用对象调用方法,比如 -5.abs() 而不是 Math.abs(-5)
- Java把数学相关的方法放到 Math,但是他们的行为类比于Python 的classmathod。并举例 Jon Skeet (Google Senior Software Developer 《C# in Depth》的作者)认为 Java应该把静态方法设计成与 Python的类方法一样,并且不允许通过对象的方式调用
- 当一组静态方法成群出现在一个类中是,类只是起到一个分组的作用,例如 Java的Math类收集一些数学相关的功能函数,但是 Python处理同样的问题是使用 module 模块。这就是我们开篇谈到的 point
- 第四个理由作者说明,内建库中唯一使用 staticmetho的两个场景
一个是
str.maketrans()
作者认为这个是可以用classmethod代替的。
importlib.abc.InspectLoader.source_to_code()
Python之父 Guido van Rossum 原话摘录 -basically said as much in April, 2012
We all know how limited static methods are.(They’re basically an accident — back in the Python 2.2 days when I was inventing new-style classes and descriptors, I meant to implement class methods but at first I didn’t understand them and accidentally implemented static methods first. Then it was too late to remove them and only provide class methods.
大概意思就是说,静态方法在 2.2 出现就是一个意外,后来意识到了它很废但是删除已经来不及
代码规范
大多数有气候的代码规范不推荐 或减少使用staticmethod
例如谷歌开源项目代码规范
谷歌开源项目代码规范Python——函数与方法装饰器
这条tips:
仅在有显著优势时, 审慎地使用装饰器. 避免使用 staticmethod. 减少使用 classmethod.**
总结
- 避免使用staticmethod。对于 Java转过来的 Python作者尤其注意,不要轻易在大脑中奖 staticmethod 等同于 Java 的static 方法
- 最严肃的理由,是Python的语义特性,模块函数实际上有同等的功效。不应该再多此一举,破坏其简洁性
- 对于习惯Java的程序员,在写Python的时候,尤其要注意,封装一切的习惯会导致你的代码非常啰嗦和繁琐,当程序出现 class1.obj1.obj2.method() 类似的代码时,通常一定是你搞错了什么,带来了糟糕的设计。
参考链接
https://www.webucator.com/article/when-to-use-static-methods-in-python-never/
https://stackoverflow.com/questions/735975/static-methods-in-python
https://docs.python.org/zh-cn/3/library/functions.html?highlight=staticmethod#staticmethod
网友评论