deepcopy()是非常强大的

2024-05-19 10:28:46 发布

您现在位置:Python中文网/ 问答频道 /正文

我在Python中有一个游戏状态,大约有1000个对象(行星系统+恒星+行星),我需要复制它,并在需要时对它应用一系列转换。然而,在大约每秒1个请求时,这是taking up 24.63% of my runtime。我怎样才能让它走得快?请注意,复制较少不是一个选项,因为转换几乎触及所有内容。

编辑:通过明智地对事物执行__deepcopy__,将其降低到8%。不过,还不够好。(足够好的是1%或更少,我计划在这方面投入更多的东西。)timeit说每deepcopy()41.8ms。


Tags: of对象游戏编辑内容my状态系统
3条回答

我做了一个快速的实验,比较了几个案例中deepcopy/json/ujson的结果,我的结果与@prehive在某些案例中的结果相矛盾,在这里发布了这个小实验:

import ujson
import timeit
import json
import random
import string
import copy
import ujson
import sys


def random_string(N):
    return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))


def random_json(width=5, height=5, levels=1):
    dct = {}
    lst = [random_string(4) for i in range(width)]
    lst2 = [random.randint(0, 10000) for i in range(width)]
    lst3 = [bool(random.randint(0, 1)) for i in range(width)]
    for j in range(height):
        dct[str(j)] = lst
        dct[str(width+j)] = lst2
        dct[str(2*width+j)] = lst3

    for i in range(levels):
        new_dct = {}
        for j in range(height):
            new_dct[str(j)] = dct
        dct = json.loads(json.dumps(new_dct))

    return new_dct

if __name__ == "__main__":
    print(sys.version)
    levels = 3
    for i in range(15):
        dataset = random_json(i, i, levels)
        print("Comparing deepcopy/ujson/json using random dataset({},{},{}), length {}".format(i,i,levels, len(json.dumps(dataset))))
        print(timeit.timeit('copy.deepcopy(dataset)',
                            setup='from __main__ import copy, dataset', number=10))
        print(timeit.timeit('ujson.loads(ujson.dumps(dataset))',
                            setup='from __main__ import ujson, dataset', number=10))
        print(timeit.timeit('json.loads(json.dumps(dataset))',
                            setup='from __main__ import json, dataset', number=10))
        print()

结果是:

3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)]
Comparing deepcopy/ujson/json using random dataset(0,0,3), length 2
2.6842977659931844e-05
0.00012039864979822371
7.776568527950847e-05

Comparing deepcopy/ujson/json using random dataset(1,1,3), length 63
0.0002731667726569534
3.552747043226263e-05
0.00012987264191349377

Comparing deepcopy/ujson/json using random dataset(2,2,3), length 1106
0.0011858280130946362
0.00034974820892205325
0.0007093651596308467

Comparing deepcopy/ujson/json using random dataset(3,3,3), length 6834
0.0042218477363672215
0.0021178319874343293
0.003378267688436718

Comparing deepcopy/ujson/json using random dataset(4,4,3), length 26572
0.011379054029782284
0.006288757016181971
0.009920059244030693

Comparing deepcopy/ujson/json using random dataset(5,5,3), length 79210
0.028879491215043435
0.027906433274870912
0.029595961868760734

Comparing deepcopy/ujson/json using random dataset(6,6,3), length 183678
0.047142979515255284
0.04682125853300759
0.06791747047568517

Comparing deepcopy/ujson/json using random dataset(7,7,3), length 395528
0.08239215142913198
0.09871347134571351
0.15347433002098887

Comparing deepcopy/ujson/json using random dataset(8,8,3), length 764920
0.1351954464835896
0.19448842613700734
0.3020533693660834

Comparing deepcopy/ujson/json using random dataset(9,9,3), length 1356570
0.24560258734724671
0.44074906118659407
0.5705849913806413

