@staticmethod是否在CPython或Micropython中保存任何ram ?



在回答最近的一个问题时,我重复了我的假设,即使用@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作为描述符,在每次查找方法时运行。