Как я могу расширить Python datetime.datetime моими собственными методами? - PullRequest
6 голосов
/ 23 июля 2010

Я пытаюсь расширить класс Python datetime.datetime с помощью нескольких дополнительных методов.Так, например, я делаю:

import datetime

class DateTime(datetime.datetime):
    def millisecond(self):
        return self.microsecond/1000

, но тогда, если я делаю

>>> d = DateTime(2010, 07, 11, microsecond=3000)
>>> print d.millisecond()
3
>>> delta = datetime.timedelta(hours=4)
>>> newd = d + delta
>>> print newd.millisecond()
AttributeError: 'datetime.datetime' object has no attribute 'millisecond'

Это очевидно, потому что выполнение d + delta вызывает метод datetime.datetime.__add__(), который возвращает datetime.datetime объект.

Можно ли как-нибудь преобразовать этот datetime.datetime объект в DateTime объект?Или мне придется переопределить все операторы в моем подклассе DateTime, чтобы вернуть правильный тип?

Ответы [ 5 ]

15 голосов
/ 08 января 2013

Немного поздно, но работает следующее:

import ctypes as c

_get_dict = c.pythonapi._PyObject_GetDictPtr
_get_dict.restype = c.POINTER(c.py_object)
_get_dict.argtypes = [c.py_object]

import datetime

def millisecond(td):
    return (td.microsecond / 1000)
d = _get_dict(datetime.datetime)[0]
d['millisecond'] = millisecond

now = datetime.datetime.now()
print now.millisecond(), now.microsecond

По сути, вы используете ctypes, чтобы получить редактируемую копию словаря классов, а затем просто вставляете свой метод. Я использую это только для бэкпорта функций к более старым версиям Python, которые я считаю довольно чистыми. Например:

from datetime import timedelta
try:
    timedelta.total_seconds # new in 2.7
except AttributeError:
    def total_seconds(td):
        return float(td.days * 24 * 3600 + td.seconds + td.microseconds / 1e6)
    d = _get_dict(timedelta)[0]
    d['total_seconds'] = total_seconds
    # works now in 2.4
3 голосов
/ 23 июля 2010

Я пытался реализовать решение с помощью monkey-patching, но столкнулся с ошибкой:

TypeError: can't set attributes of built-in/extension type 'datetime.datetime'

Это происходит с datetime.datetime.millisecond = millisecond и GvR __metaclass__=monkeypatch_class.

Возможно, datetime.so нельзя залатать обезьяной. Если это правда, то вы можете рассмотреть это:

import datetime

class DateTime(datetime.datetime):
    @property
    def millisecond(self):
        return self.microsecond/1000.0
    def __add__(self,other):
        result=super(DateTime,self).__add__(other)
        result=DateTime(result.year,
                        result.month,
                        result.day,
                        result.hour,
                        result.minute,
                        result.second,
                        result.microsecond,
                        result.tzinfo)        
        return result
    __radd__=__add__

d = DateTime(2010, 07, 11, microsecond=3000)
print d.millisecond
# 3.0

delta = datetime.timedelta(hours=4)
newd = d + delta
print newd.millisecond
# 3.0

# This uses __radd__
newd = delta + d
print newd.millisecond
# 3.0
3 голосов
/ 23 июля 2010

В этом случае я бы предпочел простые автономные функции:

import datetime
def millisecond(dt):
    return dt.microsecond/1000

В Python возможны миксины (см. Комментарий), но я думаю, что в таких случаях они излишни.

2 голосов
/ 02 июля 2012

unutbu был настолько близок к тому, что вы изначально просили:

class DateTime(datetime.datetime):
    @property
    def millisecond(self):
        return self.microsecond/1000.0

datetime.datetime = DateTime

Я использую этот шаблон для реализации API, которые будут отображаться только в 3.3 при работе в 3.1 или 3.2.Остальная часть класса продолжает работать как всегда, без потери производительности.

Отредактировано, чтобы добавить: Это работает только с 2.6+ для встроенных функций, потому что оно работает только с классами нового стиля.Встроенные были в старом стиле до 2.6.

0 голосов
/ 23 июля 2010

На простом уровне вы можете сделать что-то вроде этого

import datetime
def millisecond(self):
    return self.microsecond/1000

datetime.datetime.millisecond = millisecond

Однако динамически вставленный миксин может быть удобнее - или просто определить функцию и передать ей экземпляр datetime.

В общем, сбор обезьян не очень Pythonic - это скорее подход Ruby к проблемам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...