【问题标题】:Most efficient and most Pythonic way to deep copy class instance and perform additional manipulation深度复制类实例和执行额外操作的最有效和最 Pythonic 的方法
【发布时间】:2020-09-24 13:13:17
【问题描述】:

假设我有一个 Python 类实例,这是一个非常简单的示例:

class Book:
    def __init__(self, genre):
        self.genre = genre
        self.author = self.title = None
        return

    def assign_author(self, author_name):
        self.author = author_name
        return

    def assign_title(self, title_name):
        self.title = title_name
        return

western_book_template = Book("Western")

出于各种原因,我希望每个类实例能够生成自身的深层副本,然后对新对象执行一些额外的操作。

以下是我想知道的:

  1. 最有效的方法是什么?
  2. 最 Pythonic 的一个(最佳实践)呢?
  3. 存储在实例中的数据量以及为获取该数据而执行的计算量是否会影响先前的答案?如果有,怎么做?

我知道我可以使用western_book_1 = copy.deepcopy(western_book_template),然后直接在western_book_1 上执行我想要的所有其他操作,但是这样做不是更好吗:

class Book:
    def __init__(self, genre):
        self.genre = genre
        self.author = self.title = None
        return

    def assign_author(self, author_name):
        self.author = author_name
        return

    def assign_title(self, title_name):
        self.title = title_name
        return

    def generate_specific_book(self, author_name, title_name):
        specific_book = Book(self.genre)
        specific_book.assign_author(author_name)
        specific_book.assign_title(title_name)
        return specific_book

western_book_template = Book("Western")
western_book_1 = western_book_template.generate_specific_book("John E. Williams", "Butcher's Crossing")

这将使我能够更好地控制复制的内容和不复制的内容,并允许我在一个地方执行所有额外的操作。我假设在需要大量计算来获取存储在实例中的数据的情况下,上述方法效率低下。

【问题讨论】:

  • 关于您对效率的担忧,我不确定我是否关注您。如果您必须进行大量计算来生成数据,那么这就是您必须做的,否则您如何避免呢?无论如何,以上对我来说似乎很好而且很正常。顺便说一句,不要在 python 中编写像assign_author 这样的方法,即setter。或吸气剂。
  • 在效率和数据量方面,如果类实例中包含一些计算时间较长的数据,那么直接复制数据的值而不是经过再次生成所述数据。在这种情况下,deepcopy 肯定会比其他选项更有效。我很好奇你对 setter 和 getter 的评论。如果有一些在类实例外部计算的属性要分配给类实例,那么不使用 setter/getter 的方法是什么?谢谢!

标签: python oop deep-copy


【解决方案1】:

您可能正在寻找cls.__new__() 方法。它允许您控制实例创建。你可以调用这个函数来创建一个空白实例,然后用你原始实例的字典来更新这个实例的字典:

这是一个例子:

class A:
    def __init__(self, a):
        self.a = a

    def newA(self):
        b = type(self).__new__(type(self))
        b.__dict__.update(self.__dict__)
        return b


x = A(42)
x.a # 42
y = x.newA()
y.a # 42

这与深度复制有很大不同,但我建议这样做是因为您的示例和您提出问题的方式。实际上没有进行复制——新实例包含对与旧实例相同数据的引用,使其容易受到副作用的影响。但我提供它是因为在您给出的示例中,您创建的新类基于self.genre,这表明您并不关心新实例中的数据是否是副本 em> 与生成新实例的计算成本有关。

【讨论】:

    【解决方案2】:
    1. 使用模式很重要。如果我刚开始考虑项目中大型对象的深层副本的效率,我更喜欢lenses。这个想法是,您将与类似于Book 的东西进行交互,而不是直接与Book 交互,并且在您修改它时在内部跟踪不可变的增量和变更集。

    2. 这是有争议的,但标准库中的 copy.deepcopy 在 Python 代码方面很难被击败。

    3. 当然,没有问题。

    3a。作为一个有点病态的例子,如果你的类只存储几个字节,那么直接复制整个类并不比你要对它做的任何修改更昂贵。你的 deepcopy 策略是复制它并做你需要做的事情——pythonic 解决方案也将是大多数指标的“最佳”解决方案。

    3b。如果您为每个类存储 10**10 字节并且只修改 5 个字节,那么与您正在做的工作相比,完整副本可能非常昂贵。镜头看起来真的很有吸引力(因为它们只会保存对原始对象的引用和 5 字节的变化)。

    3c。您提到数据访问时间是一个潜在因素。我将在这里使用一个长链表作为示例——完整的深度复制和镜头在这里都表现不佳,因为它们需要遍历整个列表以进行任何幼稚的实现。如果您知道需要对链表进行深度复制,尽管您可以提出某种不可变的树结构将链表一分为二并使用它来采用准透镜策略,只交换您更改的树的叶子/分支。

    3*。进一步详细说明示例,您正在为代码中的一组对象选择一种表示形式,可以将其概念化为一系列深拷贝和修改。有一些经典方法(复制所有数据并对副本进行更改,保留数据并记录所有更改)适用于具有某些性能特征的任意对象,但如果您有关于问题的其他信息(例如,这些都是大数组,只有一个坐标变化)你可以用它来设计一个最适合你的问题的表示,更重要的是你想要的性能权衡。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-29
      • 2011-08-04
      • 1970-01-01
      • 1970-01-01
      • 2017-11-22
      • 2011-02-10
      • 1970-01-01
      相关资源
      最近更新 更多