2016-11-27 44 views
3

我的应用程序中需要很多小对象。它们必须是不可变的,并在设置新属性时返回新实例。性能不可变的对象设计

我找到了很多方法来禁用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 

这是很好的性能比较方面?有没有更快的方法来返回一些属性更改的对象的克隆?我使用__slots__。我的对象有预定义的属性。我有没有通用.set(prop, value)方法

(Python的3.5+欢迎)

+0

为什么你需要你的对象是不可变的?在OOP中,对象应该是可变的。如果你想要不变性,类和对象不是你正在寻找的数据结构。你可能会做一些metaclass hack来使它工作,但你不会保证不可变性或者获得不可变数据类型的任何好处。 –

+0

有一些有趣的项目向python引入了持久性/不可变性/功能性数据结构。你可以看看它是如何完成的,例如“pyrsistent”:https://github.com/tobgu/pyrsistent –

回答

5

为了获得真正的不变性,我宁愿继承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) 

我已经基准性能该swap()方法在两个类和类从namedtuple派生的是在Python 3.这里的基准代码快约3-4倍:

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 
3

您可以通过定义为对象的自定义副本方法得到显著的性能提升:

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