¿Cuál es la forma más pitónica y correcta de hacer alias de composición?
Aquí hay un escenario hipotético:
class House:
def cleanup(self, arg1, arg2, kwarg1=False):
# do something
class Person:
def __init__(self, house):
self.house = house
# aliases house.cleanup
# 1.
self.cleanup_house = self.house.cleanup
# 2.
def cleanup_house(self, arg1, arg2, kwarg1=False):
return self.house.cleanup(arg1=arg1, arg2=arg2, kwarg1=kwarg1)
AFAIK con el n. ° 1 mis editores probados los entienden tan bien como el n. ° 2: autocompletado, cadenas de documentos, etc.
¿Hay alguna desventaja en el enfoque # 1? ¿Qué camino es más correcto desde el punto de vista de Python?
Para ampliar el método # 1, la variante insinuable y de tipo insinuado sería inmune a todos los problemas señalados en los comentarios:
class House:
def cleanup(self, arg1, arg2, kwarg1=False):
"""clean house is nice to live in!"""
pass
class Person:
def __init__(self, house: House):
self._house = house
# aliases
self.cleanup_house = self.house.cleanup
@property
def house(self):
return self._house
Person.cleanup_house
hacer. Obtiene la casa asociada con esa persona y la limpia. # 1 limpiará alguna casa, y en algún momento (inicialización), esa casa fue la correcta. No se trata bien con la persona que cambia de casa, o si una persona no tiene casaRespuestas:
Hay varios problemas con el primer método:
house
unproperty
con un setter, pero eso es un trabajo no trivial para algo que no debería requerirlo. Vea el final de esta respuesta para una implementación de muestra.cleanup_house
No será heredable. Un objeto de función definido en una clase es un descriptor que no es de datos que puede heredarse y anularse, así como vincularse a una instancia. Un atributo de instancia como en el primer enfoque no está presente en la clase en absoluto. El hecho de que sea un método encuadernado es incidental. Una clase secundaria no podrá accedersuper().cleanup_house
, por un ejemplo concreto.person.cleanup_house.__name__ != 'cleanup_house'
. Esto no es algo que verifique con frecuencia, pero cuando lo haga, esperaría que fuera el nombre de la funcióncleanup
.La buena noticia es que no tiene que repetir las firmas varias veces para usar el enfoque n. ° 2. Python ofrece la muy conveniente notación splat (
*
) / splatty-splat (**
) para delegar todas las comprobaciones de argumentos al método que se está envolviendo:Y eso es. Todos los argumentos regulares y predeterminados se pasan como están.
Esta es la razón por la que # 2 es, con mucho, el enfoque más pitónico. No tengo idea de cómo interactuará con los editores que admiten sugerencias de tipo a menos que copie la firma del método.
Una cosa que puede ser un problema es que
cleanup_house.__doc__
no es lo mismo quehouse.cleanup.__doc__
. Esto podría merecer una conversión dehouse
aproperty
, cuyo setter asignacleanup_house.__doc__
.Para abordar el problema 1. (pero no 2. o 3.), puede implementarlo
house
como una propiedad con un establecedor. La idea es actualizar los alias cada vez quehouse
cambie el atributo. Esta no es una buena idea en general, pero aquí hay una implementación alternativa a lo que tiene en la pregunta que probablemente funcionará un poco mejor:fuente
*args, **kwargs
las funciones de finalización automática de interrupción y en general son difíciles de trabajar. También es necesario definir dos veces para docstringshouse.cleanup
yperson.cleanup_house
o hackear en alguna manera. Entonces, con este enfoque, debe mantener constantemente las firmas y las cadenas de documentos manualmente, lo que puede ser mucho trabajo en grandes programas.*args
y**kwargs
son incómodos. Están literalmente allí para que no tenga que mantener firmas.__doc__
puede mantenerse por asignación directa.functool.wraps(<parent_func>)
decorador, sin embargo, que introduce una gran cantidad de inconsistencias también.Solo quería agregar un enfoque más aquí, que si desea
house
ser público y configurable (generalmente trataría algo inmutable), puede hacer quecleanup_house
la propiedad sea así:Al menos en un Ipython REPL, la finalización del código y la cadena de documentos parecen funcionar como es de esperar. Tenga en cuenta cómo interactuaría con las anotaciones de tipo ...
EDITAR: por lo tanto, mypy 0.740 al menos no puede inferir la firma de tipo de
person.cleanup_house
, por lo que no es genial, aunque no es sorprendente:Todavía seguiría con el # 2.
fuente