Подтвердить что ты не робот

Copy.deepcopy против рассола

У меня есть древовидная структура виджетов, например. коллекция содержит модели и модель содержит виджеты. Я хочу скопировать всю коллекцию, copy.deepcopy быстрее по сравнению с 'pickle и de-pickle'ing объекта, но cPickle как написанный на C намного быстрее, поэтому

  • Почему я не должен (мы) всегда использовать cPickle вместо deepcopy?
  • Есть ли другая альтернатива? потому что рассол медленнее, чем deepcopy, но cPickle быстрее, так что может быть C-реализация deepcopy будет победителем

Пример тестового кода:

import copy
import pickle
import cPickle

class A(object): pass

d = {}
for i in range(1000):
    d[i] = A()

def copy1():
    return copy.deepcopy(d)

def copy2():
    return pickle.loads(pickle.dumps(d, -1))

def copy3():
    return cPickle.loads(cPickle.dumps(d, -1))

Тайминги:

>python -m timeit -s "import c" "c.copy1()"
10 loops, best of 3: 46.3 msec per loop

>python -m timeit -s "import c" "c.copy2()"
10 loops, best of 3: 93.3 msec per loop

>python -m timeit -s "import c" "c.copy3()"
100 loops, best of 3: 17.1 msec per loop
4b9b3361

Ответ 1

Проблема заключается в том, что pickle + unpickle может быть быстрее (в реализации C), потому что он менее общий, чем deepcopy: многие объекты могут быть подвергнуты глубокой печати, но не маринованными. Предположим, например, что ваш класс A был изменен на...:

class A(object):
  class B(object): pass
  def __init__(self): self.b = self.B()

теперь copy1 все еще работает отлично (сложность замедляет его, но его не останавливает); copy2 и copy3 break, конец трассировки стека говорит...:

  File "./c.py", line 20, in copy3
    return cPickle.loads(cPickle.dumps(d, -1))
PicklingError: Can't pickle <class 'c.B'>: attribute lookup c.B failed

I.e., травление всегда предполагает, что классы и функции являются объектами верхнего уровня в своих модулях и поэтому разжигает их "по имени" - deepcopying не делает абсолютно никаких таких предположений.

Итак, если у вас есть ситуация, когда скорость "несколько глубокого копирования" абсолютно важна, каждая миллисекунда имеет значение, и вы хотите воспользоваться специальными ограничениями, которые вы ЗНАЕТ, применимы к дублируемым объектам, например которые делают травление применимым, или те, которые предпочитают другие формы, а также сериализации и другие ярлыки, непременно идут дальше, но если вы это сделаете, вы ДОЛЖНЫ знать, что вы ограничиваете свою систему живыми этими ограничениями навсегда и документируете это дизайнерское решение очень четко и четко в интересах будущих сопровождающих.

Для случая NORMAL, где вы хотите общности, используйте deepcopy! -)

Ответ 2

Вы должны использовать deepcopy, потому что он делает ваш код более читаемым. Использование механизма сериализации для копирования объектов в памяти, по крайней мере, смущает другого разработчика, читающего ваш код. Использование метода deepcopy также означает, что вы можете воспользоваться преимуществами будущих оптимизаций в области глубокой печати. ​​

Первое правило оптимизации: не.

Ответ 3

Еще быстрее было бы избежать копирования в первую очередь. Вы упоминаете, что делаете рендеринг. Зачем нужно копировать объекты?

Ответ 4

Не всегда бывает, что cPickle быстрее, чем deepcopy(). В то время как cPickle, вероятно, всегда быстрее, чем рассол, будь то быстрее, чем глубокая копия зависит от

  • размер и уровень вложенности структур, которые нужно скопировать,
  • тип содержащихся объектов и
  • размер изображения маринованной строки.

Если что-то можно мариновать, оно, очевидно, может быть глубоко скопировано, но противоположное не так: Чтобы рассортировать что-то, он должен быть полностью сериализован; это не так для глубокой печати. В частности, вы можете эффективно реализовать __deepcopy__, скопировав структуру в памяти (подумайте о типах расширений), не имея возможности сохранить все на диск. (Подумайте о приостановке-о-RAM и приостановлении на диск.)

Известный тип расширения, который удовлетворяет вышеприведенным условиям, может быть ndarray, и действительно, он служит хорошим контрпримером для вашего наблюдения: с d = numpy.arange(100000000) ваш код дает разные времена выполнения:

In [1]: import copy, pickle, cPickle, numpy

In [2]: d = numpy.arange(100000000)

In [3]: %timeit pickle.loads(pickle.dumps(d, -1))
1 loops, best of 3: 2.95 s per loop

In [4]: %timeit cPickle.loads(cPickle.dumps(d, -1))
1 loops, best of 3: 2.37 s per loop

In [5]: %timeit copy.deepcopy(d)
1 loops, best of 3: 459 ms per loop

Если __deepcopy__ не реализовано, copy и pickle обмениваются общей инфраструктурой (см. copy_reg модуль, обсуждаемый в Отношения между рассолом и глубиной).

Ответ 5

Короткий и несколько поздний:

  • Если вы все равно должны cPickle object, вы также можете использовать метод cPickle для глубокой (но документа)

например. Вы можете подумать:

def mydeepcopy(obj):
    try:
       return cPickle.loads(cPickle.dumps(obj, -1))
    except PicklingError:
       return deepcopy(obj)