为什么使用@staticmethod?

43 投票
5 回答
27135 浏览
提问于 2025-04-18 05:37

我就是搞不懂为什么我们需要用到 @staticmethod。我们先来看一个例子。

class test1:
    def __init__(self,value):
        self.value=value

    @staticmethod
    def static_add_one(value):
        return value+1

    @property
    def new_val(self):
        self.value=self.static_add_one(self.value)
        return self.value


a=test1(3)
print(a.new_val) ## >>> 4



class test2:
    def __init__(self,value):
        self.value=value

    def static_add_one(self,value):
        return value+1

    @property
    def new_val(self):
        self.value=self.static_add_one(self.value)
        return self.value


b=test2(3)
print(b.new_val) ## >>> 4

在上面的例子中,两个类里的方法 static_add_one 在计算时并不需要类的实例(也就是 self)。

test1 类中的 static_add_one 方法加上了 @staticmethod 装饰器,运行得很好。

但与此同时,test2 类中的 static_add_one 方法没有加 @staticmethod 装饰器,依然可以正常工作,因为它用了一种技巧,给参数传了一个 self,但其实并没有用到它。

那么,使用 @staticmethod 有什么好处呢?它能提高性能吗?还是说这只是因为 Python 的哲学之一:“显式优于隐式”呢?

5 个回答

-1

我觉得你说得很对:

不过同时,类 test2 中的 static_add_one 方法没有加 @staticmethod 装饰器,但它也能正常工作,原因是它用了一种技巧,给参数传了一个 self,但实际上并没有用到它。

正如你所说,@staticmethod 会隐式地提供 self 参数,这样你就可以通过实例来调用这个方法,而不需要明确写出 self 参数。根据 这个链接

静态方法可以通过类来调用(比如 C.f()),也可以通过实例来调用(比如 C().f())。

要让 C().f() 能正常工作,你要么给 f() 加上 @staticmethod 装饰器(或者明确调用 staticmethod()),要么像你在例子中那样,明确添加 self 参数。

2

使用 @staticmethod 是为了那些不需要依赖具体对象的方法,但你还是希望它们在类的范围内(而不是模块的范围内)。

你在 test2.static_add_one 里的例子,传递了一个没用的 self 参数,浪费了时间,但它的功能和 test1.static_add_one 是一样的。注意,这个多余的参数是无法被优化掉的。

我能想到的一个例子是在我一个 Django 项目里,模型类代表一个数据库表,而这个类的对象代表一条记录。有些函数是类使用的,但它们是独立的,不需要依赖对象来运行,比如一个将标题转换成“slug”的函数,slug 是一种符合 URL 语法限制的标题表示方式。这个将标题转换成 slug 的函数被声明为 staticmethod,正是为了和使用它的类紧密关联。

3

我来补充一些其他答案没有提到的内容。这不仅仅是关于模块化的问题,也就是把相关的部分放在一起。还有一个原因是,这个方法在类的其他层级(比如子类或父类)可能是非静态的,这样就可以参与多态(根据类型来决定调用哪个方法)。所以,如果你把这个函数放在类外面,就会限制子类有效地重写它。

现在,假设你发现类 C 的方法 C.f 中不需要 self,你有三个选择:

  1. 把它放到类外面。但我们刚才已经决定不这样做。

  2. 什么都不做:虽然不使用,但仍然保留 self 参数。

  3. 声明你不使用 self 参数,同时让其他 C 的方法可以通过 self.f 来调用 f,这样做是为了保持将来可能会有依赖于某个实例状态的 f 的重写。

第二个选项需要的概念负担更少(因为你已经需要了解 self 和方法作为绑定函数的概念,这是更一般的情况)。但你可能还是希望明确表示 self 没有被使用(而且解释器甚至可能会因此给你一些优化,因为不需要部分应用函数到 self)。在这种情况下,你可以选择第三个选项,并在你的函数上方加上 @staticmethod

25

今天我突然发现使用 @staticmethod 的一个好处。

如果你在一个类里面创建了一个静态方法,你就不需要先创建这个类的实例就可以直接使用这个静态方法。

举个例子,

class File1:
    def __init__(self, path):
        out=self.parse(path)

    def parse(self, path):
        ..parsing works..
        return x

class File2:
    def __init__(self, path):
        out=self.parse(path)

    @staticmethod
    def parse(path):
        ..parsing works..
        return x

if __name__=='__main__':
    path='abc.txt'
    File1.parse(path) #TypeError: unbound method parse() ....
    File2.parse(path) #Goal!!!!!!!!!!!!!!!!!!!!

因为这个 parse 方法和 File1 以及 File2 这两个类关系很紧密,把它放在类里面更自然。不过,有时候这个 parse 方法在其他类中也可能会用到。如果你想在 File1 中使用它,你必须先创建一个 File1 的实例,然后才能调用 parse 方法。而如果在 File2 中使用静态方法,你可以直接用 File2.parse 的方式来调用这个方法。

这样做让你的工作变得更方便、更自然。

74

使用 staticmethod 的原因是,当你有一些可以作为独立函数(不属于任何类)来写的东西,但你想把它放在类里面,因为它和这个类有某种语义上的关系。(比如,它可能是一个不需要类里面任何信息的函数,但它的行为是特定于这个类的,所以子类可能想要重写它。)在很多情况下,把某些东西写成独立函数而不是静态方法也是合理的。

你的例子其实不太一样。一个关键的区别是,尽管你没有使用 self,但你仍然需要一个实例来调用 static_add_one —— 你不能直接用 test2.static_add_one(1) 来调用它。所以在这里确实存在行为上的差异。静态方法最主要的“竞争对手”不是一个忽略 self 的普通方法,而是一个独立函数。

撰写回答