这正是__new__ https://docs.python.org/2/reference/datamodel.html#object.__new__方法是为了.
在Python中,创建一个对象实际上有两个步骤。在伪代码中:
value = the_class.__new__(the_class, *args, **kwargs)
if isinstance(value, the_class):
value.__init__(*args, **kwargs)
这两个步骤称为构造和初始化。大多数类型不需要任何花哨的构造,因此它们可以只使用默认值__new__
并定义一个__init__
方法——这就是为什么教程等只提到__init__
.
But str
对象是不可变的,因此初始化程序无法执行设置属性等常规操作,因为您无法在不可变对象上设置属性。
所以,如果你想改变什么str
实际上成立,你必须重写它__new__
方法,并调用 super__new__
与你修改后的参数。
在这种情况下,你实际上并不想这样做......但是你do想确定str.__new__
没有看到你的额外参数,所以你still需要覆盖它,只是为了隐藏那些参数。
同时,你问:
str 的原始字符串值被放入什么属性?
事实并非如此。重点是什么?它的值是一个字符串,所以你会有一个str
它有一个相同的属性str
它有一个属性,等等无穷无尽。
当然,它必须在幕后存储某物。但那是在幕后。特别是,在 CPython 中,str
类是用 C 实现的,除其他外,它还包含一个 Cchar *
用于表示字符串的实际字节数组。您无法直接访问它。
但是,作为一个子类str
,如果你想知道你的字符串值,那就是self
。毕竟,这就是作为子类的全部意义。
So:
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
当然你并不真的need __init__
这里;你可以在构建的同时进行初始化__new__
。但如果你不打算flags
作为一个不可变的、仅在构造期间设置的值,将其作为初始值设定项更具有概念意义,就像任何普通类一样。
同时:
我想尽可能少地重载 str 类
那可能不会达到你想要的效果。例如,str.__add__
and str.__getitem__
将返回一个str
,不是您的子类的实例。如果那很好,那么你就完成了。如果没有,您将必须重载所有这些方法并更改它们以使用适当的元数据包装返回值。 (您可以通过编程方式执行此操作,方法是在类定义时生成包装器,或者使用__getattr__
动态生成包装器的方法。)
最后要考虑的一件事:str
构造函数并不只接受一个参数。
可能需要 0:
str() == ''
而且,虽然这与 Python 2 无关,但在 Python 3 中它可能需要 2:
str(b'abc', 'utf-8') == 'abc'
另外,即使需要 1 个参数,它显然也不必是字符串:
str(123) == '123'
那么...您确定这是您想要的界面吗?也许你最好创建一个对象owns一个字符串(在self.value
),并且只是明确地使用它。或者甚至隐式地使用它,鸭子类型作为str
通过仅委托大部分或全部str
方法self.value
?