我想节省时间并将对象标记为已修改,所以我编写了一个类并重写其
__setattr__
函数。
import time
class CacheObject(object):
__slots__ = ('modified', 'lastAccess')
def __init__(self):
object.__setattr__(self,'modified',False)
object.__setattr__(self,'lastAccess',time.time())
def setModified(self):
object.__setattr__(self,'modified',True)
object.__setattr__(self,'lastAccess',time.time())
def resetTime(self):
object.__setattr__(self,'lastAccess',time.time())
def __setattr__(self,name,value):
if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value:
object.__setattr__(self,name,value)
self.setModified()
class example(CacheObject):
__slots__ = ('abc',)
def __init__(self,i):
self.abc = i
super(example,self).__init__()
t = time.time()
f = example(0)
for i in range(100000):
f.abc = i
print(time.time()-t)
我测量了处理时间,花了2秒。当我注释掉重写的函数时,处理时间是0.1秒,我知道重写的函数会慢一些,但几乎20倍的差距太大了。我想我一定搞错了。
采纳cfi的建议:
消除if条件
def __setattr__(self,name,value):
# if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value:
object.__setattr__(self,name,value)
self.setModified()
这将运行时间降低到 1.9 秒,略有改善,但如果不更改则将对象标记为已修改,这会在其他进程中花费更多成本,所以不是一个选项。
将 self.func 更改为 classname.func(self)
def __setattr__(self,name,value):
if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value:
object.__setattr__(self,name,value)
CacheObject.setModified(self)
运行时间是 2.0 秒,所以没有真正改变
提取setmodified函数
def __setattr__(self,name,value):
if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value:
object.__setattr__(self,name,value)
object.__setattr__(self,'modified',True)
object.__setattr__(self,'lastAccess',time.time())
这将运行时间降低至 1.2 秒!这太棒了,确实节省了近 50% 的时间,尽管成本仍然很高。
不是完整的答案,但有一些建议:
你能消除价值比较吗?当然,这是您的实现的功能更改。但是,如果属性中存储的对象比整数更复杂,则运行时的开销会更严重。
每次通过
self
调用方法都需要经过完整的方法解析顺序检查。我不知道Python 是否可以自己做任何MRO 缓存。可能不是因为类型动态原则。因此,您应该能够通过将任何 self.method(args)
更改为 classname.method(self, args)
来减少一些开销。这消除了调用中的 MRO 开销。这适用于您的 self.setModified()
实现中的 settattr()
。在大多数地方,您已经参考 object
完成了此操作。每个函数调用都需要时间。你可以消除它们,例如将
setModified
的功能移至 __setattr__
本身。让我们知道每个项目的时间安排如何变化。我把实验分开了。
编辑:感谢您提供时间数字。
开销可能看起来很大(看起来仍然是 10 倍)。然而,将其纳入整体运行时间的角度来看。换句话说:总体运行时间中有多少时间将花在设置这些跟踪的属性上,以及有多少时间花在其他地方?
在单线程应用程序中阿姆达尔定律是一个简单的规则,可以直接设定期望。举个例子:如果1/3的时间花在设置属性上,2/3的时间花在其他事情上。那么将属性设置减慢10倍只会减慢30%。花在属性上的时间百分比越小,我们就越不需要关心。但如果您的百分比很高,这可能根本没有帮助您......
这里覆盖
__setattr__
似乎没有任何功能。您只有两个属性:modified 和 lastAccess。这意味着这是您可以设置的唯一属性,那么为什么要覆盖 __setattr__
?直接设置属性就可以了。
如果您希望在设置属性时发生某些事情,请使其成为具有 setter 和 getter 的属性。这更容易,也更不那么神奇。
class CacheObject(object):
__slots__ = ('modified', 'lastAccess')
def __init__(self):
self.modified = False
self.lastAccess = time.time()
def setModified(self):
self.modified = True
self.lastAccess = time.time()
def resetTime(self):
self.lastAccess = time.time()
class example(CacheObject):
__slots__ = ('_abc',)
def __init__(self,i):
self._abc = i
super(example,self).__init__()
@property
def abc(self):
self.resetTime()
return self._abc
@abc.setter
def abc(self, value):
self.setModified()
self._abc = value
老问题但值得更新。
我使用 python 3.6 遇到了与 pydantic 相同的问题。
object.__setattr__(self, name, value)
只是比通常在类上设置属性慢。没有明显的解决办法。
如果性能很重要,唯一的选择是在需要覆盖
object.__setattr__(self, name, value)
的类中将对 _setattr_
的调用保持在绝对最低限度。