2010-07-23 4 views
6

Sto provando ad estendere la classe datetime.datetime di Python con un paio di metodi aggiuntivi. Così, per esempio sto facendo:Come posso estendere il datetime.datetime di Python con i miei metodi?

import datetime 

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

ma poi se lo faccio

>>> 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' 

Questo è ovviamente perché facendo d + delta chiama il metodo datetime.datetime.__add__() che restituisce un oggetto datetime.datetime.

Esiste un modo per convertire questo oggetto datetime.datetime in un oggetto DateTime? O dovrei reimplementare tutti gli operatori nella sottoclasse DateTime per restituire il tipo corretto?

risposta

3

In questo caso preferirei semplici funzioni di free-standing:

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

mixin sono possibili in Python (vedere il commento), ma penso che in questi casi sono superflui.

-1

A livello simplet, si può fare qualcosa di simile

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

datetime.datetime.millisecond = millisecond 

Tuttavia un mixin inserita in modo dinamico potrebbe essere più ordinato - o semplicemente definendo una funzione e passando un'istanza di datetime.

In generale la monkeypatching non è molto Pythonic - è più un approccio Ruby ai problemi.

+0

TypeError: impossibile impostare gli attributi di tipo built-in/extension 'datetime.datetime ' – cmcginty

+1

Uno dei migliori usi è quello di implementare bit di funzionalità che normalmente non appariranno fino a una versione futura, soprattutto se sono abbastanza semplici e utili. – SilverbackNet

3

ho provato attuazione di una soluzione utilizzando scimmia-patching ma corse in errore:

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

Questo succede con datetime.datetime.millisecond = millisecond e GvR's __metaclass__=monkeypatch_class.

Forse datetime.so non può essere modificato con patch di scimmia. Se questo è vero, allora si potrebbe prendere in considerazione questo:

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 
2

unutbu era così vicino a quello che originariamente chiesto:

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

datetime.datetime = DateTime 

Io uso questo modello per implementare le API che mostrerà solo fino a 3.3 mentre si lavora in 3.1 o 3.2. Il resto della classe continua a lavorare come sempre, senza perdita di prestazioni.

Modificato per aggiungere: questo funziona solo con 2.6+ per i builtin, perché funziona solo su classi di nuovo stile. I builtins erano vecchio stile fino al 2.6.

+1

La migliore soluzione. Non dimenticare di usare @classmethod per i metodi di classe (sic!). – orkenstein

15

un po 'tardi, ma le seguenti opere:

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 

In sostanza, si utilizza ctypes per ottenere una copia modificabile del dizionario classi, e poi basta mettere il metodo che uso questo solo per un backport funzioni. alle vecchie versioni di Python, che penso sia abbastanza pulito. Ad esempio:

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