在回答最近的一个问题时,我重复了我的假设,即使用@staticmethod
的一个原因是为了节省内存,因为静态方法只实例化一次。这个断言可以很容易地在网上找到(例如在这里),我不知道我第一次遇到它是在哪里。
我的推理是基于两个假设,一个是错误的:a. python在实例化类时实例化了所有方法(这不是情况,正如一点想法所显示的那样,哎呀);b.静态方法在访问时不会实例化,而只是直接调用。因此,我认为这段代码:
import asyncio
class Test:
async def meth1():
await asyncio.sleep(10)
return 78
t1= Test()
t2 = Test()
loop = asyncio.get_event_loop
loop.create_task(t1)
loop.create_task(t2)
def main():
for _ in range(10):
await asyncio.sleep(2)
loop.run(main())
将比我这样定义类使用更多的内存:
class Test:
@staticmethod
async def meth1():
await asyncio.sleep(10)
return 78
是这样吗?静态方法在访问时实例化吗?类方法在访问时实例化吗?我知道t1.meth1 is t2.meth1
将在第二种情况下返回True
,在第一种情况下返回False
,但这是因为python第一次实例化meth1
,然后在第二次查找它,或者因为在这两种情况下它只是查找它,或者因为在这两种情况下它都得到静态方法的副本,这在某种程度上是相同的(我认为不是吗?)静态方法的id
似乎没有改变:但我不确定我对它的访问是在做什么。
有什么现实世界的理由去关心它吗?我在micropython代码中看到过大量的静态方法,其中异步代码中同时存在多个实例。我以为这是为了节省ram,但我怀疑我错了。我很想知道micropython和Cpython在这里的实现是否有什么不同。
编辑我认为调用t1.meth1()
和t2.meth1()
将在第一个实例中绑定两次方法,在第二个实例中绑定一次方法是正确的?
方法不会被"实例化",它们会被绑定——这是"它们的self
/cls
参数被填充"的花哨说法,类似于partial
参数绑定。staticmethod
的全部意义在于没有self
/cls
参数,因此不需要绑定。
实际上,获取staticmethod
什么也不做——它只是返回原封不动的函数:
>>> class Test:
... @staticmethod
... async def meth1():
... await asyncio.sleep(10)
... return 78
...
>>> Test.meth1
<function __main__.Test.meth1()>
由于方法是按需绑定的,所以它们通常不会以绑定的形式存在。因此,只使用方法不需要支付内存成本,staticmethod
也不需要回收。因为staticmethod
是查找过程中的一个实际层,即使它什么都不做,也不会因为使用(不)staticmethod
而获得性能增益。
In [40]: class Test:
...: @staticmethod
...: def s_method():
...: pass
...: def i_method(self):
...: pass
...:
In [41]: %timeit Test.s_method
42.1 ns ± 0.576 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [42]: %timeit Test.i_method
40.9 ns ± 0.202 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
请注意,这些时间可能会根据实现和测试设置略有不同。结论是,两种方法都相当快,选择其中一种而不是另一种与性能无关。
¹staticmethod
作为描述符,在每次查找方法时运行。