不可变对象设计的性能

dmz*_*rsk 3 python performance immutability python-3.x

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

我找到了许多禁用obj.prop = newValue行为的方法,现在我需要这个:

newObj = obj.setTitle(title)
newObj = obj.setDirection(x, y)
newObj = obj.incrementCount()
newObj = obj.swap()
Run Code Online (Sandbox Code Playgroud)

目前我这样做:

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
Run Code Online (Sandbox Code Playgroud)

在性能方面这是好的吗?是否有更快的方法返回对象的克隆,并更改了一些属性?

我用__slots__.我的对象有预定义的属性.我没有通用的.set(prop, value)方法

(欢迎使用Python 3.5+)

far*_*sil 6

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

我已经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))
Run Code Online (Sandbox Code Playgroud)

结果:

Built from scratch
1.8578372709998803
Derived from namedtuple
0.520611657999325
Run Code Online (Sandbox Code Playgroud)