将Python 2.7中继承unicode的子类移植到Python 3.6的问题
解决Python 3中无法继承unicode(替换为str)的问题
我刚帮别人处理过几乎一模一样的移植问题,太懂你的痛点了!Python 2里继承unicode的子类到Python 3直接换str确实会踩坑,核心原因是str是不可变类型,它的初始化逻辑和Python 2的unicode有本质区别——str的实例创建是靠__new__方法,而不是__init__,直接在__init__里传字符串参数肯定会报错。
下面给你两种靠谱的解决方案,按需选择:
方案1:封装str而非继承(优先推荐)
这种方式更灵活,完全避开不可变类型的限制,适合原来的unicode子类有较多自定义逻辑的场景。我们自己写一个类,内部封装一个str实例,然后实现需要的str方法(比如__str__、__len__、切片等):
class MyStrWrapper: def __init__(self, value): # 先把输入转为标准str,兼容各种传入类型 self._inner_str = str(value) # 这里放你原来在unicode子类__init__里的自定义逻辑 self.custom_attr = self._process_value(value) # 模拟str的核心方法,按需添加 def __str__(self): return self._inner_str def __repr__(self): return f"MyStrWrapper({repr(self._inner_str)})" def __len__(self): return len(self._inner_str) def __getitem__(self, idx): return self._inner_str[idx] # 比如原来的unicode子类有自定义方法,直接搬过来 def _process_value(self, value): return f"processed_{value}"
使用起来和原来的子类完全一致:
obj = MyStrWrapper("hello world") print(obj) # 输出 hello world print(len(obj)) # 输出 11 print(obj.custom_attr) # 输出 processed_hello world
方案2:继承str,重写__new__方法
如果你的代码严重依赖继承str的类型(比如需要和其他str兼容的场景),那必须用__new__来处理实例创建,因为str的__init__不接受额外参数:
class MyStr(str): def __new__(cls, value): # 这里处理输入值,转为str后传给父类的__new__创建实例 processed_value = str(value) # 调用父类的__new__生成str实例 return super().__new__(cls, processed_value) def __init__(self, value): # 注意:str的__init__不需要参数,这里只处理自定义属性 super().__init__() # 可以不传参数,或者干脆不写这行 # 原来的自定义逻辑放在这里 self.custom_attr = f"processed_{value}"
使用示例:
obj = MyStr("test") print(obj) # 输出 test print(isinstance(obj, str)) # 输出 True,完全兼容str类型 print(obj.custom_attr) # 输出 processed_test
为什么原来的方法会报错?
Python 3中str是不可变序列,它的__init__方法仅用于初始化已创建的实例,但实际上什么都不做——因为不可变对象创建后就不能修改了。所以当你继承str后在__init__里传入字符串参数,会触发TypeError: str() takes at most 1 argument (2 given),本质是父类str的__init__不接受额外参数。
而Python 2的unicode虽然也是不可变,但它的__init__允许传入参数(实际上也只是做一些无关紧要的初始化),这就导致了移植时的差异。
内容的提问来源于stack exchange,提问作者Nathaniel Ford
相关产品推荐
相关产品推荐

