从str或int继承

72 投票
6 回答
37965 浏览
提问于 2025-04-15 21:49

为什么我在创建一个继承自字符串(str)或者整数(int)的类时遇到问题

class C(str):
   def __init__(self, a, b):
     str.__init__(self,a)
     self.b = b

C("a", "B")

TypeError: str() takes at most 1 argument (2 given)

如果我尝试用 int 代替 str,情况也是一样,但用自定义类就没问题。我需要用 __new__ 而不是 __init__ 吗?为什么?

6 个回答

14

当你创建一个类的实例时,你传入的参数会同时传递给这个类的两个方法:__new__(构造器)和__init__(初始化器)。所以,如果你继承了一个在实例化时对参数数量有要求的类,你就得确保它的__new____init__方法不会收到超过它们所能处理的参数数量。这就是你遇到的问题。你用C("a", "B")来实例化你的类。解释器会先在C中查找__new__方法。C没有这个方法,所以Python会去它的父类str中查找。因为str有这个方法,所以就使用了这个方法,并且传入了两个参数。但是str.__new__只期望接收一个参数(除了类对象作为第一个参数)。因此,就会抛出TypeError错误。这就是为什么你必须在子类中扩展这个方法,就像你对__init__所做的那样。但要记住,它必须返回类的实例,并且这是一个静态方法(无论它是否用@staticmethod装饰器定义),这在你使用super函数时是很重要的。

14

继承内置类型其实很少有意义。这样做会遇到很多问题,而且你得到的好处并不多。

通常来说,使用组合的方式会更好。比如,不是直接继承 str,而是把一个 str 对象作为属性来使用。

class EnhancedString(object):
     def __init__(self, *args, **kwargs):
         self.s = str(*args, **kwargs)

你可以手动或自动使用 __getattr__ 来延迟处理任何你想要在底层 str 对象 self.s 上执行的方法。

不过,想要自己定义一个字符串类型,这个想法值得你深思。很多类确实需要把字符串作为主要数据,但一般来说,你还是应该使用 strunicode(如果你要表示文本的话)来表示字符串。(一个常见的例外是如果你需要使用某个用户界面工具包的字符串类型。)如果你想给字符串增加一些功能,尽量使用 对字符串进行操作的函数,而不是 创建新的对象来作为字符串,这样可以让你的代码更简单,也更容易和其他人的程序兼容。

113
>>> class C(str):
...     def __new__(cls, *args, **kw):
...         return str.__new__(cls, *args, **kw)
... 
>>> c = C("hello world")
>>> type(c)
<class '__main__.C'>

>>> c.__class__.__mro__
(<class '__main__.C'>, <type 'str'>, <type 'basestring'>, <type 'object'>)

因为__init__是在对象构造完成后才被调用的,所以对于不可变类型来说,修改值就太晚了。请注意,__new__是一个类方法,所以我把第一个参数叫做cls

想了解更多信息,可以查看这里

>>> class C(str):
...     def __new__(cls, value, meta):
...         obj = str.__new__(cls, value)
...         obj.meta = meta
...         return obj
... 
>>> c = C("hello world", "meta")
>>> c
'hello world'
>>> c.meta
'meta'

撰写回答