扩展具有其他属性的类时发生TypeError

时间:2019-07-02 10:44:47

标签: python python-3.x inheritance

我想用一些其他属性扩展标准的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

那么我在继承和属性扩展方面做错了什么?

2 个答案:

答案 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将接受的其他参数(encodingerrors
  • __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