子类超级返回self应该返回子类

时间:2015-01-11 16:10:01

标签: python python-2.7 python-3.x

我正在尝试扩展datetime.date类,允许添加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__()方法会返回datetime.date而不是Date的实例。

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

以下是一个突出显示问题的片段:

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

编辑:在查看datetime.py(搜索“课程日期”)后,我的第一个解决方案就是:

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

这不起作用,因为(我怀疑)日期时间模块在C中,而根据this post,这些类型无法分配给__class__。我对datetime.py中的代码有点困惑。

3 个答案:

答案 0 :(得分:2)

添加后,只需从Date创建另一个datetime.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))

答案 1 :(得分:0)

  

问题是上面的add()方法将返回datetime.date的实例而不是Date。

它会,但这就是你为什么要使用super(),毕竟:你真的想打电话给超级班级&#39;方法。

你可以打电话给你自己的班级复制构造函数!

  

最优雅的解决方案是从datetime.date复制整个add()方法并添加我的额外代码。

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

答案 2 :(得分:0)

通常,super(Subclass, self).__add__(other)可能会返回Subclass个实例:

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__方法被硬编码以返回datetime.date个实例,即使对于datetime.date子类也是如此。 In CPython, it is implemented in C出于性能原因,不会从子类调用(可能)纯Python __add__方法。其他Python实现(如Pypy,IronPython,Jython)可能使用的纯Python datetime.py不会调用子类方法来与CPython兼容。