2

我想节省时间并将对象标记为已修改,因此我编写了一个类并覆盖了它的__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 的建议

1.消除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,略有改进,但如果不更改则标记对象已修改会在其他进程中花费更多,所以不是一个选项。

2.将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 。所以没有什么真正改变

3)提取集修改函数

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% 的时间,尽管成本仍然很高。

4

3 回答 3

1

不是一个完整的答案,但有一些建议:

  1. 你能消除价值比较吗?当然,这是您的实现的功能更改。但是,如果比整数更复杂的对象存储在属性中,则运行时的开销会更加严重。

  2. 对方法的每次调用 viaself都需要经过完整的方法解析顺序检查。我不知道 Python 是否可以自己做任何 MRO 缓存。可能不是因为类型存在动态原则。因此,您应该能够通过将 any 更改self.method(args)classname.method(self, args). 这消除了呼叫中的 MRO 开销。这适用self.setModified()于您的settattr()实施。在大多数地方,您已经通过引用object.

  3. 每个函数调用都需要时间。您可以消除它们,例如 movesetModified的功能__setattr__本身。

让我们知道每一个的时间如何变化。我把实验分开了。

编辑:感谢您的计时数字。

开销可能看起来很大(似乎仍然是 10 倍)。但是,从整体运行时间的角度来看。换句话说:你有多少整体运行时间将用于设置那些被跟踪的属性,有多少时间花在其他地方?

在单线程应用程序中,阿姆达尔定律是一个简单的规则,可以直接设置期望。举例说明:如果 1/3 的时间花在设置属性上,而 2/3 的时间花在做其他事情上。那么将属性设置减慢10倍只会减慢30%。花在属性上的时间百分比越小,我们就越不需要关心。但是,如果您的百分比很高,这可能对您毫无帮助...

于 2012-11-02T13:33:16.617 回答
0

在这里覆盖__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
于 2012-11-02T15:22:02.537 回答
0

老问题,但值得更新。

我使用 python 3.6遇到了与pydantic相同的问题。

object.__setattr__(self, name, value)只是比通常在类上设置属性慢。没有明显的办法。

object.__setattr__(self, name, value)如果性能很重要,唯一的选择是在需要覆盖的类中将调用保持在绝对最小值_setattr_

于 2017-06-07T21:38:59.627 回答