Python如何扩展`str`并重载其构造函数?

Tho*_*ner 3 python string overloading class python-2.7

我有一个字符序列,如果你愿意,我会有一个字符串,但我想存储有关字符串原点的元数据.另外,我想提供一个简化的构造函数.

我尝试过str像Google为我解决的那样多的方式扩展课程.我来的时候就放弃了;

class WcStr(str):
    """wc value and string flags"""

    FLAG_NIBBLES = 8 # Four Bytes

    def __init__(self, value, flags):
        super(WcStr, self).__init__()
        self.value = value
        self.flags = flags

    @classmethod
    def new_nibbles(cls, nibbles, flag_nibbles=None):
        if flag_nibbles is None:
            flag_nibbles = cls.FLAG_NIBBLES

        return cls(
            nibbles[flag_nibbles+1:],
            nibbles[:flag_nibbles]
        )
Run Code Online (Sandbox Code Playgroud)

当我将两个参数注释掉为@classmethod's cls()时,它会给我这个错误:

TypeError: __init__() takes exactly 3 arguments (1 given)
Run Code Online (Sandbox Code Playgroud)

非常典型,错误的args错误数,

使用另外两个参数(例如,如示例代码所示):

TypeError: str() takes at most 1 argument (2 given)
Run Code Online (Sandbox Code Playgroud)

我试过改变它__init__的args,它super().__init__的args,似乎都没有改变.

只有一个参数传递给cls(...)调用,正如str类的错误所要求的那样,我得到了这个:

TypeError: __init__() takes exactly 3 arguments (2 given)
Run Code Online (Sandbox Code Playgroud)

所以我不能在这里取胜,出了什么问题?


这应该是第二篇文章,但str的原始字符串值是什么属性?我想尽可能少地重载str类,以便将这些元数据添加到构造函数中.

aba*_*ert 13

这正是该__new__方法的用途.

在Python中,创建对象实际上有两个步骤.在伪代码中:

value = the_class.__new__(the_class, *args, **kwargs)
if isinstance(value, the_class):
    value.__init__(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

这两个步骤称为构造和初始化.大多数类型在构造中不需要任何花哨的东西,所以他们可以只使用默认值__new__并定义一个__init__方法 - 这就是教程等仅提及的原因__init__.

但是str对象是不可变的,因此初始化程序无法执行设置属性等常用操作,因为您无法在不可变对象上设置属性.

因此,如果要更改str实际保存的内容,则必须覆盖其__new__方法,并__new__使用修改后的参数调用super .

在这种情况下,你实际上并不想这样做...但是你确实希望确保str.__new__看不到你的额外参数,所以你仍然需要覆盖它,只是为了隐藏它的参数.


同时,你问:

str的原始字符串值被置于什么属性?

它没有.重点是什么?它的值是一个字符串,所以你str有一个属性是相同的str,具有无限等属性.

当然,在封面下,它必须存储一些东西.但那是在幕后.特别是,在CPython中,str该类是用C实现的,它除了其他之外还包含char *用于表示字符串的实际字节的C 数组.您无法直接访问它.

但是,作为一个子类str,如果你想知道你的值是一个字符串,那就是self.毕竟,这就是成为子类的重点.


所以:

class WcStr(str):
    """wc value and string flags"""

    FLAG_NIBBLES = 8 # Four Bytes

    def __new__(cls, value, *args, **kwargs):
        # explicitly only pass value to the str constructor
        return super(WcStr, cls).__new__(cls, value)

    def __init__(self, value, flags):
        # ... and don't even call the str initializer 
        self.flags = flags
Run Code Online (Sandbox Code Playgroud)

你当然不需要 __init__这里; 你可以在你的构造中进行初始化__new__.但是如果你不打算flags成为一个不可变的,只在构造期间设置的那种值,那么在初始化程序中使用它就像在任何普通类中一样具有更多的概念意义.


与此同时:

我想尽可能少地重载str类

这可能不会做你想要的.例如,str.__add__并且str.__getitem__将返回a str,而不是子类的实例.如果这很好,那么你就完成了.如果没有,您将不得不重载所有这些方法并更改它们以使用适当的元数据包装返回值.(您可以通过编程方式执行此操作,方法是在类定义时生成包装器,或者使用__getattr__生成包装器的方法.)


最后要考虑的一点是:str构造函数不会只使用一个参数.它可能需要0(str() == '').而且,虽然这与Python 2无关,但在Python 3中它可能需要2(str(b'abc', 'utf-8') == 'abc'). Plus, even when it takes 1 argument, it obviously doesn't have to be a string (str(123)=='123'`).

那么......你确定这是你想要的界面吗?也许你最好创建一个拥有字符串(in self.value)的对象,并明确地使用它.或者甚至隐式使用它,str只需将大多数或所有str方法委托给鸭子打字self.value