Python 如何扩展 `str` 并重载其构造函数?
Python how to extend `str` and overload its constructor?
我有一个字符序列,如果您愿意,可以是一个字符串,但我想存储有关字符串来源的元数据。另外我想提供一个简化的构造函数。
我已经尝试过 str
class 的扩展方式 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]
)
当我注释掉 @classmethod
的 cls() 调用的两个参数时,它给我这个错误:
TypeError: __init__() takes exactly 3 arguments (1 given)
非常典型,参数数量错误,
还有两个参数(如示例代码所示):
TypeError: str() takes at most 1 argument (2 given)
我试过更改 __init__
的参数,super().__init__
的参数,似乎都没有改变。
只有一个参数传递给 cls(...)
调用,正如 str class 的错误提示,我得到这个:
TypeError: __init__() takes exactly 3 arguments (2 given)
所以我在这里赢不了,出了什么问题?
Ps 这应该是第二个 post 但是 属性 将 str 的原始字符串值放入什么?我想尽可能少地重载 str class 以将此元数据添加到构造函数中。
而不是 __init__
试试新的:
def __new__(cls, value, flags):
obj = str.__new__(cls, value)
obj.flags = flags
return obj
这正是 __new__
方法的用途。
在Python中,创建对象实际上有两个步骤。在伪代码中:
value = the_class.__new__(the_class, *args, **kwargs)
if isinstance(value, the_class):
value.__init__(*args, **kwargs)
这两个步骤称为构建和初始化。大多数类型在构造中不需要任何花哨的东西,所以它们可以只使用默认的 __new__
并定义一个 __init__
方法——这就是为什么教程等只提到 __init__
.
但是 str
对象是不可变的,所以初始化器不能做设置属性等通常的事情,因为你不能在不可变对象上设置属性。
所以,如果你想改变 str
实际包含的内容,你必须覆盖它的 __new__
方法,并用你修改过的参数调用超级 __new__
。
在这种情况下,您实际上并不想这样做……但是您确实想确保str.__new__
看不到您的额外参数,所以你仍然需要覆盖它,只是为了隐藏那些参数。
与此同时,您问:
what property does str's raw string value get put into?
没有。重点是什么?它的值是一个字符串,所以你有一个 str
有一个属性是相同的 str
有一个属性等无限。
在幕后,当然,它必须存储一些东西。但那是在幕后。特别是,在 CPython 中,str
class 是在 C 中实现的,除其他外,它包含一个 C char *
实际字节数组,用于表示字符串。您不能直接访问它。
但是,作为 str
的子class,如果您想知道字符串形式的值,那就是 self
。毕竟,这就是成为子class的全部意义。
所以:
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
当然你并不真的需要 __init__
这里;您可以在 __new__
中进行初始化和构建。但是,如果您不打算让 flags
成为一个不可变的、仅在构造期间设置的值,那么将其作为初始化程序在概念上更有意义,就像任何普通的 class 一样。
同时:
I'd like to overload as little of the str class as I can
这可能不是你想要的。例如,str.__add__
和 str.__getitem__
将成为 return 一个 str
,而不是您的 subclass 的一个实例。如果那很好,那么你就完成了。否则,您将不得不重载所有这些方法并更改它们以使用适当的元数据包装 return 值。 (您可以通过在 class 定义时生成包装器,或使用 __getattr__
方法动态生成包装器,以编程方式执行此操作。)
最后一件事要考虑:str
构造函数不接受一个参数。
可以取0:
str() == ''
而且,虽然这与 Python 2 无关,但在 Python 3 中可能需要 2:
str(b'abc', 'utf-8') == 'abc'
此外,即使它接受 1 个参数,它显然也不一定是字符串:
str(123) == '123'
那么……您确定这是您想要的界面吗?也许您最好创建一个 拥有 字符串(在 self.value
中)的对象,然后明确地使用它。或者甚至通过将大部分或所有 str
方法委托给 self.value
?
来隐含地使用它,将鸭子打字作为 str
我有一个字符序列,如果您愿意,可以是一个字符串,但我想存储有关字符串来源的元数据。另外我想提供一个简化的构造函数。
我已经尝试过 str
class 的扩展方式 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]
)
当我注释掉 @classmethod
的 cls() 调用的两个参数时,它给我这个错误:
TypeError: __init__() takes exactly 3 arguments (1 given)
非常典型,参数数量错误,
还有两个参数(如示例代码所示):
TypeError: str() takes at most 1 argument (2 given)
我试过更改 __init__
的参数,super().__init__
的参数,似乎都没有改变。
只有一个参数传递给 cls(...)
调用,正如 str class 的错误提示,我得到这个:
TypeError: __init__() takes exactly 3 arguments (2 given)
所以我在这里赢不了,出了什么问题?
Ps 这应该是第二个 post 但是 属性 将 str 的原始字符串值放入什么?我想尽可能少地重载 str class 以将此元数据添加到构造函数中。
而不是 __init__
试试新的:
def __new__(cls, value, flags):
obj = str.__new__(cls, value)
obj.flags = flags
return obj
这正是 __new__
方法的用途。
在Python中,创建对象实际上有两个步骤。在伪代码中:
value = the_class.__new__(the_class, *args, **kwargs)
if isinstance(value, the_class):
value.__init__(*args, **kwargs)
这两个步骤称为构建和初始化。大多数类型在构造中不需要任何花哨的东西,所以它们可以只使用默认的 __new__
并定义一个 __init__
方法——这就是为什么教程等只提到 __init__
.
但是 str
对象是不可变的,所以初始化器不能做设置属性等通常的事情,因为你不能在不可变对象上设置属性。
所以,如果你想改变 str
实际包含的内容,你必须覆盖它的 __new__
方法,并用你修改过的参数调用超级 __new__
。
在这种情况下,您实际上并不想这样做……但是您确实想确保str.__new__
看不到您的额外参数,所以你仍然需要覆盖它,只是为了隐藏那些参数。
与此同时,您问:
what property does str's raw string value get put into?
没有。重点是什么?它的值是一个字符串,所以你有一个 str
有一个属性是相同的 str
有一个属性等无限。
在幕后,当然,它必须存储一些东西。但那是在幕后。特别是,在 CPython 中,str
class 是在 C 中实现的,除其他外,它包含一个 C char *
实际字节数组,用于表示字符串。您不能直接访问它。
但是,作为 str
的子class,如果您想知道字符串形式的值,那就是 self
。毕竟,这就是成为子class的全部意义。
所以:
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
当然你并不真的需要 __init__
这里;您可以在 __new__
中进行初始化和构建。但是,如果您不打算让 flags
成为一个不可变的、仅在构造期间设置的值,那么将其作为初始化程序在概念上更有意义,就像任何普通的 class 一样。
同时:
I'd like to overload as little of the str class as I can
这可能不是你想要的。例如,str.__add__
和 str.__getitem__
将成为 return 一个 str
,而不是您的 subclass 的一个实例。如果那很好,那么你就完成了。否则,您将不得不重载所有这些方法并更改它们以使用适当的元数据包装 return 值。 (您可以通过在 class 定义时生成包装器,或使用 __getattr__
方法动态生成包装器,以编程方式执行此操作。)
最后一件事要考虑:str
构造函数不接受一个参数。
可以取0:
str() == ''
而且,虽然这与 Python 2 无关,但在 Python 3 中可能需要 2:
str(b'abc', 'utf-8') == 'abc'
此外,即使它接受 1 个参数,它显然也不一定是字符串:
str(123) == '123'
那么……您确定这是您想要的界面吗?也许您最好创建一个 拥有 字符串(在 self.value
中)的对象,然后明确地使用它。或者甚至通过将大部分或所有 str
方法委托给 self.value
?
str