Cómo lanzar objetos en Python

Tengo dos clases (llamémoslas Working y ReturnStatement) que no puedo modificar, pero quiero ampliarlas con el registro. El truco es que el método de trabajo devuelve un objeto ReturnStatement, por lo que el nuevo objeto MutantWorking también devuelve ReturnStatement a menos que pueda convertirlo en MutantReturnStatement. Diciendo con código:

# these classes can't be changed class ReturnStatement(object): def act(self): print "I'm a ReturnStatement." class Working(object): def do(self): print "I am Working." return ReturnStatement() # these classes should wrap the original ones class MutantReturnStatement(ReturnStatement): def act(self): print "I'm wrapping ReturnStatement." return ReturnStatement().act() class MutantWorking(Working): def do(self): print "I am wrapping Working." # !!! this is not working, I'd need that casting working !!! return (MutantReturnStatement) Working().do() rs = MutantWorking().do() #I can use MutantWorking just like Working print "--" # just to separate output rs.act() #this must be MutantReturnState.act(), I need the overloaded method 

El resultado esperado:
Estoy envolviendo trabajando.
Estoy trabajando.

Estoy envolviendo ReturnStatement.
Soy una statement de retorno.

¿Es posible resolver el problema? También tengo curiosidad por si el problema se puede resolver en PHP, también. A menos que obtenga una solución de trabajo, no puedo aceptar la respuesta, así que escriba el código de trabajo para que lo acepten.

No hay casting como las otras respuestas ya explicadas. Puedes hacer subclases o hacer nuevos tipos modificados con la funcionalidad adicional usando decoradores .

Aquí hay un ejemplo completo (crédito a ¿Cómo hacer una cadena de decoradores funcionales? ). No necesitas modificar tus clases originales. En mi ejemplo la clase original se llama Working.

 # decorator for logging def logging(func): def wrapper(*args, **kwargs): print func.__name__, args, kwargs res = func(*args, **kwargs) return res return wrapper # this is some example class you do not want to/can not modify class Working: def Do(c): print("I am working") def pr(c,printit): # other example method print(printit) def bla(c): # other example method c.pr("saybla") # this is how to make a new class with some methods logged: class MutantWorking(Working): pr=logging(Working.pr) bla=logging(Working.bla) Do=logging(Working.Do) h=MutantWorking() h.bla() h.pr("Working") h.Do() 

esto imprimirá

 h.bla() bla (<__main__.MutantWorking instance at 0xb776b78c>,) {} pr (<__main__.MutantWorking instance at 0xb776b78c>, 'saybla') {} saybla pr (<__main__.MutantWorking instance at 0xb776b78c>, 'Working') {} Working Do (<__main__.MutantWorking instance at 0xb776b78c>,) {} I am working 

Además, me gustaría entender por qué no se puede modificar una clase. ¿Has probado? Porque, como alternativa a hacer una subclase, si te sientes dynamic casi siempre puedes modificar una clase antigua en su lugar:

 Working.Do=logging(Working.Do) ReturnStatement.Act=logging(ReturnStatement.Act) 

Actualización: aplicar el registro a todos los métodos de una clase

Como ahora pediste específicamente para esto. Puede recorrer todos los miembros y aplicar el registro a todos ellos. Pero necesitas definir una regla para qué tipo de miembros modificar. El siguiente ejemplo excluye cualquier método con __ en su nombre.

 import types def hasmethod(obj, name): return hasattr(obj, name) and type(getattr(obj, name)) == types.MethodType def loggify(theclass): for x in filter(lambda x:"__" not in x, dir(theclass)): if hasmethod(theclass,x): print(x) setattr(theclass,x,logging(getattr(theclass,x))) return theclass 

Con esto, todo lo que tienes que hacer para hacer una nueva versión registrada de una clase es:

 @loggify class loggedWorker(Working): pass 

O modifique una clase existente en su lugar:

 loggify(Working) 

No hay “casting” en Python. Cualquier subclase de una clase se considera una instancia de sus padres. El comportamiento deseado se puede lograr llamando adecuadamente a los métodos de superclase y anulando atributos de clase.

Lo que puede hacer en su ejemplo es tener que tener un inicializador de subclase que reciba la superclase y copie sus atributos relevantes; por lo tanto, su statement de devolución de mutante podría escribirse así:

 class MutantReturnStatement(ReturnStatement): def __init__(self, previous_object=None): if previous_object: self.attribute = previous_object.attribute # repeat for relevant attributes def act(self): print "I'm wrapping ReturnStatement." return ReturnStatement().act() 

Y luego cambia tu clase de MutantWorking a:

 class MutantWorking(Working): def do(self): print "I am wrapping Working." return MutantReturnStatement(Working().do()) 

Hay formas en Pythonic para no tener muchas líneas self.attr = other.attr en el método __init__ si hay muchos atributos (como, más de 3 :-)) que desea copiar: la más perezosa de los cuales sería simplemente Copie el atributo __dict__ la otra instancia.

Alternativamente, si sabe lo que está haciendo , también podría simplemente cambiar el atributo __class__ de su objeto objective a la clase deseada, pero eso puede ser engañoso y llevarlo a errores sutiles (no se __init__ método __init__ de la subclase, no funcionaría en clases no definidas con python, y otros posibles problemas), no recomiendo este enfoque, esto no es “casting”, es el uso de la introspección para forzar un cambio de objeto y solo se incluye para mantener la respuesta completa :

 class MutantWorking(Working): def do(self): print "I am wrapping Working." result = Working.do(self) result.__class__ = MutantReturnStatement return result 

Nuevamente, esto debería funcionar, pero no lo hagas, usa el método anterior.

Por cierto, no tengo demasiada experiencia con otros idiomas OO, que permiten el envío, pero ¿está permitido el envío a una subclase en cualquier idioma? ¿Tiene sentido? Creo que el casting solo está permitido a las clases parentales.

No hay forma directa.

Puedes definir el inicio de MutantReturnStatement de esta manera:

 def __init__(self, retStatement): self.retStatement = retStatement 

y luego usarlo de esta manera:

 class MutantWorking(Working): def do(self): print "I am wrapping Working." # !!! this is not working, I'd need that casting working !!! return MutantReturnStatement(Working().do()) 

Y debería deshacerse de heredar ReturnStatement en su envoltorio, como esto

 class MutantReturnStatement(object): def act(self): print "I'm wrapping ReturnStatement." return self.retStatement.act() 

No necesitas un casting aquí. Solo necesitas

 class MutantWorking(Working): def do(self): print "I am wrapping Working." Working().do() return MutantReturnStatement() 

Obviamente, esto dará el retorno correcto y la impresión deseada.