为什么使用@staticmethod?
我就是搞不懂为什么我们需要用到 @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 个回答
我觉得你说得很对:
不过同时,类
test2
中的static_add_one
方法没有加@staticmethod
装饰器,但它也能正常工作,原因是它用了一种技巧,给参数传了一个self
,但实际上并没有用到它。
正如你所说,@staticmethod
会隐式地提供 self
参数,这样你就可以通过实例来调用这个方法,而不需要明确写出 self
参数。根据 这个链接:
静态方法可以通过类来调用(比如
C.f()
),也可以通过实例来调用(比如C().f()
)。
要让 C().f()
能正常工作,你要么给 f()
加上 @staticmethod
装饰器(或者明确调用 staticmethod()
),要么像你在例子中那样,明确添加 self
参数。
使用 @staticmethod
是为了那些不需要依赖具体对象的方法,但你还是希望它们在类的范围内(而不是模块的范围内)。
你在 test2.static_add_one
里的例子,传递了一个没用的 self
参数,浪费了时间,但它的功能和 test1.static_add_one
是一样的。注意,这个多余的参数是无法被优化掉的。
我能想到的一个例子是在我一个 Django 项目里,模型类代表一个数据库表,而这个类的对象代表一条记录。有些函数是类使用的,但它们是独立的,不需要依赖对象来运行,比如一个将标题转换成“slug”的函数,slug 是一种符合 URL 语法限制的标题表示方式。这个将标题转换成 slug 的函数被声明为 staticmethod
,正是为了和使用它的类紧密关联。
我来补充一些其他答案没有提到的内容。这不仅仅是关于模块化的问题,也就是把相关的部分放在一起。还有一个原因是,这个方法在类的其他层级(比如子类或父类)可能是非静态的,这样就可以参与多态(根据类型来决定调用哪个方法)。所以,如果你把这个函数放在类外面,就会限制子类有效地重写它。
现在,假设你发现类 C
的方法 C.f
中不需要 self
,你有三个选择:
把它放到类外面。但我们刚才已经决定不这样做。
什么都不做:虽然不使用,但仍然保留
self
参数。声明你不使用
self
参数,同时让其他C
的方法可以通过self.f
来调用f
,这样做是为了保持将来可能会有依赖于某个实例状态的f
的重写。
第二个选项需要的概念负担更少(因为你已经需要了解 self
和方法作为绑定函数的概念,这是更一般的情况)。但你可能还是希望明确表示 self
没有被使用(而且解释器甚至可能会因此给你一些优化,因为不需要部分应用函数到 self
)。在这种情况下,你可以选择第三个选项,并在你的函数上方加上 @staticmethod
。
今天我突然发现使用 @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
的方式来调用这个方法。
这样做让你的工作变得更方便、更自然。
使用 staticmethod
的原因是,当你有一些可以作为独立函数(不属于任何类)来写的东西,但你想把它放在类里面,因为它和这个类有某种语义上的关系。(比如,它可能是一个不需要类里面任何信息的函数,但它的行为是特定于这个类的,所以子类可能想要重写它。)在很多情况下,把某些东西写成独立函数而不是静态方法也是合理的。
你的例子其实不太一样。一个关键的区别是,尽管你没有使用 self
,但你仍然需要一个实例来调用 static_add_one
—— 你不能直接用 test2.static_add_one(1)
来调用它。所以在这里确实存在行为上的差异。静态方法最主要的“竞争对手”不是一个忽略 self
的普通方法,而是一个独立函数。