property descriptors classmethod python python-2.7 static-methods class-method python-descriptors

classmethod - python 3 descriptors



¿Por qué @staticmethod no se conserva en todas las clases, cuando @classmethod es? (2)

DESCARGO DE RESPONSABILIDAD: Esta no es realmente una respuesta, pero tampoco encaja en un formato de comentario.

Tenga en cuenta que con Python2 @classmethod @classmethod se conserva correctamente en todas las clases. En el siguiente código, la llamada a B.one() funciona como si se invocó a través de la clase A :

$ cat test.py class A(object): @classmethod def one(cls): print("I am class", cls.__name__) class A2(A): pass class B(object): one = A.one A.one() A2.one() B.one() $ python2 test.py (''I am class'', ''A'') (''I am class'', ''A2'') (''I am class'', ''A'')

Tome el siguiente script de ejemplo:

class A(object): @classmethod def one(cls): print("I am class") @staticmethod def two(): print("I am static") class B(object): one = A.one two = A.two B.one() B.two()

Cuando ejecuto este script con Python 2.7.11 obtengo:

I am class Traceback (most recent call last): File "test.py", line 17, in <module> B.two() TypeError: unbound method two() must be called with B instance as first argument (got nothing instead)

Parece que el decorador de @classmethod se conserva en todas las clases, pero @staticmethod no lo está.

Python 3.4 se comporta como se esperaba:

I am class I am static

¿Por qué Python2 no preserva el método @static, y hay una solución alternativa?

editar: tomar dos de una clase (y retener @staticmethod) parece funcionar, pero esto todavía me parece extraño.


classmethod y staticmethod son descriptores, y ninguno de ellos está haciendo lo que espera, no solo staticmethod .

Cuando accedes a A.one , está creando un método enlazado en A , luego haciendo que sea un atributo de B , pero como está vinculado a A , el argumento cls siempre será A , incluso si llamas a B.one (este es el caso) en Python 2 y Python 3; está mal en todos lados).

Cuando accedes a A.two , devuelve el objeto de función raw (el descriptor de staticmethod no necesita hacer nada especial además de evitar el enlace que pasaría self o cls , por lo que solo devuelve lo que envuelve). Pero ese objeto de función sin staticmethod se adjunta a B como un método de instancia staticmethod , porque sin el envoltorio de los elementos staticmethod , es como si lo hubieras definido normalmente.

La razón por la cual este último funciona en Python 3 es que Python 3 no tiene ningún concepto de métodos independientes. Tiene funciones (que si se accede a través de una instancia de una clase se vuelven métodos vinculados) y métodos vinculados, donde Python 2 tiene funciones, métodos independientes y métodos vinculados.

Los métodos sin consolidar comprueban que se invocan con un objeto del tipo correcto y, por lo tanto, con su error. Las funciones simples solo quieren la cantidad correcta de argumentos.

El decorador de staticmethod en Python 3 todavía está devolviendo el objeto de función sin procesar, pero en Python 3, está bien; dado que no es un objeto de método desatado especial, si lo llamas a la clase en sí, es como una función de espacio de nombres, no un método de ningún tipo. Verías el problema si intentaras hacer:

B().two()

sin embargo, porque eso hará que un método vinculado de esa instancia de B y las two funciones, pasando un argumento extra ( self ) que two no acepte. Básicamente, en Python 3, staticmethod es una conveniencia que te permite llamar a la función en instancias sin causar un enlace, pero si solo llamas a la función al hacer referencia a la clase, no es necesaria, porque es solo una función simple, no el Python 2 "método sin consolidar".

Si tiene alguna razón para realizar esta copia (normalmente, le sugiero que herede de A , pero lo que sea), y quiere asegurarse de obtener la versión de la función ajustada al descriptor, no lo que le indique el descriptor cuando acceda a A , __dict__ el protocolo del descriptor al acceder directamente a A ''s __dict__ :

class B(object): one = A.__dict__[''one''] two = A.__dict__[''two'']

Copiando directamente desde el diccionario de la clase, nunca se invoca el protocolo descriptor magic, y obtienes las staticmethod classmethod de one y two del staticmethod y de la classmethod .