将Unicode的子类移植到Python 3

时间:2018-02-28 21:06:07

标签: python python-3.x python-2.7

我正在将遗留代码库从Python 2.7移植到Python 3.6。在该代码库中,我有许多例子:

class EntityName(unicode):

    @staticmethod
    def __new__(cls, s):
        clean = cls.strip_junk(s)
        return super(EntityName, cls).__new__(cls, clean)

    def __init__(self, s):
        self._clean = s
        self._normalized = normalized_name(self._clean)
        self._simplified = simplified_name(self._clean)
        self._is_all_caps = None
        self._is_all_lower = None
        super(EntityName, self).__init__(self._clean)

可能会这样调用:

EntityName("Guy DeFalt")

将此代码移植到Python 3时,上述代码失败,因为unicode不再是可以扩展的类(至少,如果有一个我找不到的等效类)。鉴于str现在是unicode,我试图只交换str,但是父init不接受我试图传递的字符串值:

TypeError: object.__init__() takes no parameters

这是有道理的,因为str没有__init__方法 - 这似乎不是使用此类的惯用方式。所以我的问题有两个主要分支:

  • 是否有更好的方法来移植分类旧的unicode类的类?
  • 如果子类化str是合适的,我应该如何修改__init__函数的惯用行为?

1 个答案:

答案 0 :(得分:0)

在Python 3中子类化字符串或另一个不可变类的正确方法与Python 2中相同:

class MyString(str):
  def __new__(cls, initial_arguments): # no staticmethod
     desired_string_value = get_desired_string_value(initial_arguments)
     return super(MyString, cls).__new__(cls, desired_string_value)
     # can be shortened to super().__new__(...)
  def __init__(self, initial_arguments): # arguments are unused
     self.whatever = whatever(self)
     # no need to call super().__init__(), but if you must, do not pass arguments 

您的示例有几个问题。首先,为什么__new__@staticmethod?它是@classmethod,尽管您不需要指定它。其次,该代码似乎在这样的假设下运行:当您调用超类的__new__时,它也会以某种方式调用您的__init__。我是通过查看应该如何设置self._clean来得出的。不是这种情况。当您致电MyString(arguments)时,会发生以下情况:

  • 第一个Python使用类参数(通常称为__new__)和cls调用arguments__new__必须返回类实例。为此,它可以像我们一样创建它,或者做其他事情。例如它可能会返回一个现有的,或者实际上是任何东西。
  • 然后Python使用从__init__接收到的实例(此参数通常称为__new__)和相同的self调用arguments
  • (有一种特殊情况:如果__init__返回的不是所传递类的子类,Python不会调用__new__。)

Python使用类层次结构来查看要调用的__new____init__。正确选择参数并在这两种方法中使用正确的超类调用取决于您。