TypeError: 无法深层复制此模式对象。

18

尝试理解我“Variable”类中的错误。

我希望在我的“Variable”类中存储sre.SRE_Pattern。我刚开始复制Variable类并注意到它会导致所有Variable类实例发生变化。我现在知道我需要深拷贝(deepcopy)这个类,但现在我遇到了“TypeError:无法深度复制此模式对象”的问题。我可以将该模式作为文本字符串存储,但是我的其他代码已经期望编译后的模式!如何以最佳方式复制具有模式对象的Variable类?

import re
from copy import deepcopy

class VariableWithRE(object):
    "general variable class"
    def __init__(self,name,regexTarget,type):
        self.name = name
        self.regexTarget = re.compile(regexTarget, re.U|re.M) 
        self.type = type 

class VariableWithoutRE(object):
    "general variable class"
    def __init__(self,name,regexTarget,type):
        self.name = name
        self.regexTarget = regexTarget
        self.type = type 

if __name__ == "__main__":

    myVariable = VariableWithoutRE("myName","myRegexSearch","myType")
    myVariableCopy = deepcopy(myVariable)

    myVariable = VariableWithRE("myName","myRegexSearch","myType")
    myVariableCopy = deepcopy(myVariable)

鉴于编译器正则表达式是不可变的,因此不必进行深拷贝。但是我不记得如何告诉 deepcopy() 如何处理特定类型(但请注意,如果需要,你可以向内置类型添加属性)。 - Jan Hudec
你的代码哪一行抛出异常了?我已经复制并编译过了,没有错误。唯一的问题是你尝试使用一个重写Python方法'type'的变量,这不是很好的风格。 - Artsiom Rudzenka
在Python 2.6中,最后一行会抛出错误。 - Marcus Jones
5个回答

11

deepcopy 不知道你的类的任何信息,也不知道如何复制它们。

你可以通过实现 __deepcopy__() 方法来告诉 deepcopy 如何复制你的对象:

class VariableWithoutRE(object):
   # ...
   def __deepcopy__(self):
      return VariableWithoutRE(self.name, self.regexTarget, self.type)

问题是复制VariableWithRE,所以你的例子并没有真正帮助到,但它朝着正确的方向前进。 - Jochen Ritzel
我在我的两个类中尝试了这种方法,首先我收到了“TypeError:__deepcopy __()需要一个参数(给定2个)”,然后我看了一下这个问题,并尝试了“def __deepcopy __(self,memo)”。语法对我这样的仍在学习Python的人来说似乎很奇怪,但好吧,备忘录,好的。这在VariableWithoutRE类中起作用,但VariableWithRE类仍有问题!新错误:“ValueError:无法使用编译的模式处理标志参数” - Marcus Jones
2
@user789215 问题在于你正在使用已编译的正则表达式(self.regexTarget)调用“VariableWithoutRE”构造函数(init),而构造函数期望一个字符串。 - ThomasH

8
这可以通过在Python 3.7之前的版本中修改copy模块来解决:
import copy
import re 

copy._deepcopy_dispatch[type(re.compile(''))] = lambda r, _: r

o = re.compile('foo')
assert copy.deepcopy(o) == o

这会创建一个新实例吗?从我的测试来看是这样的,但代码表明它是同一实例? - Markus Ressel
1
@MarkusRessel:它并不会——正如assert copy.deepcopy(o) is o所示。你是如何测试的? - Adam Sosnowski
你说得对,我的测试是错误的。re.compile() 方法有一个内置缓存,因此即使在 lambda 中使用类似 re.compile(r.pattern) 的东西,如果模式(和标志)相同,则不会创建新实例。我想知道 Python 3.7 是否也是这样做的。 - Markus Ressel
这实际上是相同的(即:复制被实现为标识),但在 re 模块的本机代码中实现: https://github.com/python/cpython/commit/fdbd01151dbd5feea3e4c0316d102db3d2a2a412 - Adam Sosnowski

5
这个问题在Python 3.7+版本中似乎已经得到解决:
编译的正则表达式和匹配对象现在可以使用copy.copy()和copy.deepcopy()进行复制。(由Serhiy Storchaka在bpo-10076中贡献。)
具体请见:https://docs.python.org/3/whatsnew/3.7.html#re 测试:
import re,copy

class C():
    def __init__(self):
       self.regex=re.compile('\d+')

myobj = C()    
foo = copy.deepcopy(myobj)
foo.regex == myobj.regex
# True

0
问题似乎在于编译的正则表达式。 deepcopy 无法处理它们。
一个最小的示例也会给我相同的错误:
import re,copy
class C():
    def __init__(self):
        self.regex=re.compile('\d+')

myobj = C()    
copy.deepcopy(myobj)

这会抛出错误:TypeError: 无法深复制此模式对象。我在 Python3.5 中。


我遇到了相同的问题。 - April Polubiec

0

如果您的此类实例(及其子类)不需要深度复制,但仅因为它们是需要深度复制的对象图的一部分而导致问题,那么您可以这样做:

#might as well limit this hack to versions that need it...
if sys.version_info <= (3, 7):

    def __deepcopy__(self, *args, **kwargs):
        """ cheat here because regex can't be deepcopied"""
        return self

现在,你需要小心对待我所做的假设。如果你开始修改这个类的任何实例,你就有可能出现副作用,因为深拷贝并没有真正发生。只有在你需要在其他地方进行深拷贝,并且你非常确定你不关心这个类和深拷贝时,才值得这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接