【问题标题】:Why can't I subclass datetime.date?为什么我不能继承 datetime.date?
【发布时间】:2010-09-28 18:43:40
【问题描述】:

为什么以下方法不起作用(Python 2.5.2)?

>>> import datetime
>>> class D(datetime.date):
        def __init__(self, year):
            datetime.date.__init__(self, year, 1, 1)
>>> D(2008)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: function takes exactly 3 arguments (1 given)

我想创建一个类似于datetime.date 的类,但具有不同的__init__ 函数。显然我的函数永远不会被调用。相反,原来的 datetime.date.__init__ 被调用并失败,因为它需要 3 个参数,而我传入一个。

这里发生了什么?这是一个线索吗?

>>> datetime.date.__init__
<slot wrapper '__init__' of 'object' objects>

谢谢!

【问题讨论】:

标签: python oop datetime subclass


【解决方案1】:

这是答案和可能的解决方案(使用函数或 strptime 而不是子类化)

http://www.mail-archive.com/python-list@python.org/msg192783.html

【讨论】:

  • 谢谢,问题的链接在钱上是对的,但没有解释为什么它不起作用。问题的根源是什么,能否克服?
【解决方案2】:

您可能应该使用工厂函数而不是创建子类:

def first_day_of_the_year(year):
  return datetime.date(year, 1, 1)

【讨论】:

  • 是的,这很酷,但我很好奇为什么子类化不起作用(它是一个扩展类是如何产生影响的)以及这是否可以克服......
【解决方案3】:

你的功能没有被绕过; Python 永远不会达到它会调用它的地步。由于 datetime 是在 C 中实现的,因此它在 datetime.__new__ 而不是 datetime.__init__ 中进行初始化。这是因为 datetime 是不可变的。您大概可以通过覆盖__new__ 而不是__init__ 来解决这个问题。但正如其他人所建议的那样,最好的方法可能根本不是子类化日期时间。

【讨论】:

  • @Benjamin:请检查我的答案,并考虑纠正你的答案,因为它是迄今为止投票最多的;基本上,只有你的最后一句话被认为是有帮助的;其他人被误导。另外,请修正你的“it's”​→“its”和“(Not”​→“(Note”)。
【解决方案4】:

您可以包装它并向包装器添加扩展功能。

这是一个例子:

class D2(object):
    def __init__(self, *args, **kwargs):
        self.date_object = datetime.date(*args, **kwargs)

    def __getattr__(self, name):
        return getattr(self.date_object, name)

这是它的工作原理:

>>> d = D2(2005, 10, 20)
>>> d.weekday()
3
>>> dir(d)
['__class__', '__delattr__', '__dict__', '__doc__', '__getattr__',
 '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
 '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
 '__weakref__', 'date_object']
>>> d.strftime('%d.%m.%Y')
'20.10.2005'
>>>

注意dir() 没有列出datetime.dates 属性。

【讨论】:

  • @ΤZΩΤZΙΟΥ:你是对的。它实际上是可子类化的。我将返回文档并找出我在哪里犯了这个错误。与此同时,我正在修复答案。谢谢。
【解决方案5】:

关于其他几个答案,这与 C 本身中实现的日期没有任何关系。 __init__ 方法什么都不做,因为它们是 不可变 对象,因此构造函数 (__new__) 应该完成所有工作。您会看到继承 int、str 等的相同行为。

>>> import datetime
>>> class D(datetime.date):
        def __new__(cls, year):
            return datetime.date.__new__(cls, year, 1, 1)


>>> D(2008)
D(2008, 1, 1)

【讨论】:

    【解决方案6】:

    请阅读 Data model 上的 Python 参考资料,尤其是关于 __new__ special method 的参考资料。

    摘自该页面(我的斜体):

    __new__() 主要是为了允许不可变 类型(如int、str 或tuple)的子类自定义实例创建。它通常在自定义元类中被覆盖以自定义类创建。

    datetime.datetime 也是不可变类型。

    PS 如果你认为:

    • 用 C 实现的对象不能被子类化,或者
    • __init__ 不会被 C 实现的对象调用,只有 __new__

    那就试试吧:

    >>> import array
    >>> array
    <module 'array' (built-in)>
    >>> class A(array.array):
        def __init__(self, *args):
            super(array.array, self).__init__(*args)
            print "init is fine for objects implemented in C"
    
    >>> a=A('c')
    init is fine for objects implemented in C
    >>> 
    

    【讨论】:

    • 赞成,因为这似乎是关于 SO 的唯一答案,带有文档链接。但是你的例子被打破了。它之所以有效,是因为初始化程序是一个可选参数。试试a=A('c',[],5,8)
    • &gt;&gt;&gt; a=array.array('c', [], 5, 8) Traceback (most recent call last): File "&lt;stdin&gt;", line 1, in &lt;module&gt; TypeError: array() takes at most 2 arguments (4 given) &gt;&gt;&gt; a=A('c', [], 5, 8) Traceback (most recent call last): File "&lt;stdin&gt;", line 1, in &lt;module&gt; TypeError: array() takes at most 2 arguments (4 given) 什么是完整的例子? @mkiever
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-20
    • 1970-01-01
    • 2015-02-03
    • 2020-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多