2010-07-23 3 views
6

J'essaie d'étendre la classe datetime.datetime de Python avec quelques méthodes supplémentaires. Ainsi, par exemple, je fais:Comment puis-je étendre datetime.datetime de Python avec mes propres méthodes?

import datetime 

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

mais si je

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

Ceci est évidemment parce que cela d + delta appelle la méthode datetime.datetime.__add__() qui retourne un objet datetime.datetime.

Est-il possible de convertir cet objet datetime.datetime en objet DateTime? Ou devrais-je réimplémenter tous les opérateurs dans ma sous-classe DateTime pour renvoyer le type correct?

Répondre

3

Dans ce cas, je préfère de simples fonctions autonomes:

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

Mixins sont possibles en Python (voir le commentaire), mais je pense que dans ce cas, ils sont superflus.

-1

Au niveau simplet, vous pouvez faire quelque chose comme ça

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

datetime.datetime.millisecond = millisecond 

Toutefois, un mixin dynamique inséré pourrait être plus propre - ou tout simplement définir une fonction et de la transmettre une instance de datetime.

En général monkeypatching n'est pas très Pythonic - c'est plus une approche Ruby aux problèmes.

+0

TypeError: impossible de définir les attributs de type datetime/type d'extension intégré.datetime ' – cmcginty

+1

L'une des meilleures utilisations consiste à implémenter des éléments de fonctionnalité qui normalement n'apparaîtront pas avant une future version, surtout s'ils sont assez simples et utiles. – SilverbackNet

3

J'ai essayé la mise en œuvre d'une solution à l'aide singe-patcher, mais a couru dans l'erreur:

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

Cela se produit avec datetime.datetime.millisecond = millisecond et GvR's __metaclass__=monkeypatch_class.

Peut-être que datetime.so ne peut pas être patché par un singe. Si cela est vrai, alors vous voudrez peut-être considérer ceci:

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 était si proche de ce que vous avez initialement demandé:

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

datetime.datetime = DateTime 

J'utilise ce modèle pour mettre en œuvre des API qui ne seront visibles que dans 3.3 tout en travaillant en 3.1 ou 3.2. Le reste de la classe continue à travailler comme jamais, sans perte de performance.

Édité pour ajouter: Cela ne fonctionne qu'avec 2.6+ pour les builtins, car il ne fonctionne que sur les nouvelles classes. Les builtins étaient de style ancien jusqu'au 2.6.

+1

La meilleure solution. N'oubliez pas d'utiliser @classmethod pour les méthodes de classe (sic!). – orkenstein

15

Un peu en retard, mais les travaux suivants:

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 

Fondamentalement, vous utilisez ctypes pour obtenir une copie modifiable du cours du dictionnaire, puis il suffit de mettre votre méthode que j'utilise ce que pour rétroporter fonctions. aux anciennes versions de python, que je pense être assez propre. Par exemple:

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 
Questions connexes