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

Python如何扩展'str'并重载其构造函数?,python,string,class,python-2.7,overloading,Python,String,Class,Python 2.7,Overloading,我有一个字符序列,一个字符串(如果你愿意的话),但我想存储关于字符串起源的元数据。此外,我想提供一个简化的构造函数 我试着用谷歌为我解决的方法扩展str类。当我来到这里时,我放弃了 class WcStr(str): """wc value and string flags""" FLAG_NIBBLES = 8 # Four Bytes def __init__(self, value, flags): super(WcStr, self).__in

我有一个字符序列,一个字符串(如果你愿意的话),但我想存储关于字符串起源的元数据。此外,我想提供一个简化的构造函数

我试着用谷歌为我解决的方法扩展
str
类。当我来到这里时,我放弃了

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]
        )
当我将这两个参数注释掉到
@classmethod
的cls()调用时,会出现以下错误:

TypeError: __init__() takes exactly 3 arguments (1 given)
非常典型,args错误的数目

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

我尝试过更改
\uuuu init\uuuu
的参数,即
super()。\uuuuu init\uuu
的参数,这两个参数似乎都没有做任何更改

由于只有一个参数传递给
cls(…)
call,正如str类的错误所要求的那样,我得到以下结果:

TypeError: __init__() takes exactly 3 arguments (2 given)
所以我在这里赢不了,怎么了



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

而不是
\uuuu init\uuuu
尝试新建:

def __new__(cls, value, flags):    
    obj = str.__new__(cls, value)
    obj.flags = flags
    return obj    
这正是该方法的目的

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

value = the_class.__new__(the_class, *args, **kwargs)
if isinstance(value, the_class):
    value.__init__(*args, **kwargs)
这两个步骤称为构造和初始化。大多数类型在构造上不需要任何花哨的东西,所以它们可以使用默认的
\uuuuuuuuuuuuuuuuuuuuuuuuu
并定义一个
\uuuuuuuuuuuuuuuuuuuuuu
方法,这就是为什么教程等只提到
\uuuuuuuuuuuuuuuuuuuuuuuuuuuu
的原因

但是
str
对象是不可变的,因此初始值设定项无法执行设置属性等常规操作,因为您无法在不可变对象上设置属性

因此,如果您想更改
str
实际包含的内容,您必须重写它的
\uuuuu new\uuuuu
方法,并使用修改后的参数调用super
\uuuuu new\uuuuuu

在这种情况下,您实际上不想这样做……但是您确实想确保
str.\uuuu new\uuuu
没有看到您的额外参数,因此您仍然需要覆盖它,只是为了隐藏这些参数


与此同时,你问:

str的原始字符串值放在什么属性中

没有。重点是什么?它的值是一个字符串,所以你会有一个
str
,它有一个属性,这个属性与
str
的属性相同,这个属性是无限的

当然,在被子下面,它必须储存一些东西。但那是隐藏的。特别是,在CPython中,
str
类是用C实现的,其中包括一个C
char*
数组,该数组包含用于表示字符串的实际字节。你不能直接访问它

但是,作为
str
的子类,如果您想知道作为字符串的值,那只是
self
。毕竟,这就是作为一个子类的全部意义所在


因此:

当然,这里并不需要
\uuuu init\uuuu
;您可以在
\uuuu new\uuuu
中进行初始化和构造。但是,如果您不想让
标志
成为一个不可变的、仅在构造过程中设置的值,那么使用初始值设定项(initializer)更具概念意义,就像任何普通类一样


同时:

我希望尽可能少地重载str类

那可能不是你想要的。例如,
str.\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
str.\uuuuuuuuuuuu getitem\uuuuuuuuuuuuuuuu。如果这很好,那你就完了。否则,您将不得不重载所有这些方法,并将它们更改为使用适当的元数据包装返回值。(您可以通过编程方式实现这一点,可以在类定义时生成包装,也可以使用动态生成包装的
\uu getattr\uu
方法。)


最后要考虑的一件事是:
str
构造函数不完全接受一个参数。它可能需要0(
str()=''
)。而且,虽然这在Python2中不相关,但在Python3中可能需要2(
str(b'abc','utf-8')=='abc')。另外,即使只有一个参数,它也不一定是字符串(
str(123)=='123'`)


那么…你确定这就是你想要的界面吗?也许您最好创建一个拥有字符串的对象(在
self.value
中),然后显式地使用它。或者甚至隐式地使用它,通过将大部分或全部
str
方法委托给
self.value

Python的原始字符串值不会被放入任何属性中,从而将duck键入为
str
。没有“原始字符串”;它的值只是一个字符串,如果它在任何属性中,那么它的类型将与
str
相同。这是他们没有教你的东西之一,因为大多数类型只需要一个初始值设定项,而不需要构造函数……但是现在你正试图对一个不可变类型进行子类化,所以你确实需要一个构造函数。最后,你显式地调用super的
\uuuu init\uuu
,没有参数。充其量,这会给你一个空字符串。因为字符串是不可变的,所以它将永远是空的。您可能不希望这样,但我不确定您希望从代码的其余部分得到什么。(你确定你甚至想要一个
str
子类,而不仅仅是一个拥有
str
和duck样式的子类,通过委派许多方法来完成吗?)
value = the_class.__new__(the_class, *args, **kwargs)
if isinstance(value, the_class):
    value.__init__(*args, **kwargs)
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