2010-07-23 30 views
6

Tôi đang cố gắng mở rộng lớp datetime.datetime của Python bằng một vài phương pháp bổ sung. Như vậy, ví dụ như tôi đang làm:Làm cách nào để mở rộng datetime.datetime của Python bằng các phương thức của riêng tôi?

import datetime 

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

nhưng sau đó nếu tôi làm

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

Điều này rõ ràng là bởi vì làm như d + delta gọi phương thức datetime.datetime.__add__() mà trả về một đối tượng datetime.datetime.

Có cách nào để tôi có thể biến đối tượng datetime.datetime này chuyển thành đối tượng DateTime không? Hoặc tôi có phải thực hiện lại tất cả các toán tử trong lớp con DateTime của mình để trả lại đúng loại không?

Trả lời

3

Trong trường hợp này tôi muốn các chức năng tự đứng đơn giản:

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

mixins có thể xảy ra trong Python (xem bình luận), nhưng tôi nghĩ rằng trong trường hợp này họ không cần thiết.

-1

Ở cấp simplet, bạn có thể làm một cái gì đó như thế này

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

datetime.datetime.millisecond = millisecond 

Tuy nhiên một mixin động chèn có thể gọn gàng - hoặc chỉ định một chức năng và đi qua nó một thể hiện của datetime.

Nói chung việc bắt chước không phải là rất Pythonic - đó là cách tiếp cận của Ruby đối với các vấn đề.

+0

LoạiError: không thể đặt thuộc tính của kiểu mở rộng/loại tiện ích mở rộng '.datetime ' – cmcginty

+1

Một trong những cách sử dụng tốt nhất là triển khai các bit chức năng bình thường sẽ không xuất hiện cho đến phiên bản tương lai, đặc biệt nếu chúng khá đơn giản và hữu ích. – SilverbackNet

3

tôi đã cố gắng thực hiện một giải pháp sử dụng khỉ vá nhưng chạy vào các lỗi:

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

Điều này xảy ra với datetime.datetime.millisecond = millisecondGvR's __metaclass__=monkeypatch_class.

Có lẽ datetime.so không thể được vá khỉ. Nếu đó là sự thật, sau đó bạn có thể muốn xem xét việc này:

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 rất gần với những gì bạn ban đầu yêu cầu:

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

datetime.datetime = DateTime 

tôi sử dụng mô hình này để thực hiện các API mà sẽ chỉ hiển thị trong 3,3 trong khi làm việc trong 3,1 hoặc 3,2. Phần còn lại của lớp vẫn tiếp tục hoạt động như trước đây, không mất hiệu năng.

Đã chỉnh sửa để thêm: Tính năng này chỉ hoạt động với 2.6+ đối với nội trang dựng sẵn, vì nó chỉ hoạt động trên các lớp kiểu mới. Nội trang được tạo kiểu cũ cho tới 2.6.

+1

Giải pháp tốt nhất. Đừng quên sử dụng @classmethod cho các phương thức lớp (sic!). – orkenstein

15

Một hơi muộn, nhưng các công việc sau:

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 

Về cơ bản, bạn sử dụng ctypes để có được một bản sao có thể chỉnh sửa các từ điển các lớp học, và sau đó chỉ cần đặt phương pháp của bạn trong tôi sử dụng này chỉ để backport chức năng. với các phiên bản python cũ hơn, mà tôi nghĩ là khá sạch sẽ. Ví dụ:

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 
Các vấn đề liên quan