性能不可变对象设计
Immutable object design for performance
我的应用程序中需要很多小对象。它们必须是不可变的,并且 return 当我设置新属性时是一个新实例。
我找到了很多禁用 obj.prop = newValue
行为的方法,现在我需要这个:
newObj = obj.setTitle(title)
newObj = obj.setDirection(x, y)
newObj = obj.incrementCount()
newObj = obj.swap()
目前我这样做:
class Info(object):
__slots__ = ['_x', '_y', ...]
def setDirection(self, x, y):
newObj = copy.copy(self) # shallow copy is OK
newObj._x = x
newObj._y = y
return newObj
def swap(self):
newObj = copy.copy(self)
# methods can do different things
newObj._x, newObj._y = self._y, self._x
return newObj
这在性能方面好吗?是否有任何更快的方法来 return 更改某些属性的对象的克隆?
我用__slots__
。我的对象具有预定义的属性。我没有通用 .set(prop, value)
方法
(Python 欢迎 3.5+)
为了获得真正的不变性,我宁愿subclass collections.namedtuple
并使用方法_replace()
:
import collections as co
# this will create a class with five attributes
class Info(co.namedtuple('BaseInfo', 'x y a b c')):
__slots__ = ()
def setDirection(self, x, y):
return self._replace(x=x, y=y)
def swap(self):
return self._replace(x=self.y, y=self.x)
我已经在两个 classes 中对 swap()
方法的性能进行了基准测试,并且从 namedtuple
派生的 class 大约快 3-4 倍python 3. 这是基准代码:
import copy
import collections as co
class Info(object):
__slots__ = ['x', 'y', 'a', 'b', 'c']
def swap(self):
newObj = copy.copy(self)
newObj.x, newObj.y = self.y, self.x
return newObj
# for the sake of convenience
def __init__(self, x, y, a, b, c):
self.x = x
self.y = y
class TupleInfo(co.namedtuple('BaseInfo', 'x y a b c')):
__slots__ = ()
def swap(self):
return self._replace(x=self.y, y=self.x)
if __name__ == "__main__":
from timeit import timeit
i1 = Info(1, 2, 0, 0, 0)
i2 = TupleInfo(1, 2, 0, 0, 0)
print("Built from scratch")
print(timeit("z = i1.swap()", "from __main__ import i1", number=100000))
print("Derived from namedtuple")
print(timeit("z = i2.swap()", "from __main__ import i2", number=100000))
结果:
Built from scratch
1.8578372709998803
Derived from namedtuple
0.520611657999325
您可以通过为对象定义自定义复制方法来显着提高性能:
class Info(object):
__slots__ = ['x', 'y', 'z']
def swap(self):
newObj = self.copy()
newObj.x, newObj.y = self.y, self.x
return newObj
def copy(self):
clone = type(self)()
for slot in self.__slots__:
if hasattr(self, slot):
setattr(clone, slot, getattr(self, slot))
return clone
测试:
i = Info()
i.x = i.y = 4
def fn1(i=i, copy=copy.copy): return copy(i)
def fn2(i=i): return i.copy()
print('copy.copy')
print(timeit('fn1()', 'from __main__ import fn1', number=100000))
print('custom copy')
print(timeit('fn2()', 'from __main__ import fn2', number=100000))
结果:
copy.copy
1.5658336669985147
custom copy
0.4359149369993247
我的应用程序中需要很多小对象。它们必须是不可变的,并且 return 当我设置新属性时是一个新实例。
我找到了很多禁用 obj.prop = newValue
行为的方法,现在我需要这个:
newObj = obj.setTitle(title)
newObj = obj.setDirection(x, y)
newObj = obj.incrementCount()
newObj = obj.swap()
目前我这样做:
class Info(object):
__slots__ = ['_x', '_y', ...]
def setDirection(self, x, y):
newObj = copy.copy(self) # shallow copy is OK
newObj._x = x
newObj._y = y
return newObj
def swap(self):
newObj = copy.copy(self)
# methods can do different things
newObj._x, newObj._y = self._y, self._x
return newObj
这在性能方面好吗?是否有任何更快的方法来 return 更改某些属性的对象的克隆?
我用__slots__
。我的对象具有预定义的属性。我没有通用 .set(prop, value)
方法
(Python 欢迎 3.5+)
为了获得真正的不变性,我宁愿subclass collections.namedtuple
并使用方法_replace()
:
import collections as co
# this will create a class with five attributes
class Info(co.namedtuple('BaseInfo', 'x y a b c')):
__slots__ = ()
def setDirection(self, x, y):
return self._replace(x=x, y=y)
def swap(self):
return self._replace(x=self.y, y=self.x)
我已经在两个 classes 中对 swap()
方法的性能进行了基准测试,并且从 namedtuple
派生的 class 大约快 3-4 倍python 3. 这是基准代码:
import copy
import collections as co
class Info(object):
__slots__ = ['x', 'y', 'a', 'b', 'c']
def swap(self):
newObj = copy.copy(self)
newObj.x, newObj.y = self.y, self.x
return newObj
# for the sake of convenience
def __init__(self, x, y, a, b, c):
self.x = x
self.y = y
class TupleInfo(co.namedtuple('BaseInfo', 'x y a b c')):
__slots__ = ()
def swap(self):
return self._replace(x=self.y, y=self.x)
if __name__ == "__main__":
from timeit import timeit
i1 = Info(1, 2, 0, 0, 0)
i2 = TupleInfo(1, 2, 0, 0, 0)
print("Built from scratch")
print(timeit("z = i1.swap()", "from __main__ import i1", number=100000))
print("Derived from namedtuple")
print(timeit("z = i2.swap()", "from __main__ import i2", number=100000))
结果:
Built from scratch
1.8578372709998803
Derived from namedtuple
0.520611657999325
您可以通过为对象定义自定义复制方法来显着提高性能:
class Info(object):
__slots__ = ['x', 'y', 'z']
def swap(self):
newObj = self.copy()
newObj.x, newObj.y = self.y, self.x
return newObj
def copy(self):
clone = type(self)()
for slot in self.__slots__:
if hasattr(self, slot):
setattr(clone, slot, getattr(self, slot))
return clone
测试:
i = Info()
i.x = i.y = 4
def fn1(i=i, copy=copy.copy): return copy(i)
def fn2(i=i): return i.copy()
print('copy.copy')
print(timeit('fn1()', 'from __main__ import fn1', number=100000))
print('custom copy')
print(timeit('fn2()', 'from __main__ import fn2', number=100000))
结果:
copy.copy
1.5658336669985147
custom copy
0.4359149369993247