poo - programacion orientada a objetos python pdf
Los decoradores de Python que son parte de una clase base no se pueden usar para decorar funciones de miembros en clases heredadas (4)
Has respondido la pregunta al preguntarla: ¿qué argumento esperarías obtener si llamas a SubSystem.UpdateGUI
? No hay una instancia obvia que deba pasarse al decorador.
Hay varias cosas que podrías hacer para evitar esto. ¿Tal vez ya tienes un subSystem
que has subSystem
en otra parte? Entonces podrías usar su decorador:
subSystem = SubSystem()
subSystem.UpdateGUI(...)
Pero tal vez no necesitabas la instancia en primer lugar, ¿solo la clase SubSystem
? En ese caso, use el decorador classmethod
para decirle a Python que esta función debe recibir su clase como primer argumento en lugar de una instancia:
@classmethod
def UpdateGUI(cls,...):
...
Finalmente, ¡quizás no necesites acceder a la instancia o a la clase! En ese caso, use staticmethod
:
@staticmethod
def UpdateGUI(...):
...
Ah, por cierto, la convención de Python es reservar los nombres de CamelCase para las clases y usar los nombres mixedCase o under_scored para los métodos en esa clase.
Los decoradores de Python son divertidos de usar, pero parece que choqué contra una pared debido a la forma en que los argumentos se transmiten a los decoradores. Aquí tengo un decorador definido como parte de una clase base (el decorador tendrá acceso a los miembros de la clase, por lo tanto, requerirá el parámetro automático).
class SubSystem(object):
def UpdateGUI(self, fun): #function decorator
def wrapper(*args):
self.updateGUIField(*args)
return fun(*args)
return wrapper
def updateGUIField(self, name, value):
if name in self.gui:
if type(self.gui[name]) == System.Windows.Controls.CheckBox:
self.gui[name].IsChecked = value #update checkbox on ui
elif type(self.gui[name]) == System.Windows.Controls.Slider:
self.gui[name].Value = value # update slider on ui
...
He omitido el resto de la implementación. Ahora esta clase es una clase base para varios subsistemas que heredarán de ella; algunas de las clases heredadas necesitarán usar el decorador UpdateGUI.
class DO(SubSystem):
def getport(self, port):
"""Returns the value of Digital Output port "port"."""
pass
@SubSystem.UpdateGUI
def setport(self, port, value):
"""Sets the value of Digital Output port "port"."""
pass
Una vez más, he omitido las implementaciones de funciones ya que no son relevantes.
En resumen, el problema es que, aunque puedo acceder al decorador definido en la clase base de la clase heredada al especificarlo como SubSystem.UpdateGUI, finalmente obtengo este TypeError cuando intento usarlo:
unbound method UpdateGUI() must be called with SubSystem instance as first argument (got function instance instead)
¡Esto se debe a que no tengo una forma inmediatamente identificable de pasar el self
parámetro al decorador!
¿Hay alguna forma de hacer esto? ¿O alcancé los límites de la implementación del decorador actual en Python?
Necesita usar una instancia de SubSystem
para decorar o usar un classmethod
como sugiere Kenny.
subsys = SubSystem()
class DO(SubSystem):
def getport(self, port):
"""Returns the value of Digital Output port "port"."""
pass
@subsys.UpdateGUI
def setport(self, port, value):
"""Sets the value of Digital Output port "port"."""
pass
Usted decide qué hacer al decidir si desea que todas las instancias de subclas compartan la misma interfaz GUI o si desea permitir que las distintas tengan interfaces distintas.
Si todos comparten la misma interfaz GUI, utilice un método de clase y haga que todo lo que el decorador acceda a una instancia de clase.
Si pueden tener interfaces distintas, debe decidir si desea representar la distinción con la herencia (en cuyo caso también usaría classmethod
y llamará al decorador en las subclases de SubSystem
) o si está mejor representado como instancias distintas. En ese caso, haga una instancia para cada interfaz y llame al decorador en esa instancia.
Necesitas hacer que UpdateGUI
un @classmethod
, y hacer que tu wrapper
consciente de @classmethod
self
. Un ejemplo de trabajo:
class X(object):
@classmethod
def foo(cls, fun):
def wrapper(self, *args, **kwargs):
self.write(*args, **kwargs)
return fun(self, *args, **kwargs)
return wrapper
def write(self, *args, **kwargs):
print(args, kwargs)
class Y(X):
@X.foo
def bar(self, x):
print("x:", x)
Y().bar(3)
# prints:
# (3,) {}
# x: 3
Podría ser más fácil sacar al decorador de la clase SubSytem
: (Tenga en cuenta que estoy asumiendo que el self
que llama a setport
es el mismo self
que desea usar para llamar a updateGUIField
).
def UpdateGUI(fun): #function decorator
def wrapper(self,*args):
self.updateGUIField(*args)
return fun(self,*args)
return wrapper
class SubSystem(object):
def updateGUIField(self, name, value):
# if name in self.gui:
# if type(self.gui[name]) == System.Windows.Controls.CheckBox:
# self.gui[name].IsChecked = value #update checkbox on ui
# elif type(self.gui[name]) == System.Windows.Controls.Slider:
# self.gui[name].Value = value # update slider on ui
print(name,value)
class DO(SubSystem):
@UpdateGUI
def setport(self, port, value):
"""Sets the value of Digital Output port "port"."""
pass
do=DO()
do.setport(''p'',''v'')
# (''p'', ''v'')