Comparing deepcopy/ujson/json using random dataset(10,10,3), length 2287770
0.3237815755327835
0.61104051671153
0.8698565598118777

Comparing deepcopy/ujson/json using random dataset(11,11,3), length 3598750
0.4958284828467452
0.9472223636741877
1.2514314609961668

Comparing deepcopy/ujson/json using random dataset(12,12,3), length 5636414
0.6261448233909714
1.4066722957969802
1.8636325417418167

Comparing deepcopy/ujson/json using random dataset(13,13,3), length 8220800
0.8396582099444547
2.061675688670409
2.755659427352441

Comparing deepcopy/ujson/json using random dataset(14,14,3), length 12018290
1.0951926990258762
2.96703050743886
4.088875914783021

这个小实验的结论是:

  • 当字典很小的时候time(ujson)<time(json)<time(deepcopy)
  • 当字典是大字典时time(deepcopy)<time(ujson)<time(json)

因此,这取决于每秒生成的副本数量和处理的词典类型,您更喜欢在deepcopy或ujson之间切换。

实际上,deepcopy非常慢。但我们可以使用json、ujson或cPickle。 我们可以使用json/cPickle转储一个对象,稍后再加载它。 这是我的测试:

Total time: 3.46068 s
File: test_deepcopy.py
Function: test at line 15
Line #   Hits          Time Per Hit   % Time  Line Contents
==============================================================
15                                             @profile
16                                             def test():
17       100       957585   9575.9     27.7        b = deepcopy(a)
18       100          862      8.6      0.0        c = copy(a)
19       100        42295    422.9      1.2        d = ujson.loads(ujson.dumps(a))
20       100        85040    850.4      2.5        e = json.loads(json.dumps(a))
21       100      2323465  23234.7     67.1        f = pickle.loads(pickle.dumps(a, -1))
22       100        51434    514.3      1.5        g = cPickle.loads(cPickle.dumps(a, -1))

我们可以看到,json/ujson/cPickle比deepcopy快,但是pickle。。。

如果创建自己的类来保存这些对象,则可以创建自己的方法来处理复制和深度复制。http://www.rafekettler.com/magicmethods.html#copying(断开链接)

github存储库的新链接https://github.com/RafeKettler/magicmethods

class MyClass():
    def __copy__(self):
        copy_object = MyClass()
        return copy_object

    def __deepcopy__(self, memodict={}):
        copy_object = MyClass()
        copy_object.value = self.value
        return copy_object

if __name__ == "__main__":
    my_inst = MyClass()
    print(copy.deepcopy(my_inst))

下面是以前断开的链接的类似描述。

复制

有时,特别是在处理可变对象时,您希望能够复制对象并进行更改,而不会影响从中复制的内容。这就是Python的副本发挥作用的地方。然而(幸运的是),Python模块没有感知能力,因此我们不必担心基于Linux的机器人起义,但我们必须告诉Python如何有效地复制东西。

__copy__(self)

为类的实例定义copy.copy()的行为。copy.copy()返回对象的浅层副本——这意味着,当实例本身是一个新实例时,它的所有数据都被引用——即,对象本身被复制,但它的数据仍然被引用(因此浅层副本中的数据更改可能导致原始副本的更改)。

__deepcopy__(self, memodict={})

为类的实例定义copy.deepcopy()的行为。copy.deepcopy()返回对象的深度副本——对象及其数据都被复制。memodict是以前复制的对象的缓存——这优化了复制,并防止在复制递归数据结构时出现无限递归。如果要深入复制单个属性,请对该属性调用copy.deepcopy(),并将memodict作为第一个参数。 这些神奇方法有哪些用例?与往常一样,在任何情况下,您都需要比默认行为提供的更细粒度的控制。例如,如果您试图复制一个将缓存存储为字典的对象(可能很大),那么复制缓存也可能没有意义——如果缓存可以在实例之间的内存中共享,那么它应该是。

相关问题 更多 >

    热门问题