2008-12-29 7 views
18

¿Por qué no funciona lo siguiente (Python 2.5.2)?¿Por qué no puedo subclasificar datetime.date?

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

quería crear una clase que era igual que datetime.date, pero con una función diferente __init__. Aparentemente mi función nunca se llama. En cambio, se llama al datetime.date.__init__ original y falla porque eso espera 3 argumentos y estoy pasando uno.

¿Qué está pasando aquí? ¿Y esto es una pista?

>>> datetime.date.__init__ 
<slot wrapper '__init__' of 'object' objects> 

¡Gracias!

+0

vea también http://stackoverflow.com/questions/309 129/why-cant-i-inherit-from-dict-and-exception-in-python – hop

Respuesta

34

En relación con otras respuestas, esto no tiene nada que ver con las fechas implementadas en C per se. El método __init__ no hace nada porque son objetos inmutables, por lo tanto, el constructor (__new__) debería hacer todo el trabajo. Se podría ver el mismo comportamiento de subclases int, str, etc.

>>> import datetime 
>>> class D(datetime.date): 
     def __new__(cls, year): 
      return datetime.date.__new__(cls, year, 1, 1) 


>>> D(2008) 
D(2008, 1, 1) 
+0

Espero que no te importe poner un poco de énfasis. – tzot

+0

Gran respuesta, no estaba muy claro sobre esto antes, pero ahora tiene mucho sentido. – Kiv

4

Aquí está la respuesta, y una solución posible (utilizar una función o strptime en lugar de subclases)

http://www.mail-archive.com/[email protected]/msg192783.html

+0

Gracias, el enlace al problema está justo en el dinero, pero no hay una explicación __why__ no funcionó. ¿Cuál es la raíz del problema y puede ser superado? – Arkady

0

Probablemente debería utilizar una función de la fábrica en vez de crear una subclase:

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

Sí, eso es genial, pero tengo curiosidad por saber por qué la subclase no funciona (cómo una clase de extensión marca la diferencia) y si esto se puede superar ... – Arkady

2

Tu función no está siendo pasada por alto; Python nunca llega al punto donde lo llamaría. Como datetime se implementa en C, realiza su inicialización en datetime.__new__ y no en datetime.__init__. Esto se debe a que datetime es inmutable. Podría prescindir de esto al anular __new__ en lugar de __init__. Pero como otras personas han sugerido, la mejor manera es probablemente no crear subclases de fecha y hora.

+1

@Benjamin: compruebe mi respuesta y considere corregirla el tuyo, ya que es el más votado hasta el momento; básicamente, solo su última oración podría considerarse útil; los otros están mal informados. Además, arregle su "it's" → "its" y "(Not" → "(Note" . – tzot

0

Puede envolver y agregar funcionalidad extendida a su envoltorio.

Aquí se muestra un ejemplo:

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) 

Y aquí es cómo funciona:

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

Tenga en cuenta que dir() no enumera datetime.date s atributos.

+0

@ ΤΖΩΤΖΙΟΥ: Tiene razón. Es realmente subclassable. Regresaré a los documentos y averiguaré dónde hice este error. Mientras tanto estoy arreglando la respuesta. Gracias. – muhuk

10

Lea la referencia de Python en Data model, especialmente sobre el __new__special method.

extracto de esa página (la cursiva es mía):

__new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.

datetime.datetime es también un tipo inmutable.

PS Si usted piensa que:

  • un objeto implementado en C no puede tener subclases, o
  • __init__ no se consiga llamar para C implementa objetos, solamente __new__

, por favor pruébelo:

>>> 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 
>>> 
+0

Votación máxima, porque esta parece ser la única respuesta en SO con un enlace de documentación. Pero su ejemplo está roto. Solo funciona porque el inicializador es un argumento opcional. Pruebe 'a = A ('c', [], 5,8)'! – mkiever

+0

' >>> a = array.array ('c', [], 5, 8) Traceback (última llamada más reciente): Archivo "", línea 1, en TypeError: array() toma como máximo 2 argumentos (4 dados) >>> a = A ('c', [], 5, 8) Rastreo (llamada último el más reciente): Archivo "", línea 1, en TypeError: array() toma como máximo 2 argumentos (4 dado) ' ¿Qué sería un ejemplo no roto? @mkiever – tzot

Cuestiones relacionadas