Почему я не могу создать подкласс datetime.date? - PullRequest
18 голосов
/ 30 декабря 2008

Почему не работает следующее (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>

Спасибо!

Ответы [ 6 ]

35 голосов
/ 30 декабря 2008

Что касается нескольких других ответов, это не имеет ничего общего с датами, внедряемыми в 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)
10 голосов
/ 31 декабря 2008

Пожалуйста, прочитайте ссылку на Python на Модель данных , особенно о __new__ специальном методе .

Выдержка из этой страницы (мой курсив):

__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
>>> 
4 голосов
/ 30 декабря 2008

Вот ответ и возможное решение (используйте функцию или время выполнения вместо подклассов)

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

2 голосов
/ 30 декабря 2008

Ваша функция не обойдена; Python просто никогда не доходит до точки, где он будет называть это. Поскольку datetime реализован на C, он выполняет инициализацию в datetime.__new__, а не datetime.__init__. Это потому, что дата и время неизменны. Вероятно, вы могли бы обойти это, переопределив __new__ вместо __init__. Но, как полагают другие люди, лучший способ - это вообще не делать подклассы datetime.

0 голосов
/ 30 декабря 2008

Вы можете обернуть это и добавить расширенную функциональность к вашей обертке.

Вот пример:

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.date.

0 голосов
/ 30 декабря 2008

Вы, вероятно, должны использовать фабричную функцию вместо создания подкласса:

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