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实现的,其中包括一个Cchar*
数组,该数组包含用于表示字符串的实际字节。你不能直接访问它
但是,作为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