子类 super returning self 应该 return 子类

Subclass super returning self should return subclass

我正在尝试扩展 datetime.date class 允许添加 int 而不是使用 timedelta 对象:

class Date(datetime.date):
    def __add__(self, other):
        if isinstance(other, int):
            other = datetime.timedelta(days=other)
        return super(Date, self).__add__(other)

问题是上面的 __add__() 方法将 return datetime.date 的实例而不是 Date.

根据我的理解,没有办法让 super().__add__() 知道 Date 类型。最优雅的解决方案是从 datetime.date 复制整个 __add__() 方法并添加我的额外代码。有没有办法在 Date.__add__() 方法中将 datetime.date 对象转换为 Date 对象?

这里是突出问题的片段:

D = Date(2000,1,1)
d = D + 1
type(d) # datetime.date instead of Date

编辑:我在查看 datetime.py(搜索 "class date")后的第一个解决方案是:

class Date(datetime.date):
    def __add__(self, other):
        if isinstance(other, int):
            other = datetime.timedelta(days=other)
        d = super(Date, self).__add__(other)
        self.__year = d.year
        self.__month = d.month
        self.__day = d.day
        return self # this is of type datetime.date

好的,我想我要指出我的第一次尝试是这样的:

class Date(datetime.date):
    def __add__(self, other):
        if isinstance(other, int):
            other = datetime.timedelta(days=other)
        d = super(Date, self).__add__(other)
        d.__class__ = Date
        return d

这是行不通的,因为(正如我所怀疑的那样)datetime 模块是在 C 中,并且根据 this post 对于这样的类型你不能分配给 __class__。我有点困惑 datetime.py 中的代码是干什么用的。

The problem is that the add() method above will return an instance of datetime.date instead of a Date.

它会,但这就是您使用 super() 的原因,毕竟:您确实想要调用 super classes 的方法。

你可以只调用你自己的 class 的复制构造函数!

The most elegant solution is to copy the entire add() method from datetime.date and add my extra bit of code.

我认为这不能真正称为优雅的解决方案。另外,我认为 datetime 模块是 C.

添加后,只需从 datetime.date 和 return 创建另一个 Date 对象,就像这样

    def __add__(self, other):
        if isinstance(other, int):
            other = datetime.timedelta(days=other)
        result = super(Date, self).__add__(other)
        return Date(result.year, result.month, result.day)

这是一个测试,

D = Date(2000, 1, 1) + 1
print(type(D), D)
# (<class '__main__.Date'>, Date(2000, 1, 2))

一般来说,super(Subclass, self).__add__(other)可能returnSubclass实例:

class Base(object):
    __slots__ = 'n'
    def __new__(cls, n):
        self = object.__new__(cls)
        self.n = n
        return self
    @classmethod
    def fromordinal(cls, n):
        return cls(n)
    def __add__(self, other):
        if hasattr(other, 'n'):
            return self.fromordinal(self.n + other.n)
        return NotImplemented
    def __repr__(self):
        return "{cls}({arg})".format(cls=self.__class__.__name__, arg=self.n)
    def __eq__(self, other):
        return self.n == other.n

class Subclass(Base):
    __slots__ = ['_secret']
    def __add__(self, other):
        if hasattr(other, '_secret'):
            other = self.fromordinal(other._secret)
        return super(Subclass, self).__add__(other)


a = Subclass(1)
b = Subclass(2)
c = Subclass(2)
c._secret = 3
print(a + b)
print(a + c)
assert (a + b) == Subclass(3)
assert (a + c) == Subclass(4)
assert b == c and (a + b) != (a + c) and type(a + b) == type(a + c) == Subclass

但是 datetime.date.__add__ 方法被硬编码到 return datetime.date 个实例,即使对于 datetime.date 个子类也是如此。 In CPython, it is implemented in C 出于性能原因,不会从子类中调用(可能)纯 Python __add__ 方法。纯 Python datetime.py 可能被其他 Python 实现使用,例如 Pypy、IronPython,Jython 不调用子类方法以与 CPython 兼容。