从str或int继承

Rug*_*rra 47 python string int inheritance new-operator

为什么我在创建一个继承自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)
Run Code Online (Sandbox Code Playgroud)

如果我尝试使用int而不是str,它会发生同样的情况,但它适用于自定义类.我需要用__new__而不是__init__?为什么?

Joh*_*ooy 71

>>> 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'>)
Run Code Online (Sandbox Code Playgroud)

因为__init__在构造对象之后调用,所以修改不可变类型的值为时已晚.注意,这__new__是一个classmethod,所以我调用了第一个参数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'
Run Code Online (Sandbox Code Playgroud)


Mik*_*ham 13

继承内置类型很少值得.你必须处理好几个问题而且你并没有获得太多的好处.

使用构图几乎总是更好.str您可以将str对象保留为属性,而不是继承.

class EnhancedString(object):
     def __init__(self, *args, **kwargs):
         self.s = str(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

您可以str self.s手动或自动使用推迟您想要在底层工作的任何方法__getattr__.

话虽这么说,需要你自己的字符串类型应该让你停下来.有许多类应该将字符串存储为其主要数据,但是您通常希望使用strunicode(后者,如果您表示文本)用于字符串的一般表示.(一个常见的例外是如果您需要使用UI工具包的字符串类型.)如果您想为字符串添加功能,请尝试使用对字符串而不是新对象进行操作的函数作为字符串进行操作您的代码更简单,与其他人的程序更兼容.

  • 我不相信.你能指出为什么你认为继承str是潜在的错误来源吗?因为它不是惯用的吗? (3认同)
  • @wise,使用`__getattr__`将其他未定义的属性推迟到组合中的特定属性是由`def __getattr __(self,name):return getattr(self.s,name)`之类的代码完成的.请注意,不一定建议使用此代码,但在某些情况下可以正常使用; 在组合中手动定义所需的东西通常更好. (2认同)
  • >"继承内置类型很少值得." 这可能是真的,但并非总是如此.有时创建"标记字符串"类型很有用,例如从解析器生成不同类型的字符串式标记.然而,它的问题是(我认为)你失去了短串性能优化. (2认同)
  • https://docs.python.org/3/library/collections.html#collections.UserString"该类,UserString充当字符串对象的包装.对此类的需求已部分取代了直接从子类化的能力海峡;" 这不是那么单一的. (2认同)
  • @blais,假设你做`your_subclass [:5]`或`your_subclass.replace(x,y)` - 它返回什么?`str`的​​实例或子类的实例?这甚至没有定义,但在实践中它将返回`str`.不能保证任何方式,这种未定义的行为是一件坏事. (2认同)

use*_*246 8

实例化一个类时,传入的参数将传递给类的__new__(构造函数),然后传递给类的__init__(初始化程序)方法.因此,如果从一个对实例化期间可能提供的参数数量有限制的类继承,则必须保证它既不会__new__,也__init__不会获得比预期更多的参数.这就是你遇到的问题.用实例化你的类C("a", "B").解释器寻找__new__方法C.C没有它,所以python窥视它的基类str.因为它有一个,所以使用并提供两个参数.但是str.__new__期望只获得一个参数(除了它的类对象作为第一个参数).所以TypeError提出来了.这就是为什么你必须在你的子类中扩展它,就像你使用它一样__init__.但请记住,它必须返回类实例,并且它是一个静态方法(无论是否使用@staticmethod装饰器定义),如果使用super函数,则会计算.


zol*_*i2k 5

__new__在不可变类型的情况下使用:

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
Run Code Online (Sandbox Code Playgroud)

打印返回hello.

Python字符串是不可变类型.__new__调用该函数以创建对象的新实例C.该蟒蛇__new__功能基本存在允许继承自不变类型.

  • 我写了2条评论. (2认同)

小智 5

仔细阅读this后,这是对str进行子类化的另一种尝试。其他答案的变化是使用super(TitleText, cls).__new__. 无论何时使用,这个似乎都像 str 一样,但允许我覆盖一个方法:

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'
Run Code Online (Sandbox Code Playgroud)

这使您可以正确地进行切片和下标。这是用来干嘛的?用于在管理索引页面中重命名 Django 应用程序。