关于python:从str或int继承

inheritance from str or int

为什么我在创建从str继承的类(或者从int继承的类)时遇到问题?

1
2
3
4
5
6
7
8
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__?为什么?


1
2
3
4
5
6
7
8
9
10
>>> 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

有关详细信息,请参阅此处

1
2
3
4
5
6
7
8
9
10
11
>>> 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'


继承内置类型是非常不值得的。你必须处理好几个问题,但实际上你得不到多少好处。

使用构图几乎总是更好的。您将保留一个str对象作为属性,而不是继承str

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

您可以推迟任何您想在基础strself.s上使用的方法,手动或自动使用__getattr__

也就是说,需要自己的字符串类型应该会让你暂停。有许多类应该存储一个字符串作为它们的主数据,但是您通常希望使用strunicode(如果您表示文本,则后者)来表示字符串的一般表示。(一个常见的例外是,如果需要使用UI工具箱的字符串类型。)如果要向字符串中添加功能,请尝试使用对字符串而不是新对象操作的函数作为字符串,这样可以使代码更简单,更与其他人的程序兼容。


当实例化一个类时,传入的参数将同时传递给该类的__new__方法和__init__方法。因此,如果您继承的类对实例化期间可能提供的参数数量有限制,则必须保证其__new____init__都不会得到比预期更多的参数。所以这就是你的问题所在。用C("a","B")实例化类。解释器在C中查找__new__方法。C没有它,所以python窥视它的基类str。因为它有一个,所以使用和提供两个参数。但是str.__new__只期望得到一个参数(除了作为第一个参数的类对象之外)。所以提出了TypeError。这就是为什么你必须在你的孩子班上扩展它,类似于你对__init__所做的。但是要记住,如果使用super函数,它必须返回类实例,并且它是一个静态方法(不管它是否用@staticmethoddecorator定义)。


对于不可变类型,使用__new__

1
2
3
4
5
6
7
8
9
class C(str):
    def __new__(cls, content, b):
        return str.__new__(cls, content)

    def __str__(self):
        return str.__str__(self)

a=C("hello","world")
print a

print返回hello

python字符串是不可变的类型。调用函数__new__来创建对象C的新实例。python __new__函数的基本存在是为了允许从不可变类型继承。


仔细阅读之后,这里还有另一种尝试,试图对str进行子类化。与其他答案不同的是,使用super(TitleText, cls).__new__在正确的类中创建实例。这个函数在使用时的行为看起来像str,但允许我重写一个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class TitleText(str):
    title_text=""
    def __new__(cls,content,title_text):
        o=super(TitleText, cls).__new__(cls,content)
        o.title_text = title_text
        return o

    def title(self):
        return self.title_text

>>> a=TitleText('name','A nice name')
>>> a
'name'
>>> a[0]
'n'
>>> a[0:2]
'na'
>>> a.title()
'A nice name'

这样可以正确地进行切片和订阅。这是干什么的?用于重命名管理索引页中的Django应用程序。