我想用一些其他属性扩展标准的Python bytes
类,以便可以像处理任何普通bytes
objext一样处理它们(例如,将它们放入列表并对其进行排序)。
因此,我创建了一个自己的类,该类继承自bytes
,并覆盖了构造函数以采用其他属性,并在调用父类(bytes
)构造函数之前对其进行了设置。
class inheritest(bytes):
def __init__(self, bs: bytes, x: int = 0):
print("child class init")
self.x = x
super().__init__(bs)
print(inheritest(b'foobar', 3))
此方法不起作用:尽管我仅在第5行中使用类型为bytes
的单个参数来调用它,但我收到了有关将错误的参数签名传递给bytes
构造函数的类型错误。 ,应该没问题。
甚至还要注意,print语句永远不会执行,因此inheritest
类的构造函数永远不会执行,但是参数类型签名检查(引发TypesError)似乎是事先发生的。
Traceback (most recent call last):
File "inheritest.py", line 8, in <module>
print(inheritest(b'foobar', 3))
TypeError: bytes() argument 2 must be str, not int
那么我在继承和属性扩展方面做错了什么?
答案 0 :(得分:2)
您需要重写__new__
才能从字节继承:
class MyBytes(bytes):
def __new__(cls, *args, **kwargs):
self = super().__new__(cls, *args, *kwargs)
return self
现在扩展,如您所愿:
class MyBytes(bytes):
def __new__(cls, source, x):
self = super().__new__(cls, source)
self.x = x
return self
def __str__(self):
return f"<{repr(self)}, {self.x}>"
b = MyBytes(b"", 3)
print(b) # <b'', 3>
b = MyBytes(b"345", 5)
print(b) # <b'345', 5>
注意:
bytes
将接受的其他参数(encoding
和errors
)__str__
仅在不覆盖__repr__
的情况下有效。MyByte
的x
(使用property
)并为该类设置__slots__
属性-或提供一个__hasĥ__
方法考虑了x
。这也将起作用:
class MyBytes(bytes):
def __new__(cls, source, x):
self = super().__new__(cls, source)
return self
def __init__(self, source, x):
self.x = x
答案 1 :(得分:1)
TLDR:您错误地覆盖了__init__
而不是__new__
。
class XBytes(bytes):
__slots__ = 'x', # avoid arbitrary attributes
def __new__(cls, bs: bytes, x: int = 0):
# call original __new__ with expected signature
self = super().__new__(cls, bs)
self.x = x
return self
Python类型通常具有构造两个对象时使用的两个方法:
__new__
创建对象(“构造函数”)__init__
创建状态(“初始化程序”)值得注意的是,两者在创建对象时都会被调用。您可以考虑将Class
(例如Class(1, 2, 3)
)的实例构造为此:
def new(cls, *args, **kwargs):
"""same as ``cls(*args, **kwargs)``"""
self = cls.__new__(cls, *args, **kwargs)
if isinstance(self, cls):
cls.__init__(self, *args, **kwargs)
请注意__new__
是如何创建对象,而__init__
仅是更改对象。对于不可变类型,您必须覆盖__new__
,因为它们无法更改。
如果您覆盖__init__
,则__new__
的签名不会改变!调用inheritest(b'foobar', 3)
时,参数b'foobar', 3
被传递给原始的bytes.__new__
。这是在您的自定义__init__
被调用之前 发生的,因此永远不会触发print
。