python - español - django tutorial
El mismo nombre funciona en la misma clase, ¿una forma elegante de determinar a quién llamar? (7)
¿Podría colocar su clase de Product
en dos módulos, v1 y v2, y luego importarlos condicionalmente?
Por ejemplo:
Productv1.py
class Product(object):
def function():
print(''for version 1.0'')
Productv2.py
class Product(object):
def function():
print(''for version 2.0'')
Luego en tu archivo principal:
main.py
if client.version == ''1.0'':
from Productv1 import Product
elif client.version == ''2.0'':
from Productv2 import Product
else:
print(f''function not support {self.version}'')
p = Product
p.function()
Estoy tratando de hacer el control de versión del producto en los scripts de Python por una razón específica, pero no pude averiguar cómo hacerlo de una manera elegante;
Actualmente estoy haciendo algo como el siguiente. Sin embargo, los scripts son difíciles de mantener cuando se modifica el contenido de la versión.
class Product(object):
def __init__(client):
self.version = client.version # get client version from another module
def function():
if self.version == ''1.0'':
print(''for version 1.0'')
elif self.version == ''2.0'':
print(''for version 2.0'')
else:
print(f''function not support {self.version}'')
Por lo tanto, quiero hacer algo como lo siguiente para separar las funciones con el mismo nombre.
class Product(object):
def __init__(client):
self.version = client.version # get client version from another module
def function():
print(''for version 1.0'')
def function():
print(''for version 2.0'')
Estaba pensando en usar el decorador para lograr esto:
class Product(object):
def __init__(client):
self.version = client.version # get client version from another module
@version(1.0)
def function():
print(''for version 1.0'')
@version(2.0)
def function():
print(''for version 2.0'')
Sin embargo, no pude averiguar cómo ... parece que un decorador no puede hacer este tipo de operación o simplemente no entiendo cómo hacerlo.
¿Hay alguna manera elegante de hacer esto?
Como otra opción, podrías ir a alguna fábrica para crear tu clase.
Cree sus funciones versionadas (tenga en cuenta el parámetro self
). Esto se puede hacer en un módulo diferente. Además, agregue alguna colección para obtener la función basada en el número de versión.
def func_10(self):
print(''for version 1.0'')
def func_20(self):
print(''for version 2.0'')
funcs = {"1.0": func_10,
"2.0": func_20}
Agregue una clase base que contenga las partes estáticas de su implementación y una clase de utilidad para crear sus instancias en:
class Product:
def __init__(self, version):
self.version = version
class ProductFactory(type):
@classmethod
def get_product_class(mcs, version):
# this will return an instance right away, due to the (version) in the end
return type.__new__(mcs, "Product_{}".format(version.replace(".","")), (Product,), {"function": funcs.get(version)})(version)
# if you want to return a class object to instantiate in your code omit the (version) in the end
Usando esto:
p1 = ProductFactory.get_product_class("1.0")
p2 = ProductFactory.get_product_class("2.0")
print(p1.__class__.__name__) # Product_10
p1.function() # for version 1.0
print(p1.function) # <bound method func_10 of <__main__.Product_10 object at 0x0000000002A157F0>>
print(p2.__class__.__name__) # Product_20
p2.function() # for version 2.0
print(p2.function) # <bound method func_20 of <__main__.Product_20 object at 0x0000000002A15860>>
La herencia es probablemente la mejor manera de hacer esto, pero como usted preguntó específicamente sobre los decoradores, quise mostrar que podía hacerlo usando decoradores.
Necesitará usar un diccionario para almacenar sus funciones por versión, luego busque qué versión usar en tiempo de ejecución. Aquí hay un ejemplo.
version_store = {}
def version(v):
def dec(f):
name = f.__qualname__
version_store[(name, v)] = f
def method(self, *args, **kwargs):
f = version_store[(name, self.version)]
return f(self, *args, **kwargs)
return method
return dec
class Product(object):
def __init__(self, version):
self.version = version
@version("1.0")
def function(self):
print("1.0")
@version("2.0")
def function(self):
print("2.0")
Product("1.0").function()
Product("2.0").function()
No soy un desarrollador de Python, pero no puedo evitar preguntarme por qué no haces algo como esto:
class Product(object):
def __init__(self, version):
self.version = version
def function(self):
print(''for version '' + self.version)
O puede hacerlo, dict.get
. dict.get
a una función e print
en lambda
si nada está bien:
def func_1(self):
print(''for version 1.0'')
def func_2(self):
print(''for version 2.0'')
def function(self):
funcs = {"1.0": self.func_1,
"2.0": self.func_2}
funcs.get(self.version,lambda: print(f''function not support {self.version}''))()
Ejemplo:
class Product(object):
def __init__(self,version):
self.version = version
def func_1(self):
print(''for version 1.0'')
def func_2(self):
print(''for version 2.0'')
def function(self):
funcs = {"1.0": self.func_1,
"2.0": self.func_2}
funcs.get(self.version,lambda: print(f''function not support {self.version}''))()
Product(''1.0'').function()
Product(''2.0'').function()
Product(''3.0'').function()
Salida:
for version 1.0
for version 2.0
function not support 3.0
Puedes usar decoradores para esto.
def v_decorate(func):
def func_wrapper(name):
return func(name)
return func_wrapper
Y
@v_decorate
def get_version(name):
return "for version {0} ".format(name)
Puedes llamarlo
get_version(1.0)
''for version 1.0 ''
get_version(2.0)
''for version 2.0 ''
En general, no lo hagas. Método de sobrecarga no se recomienda en Python. Si tiene que diferenciarse en el nivel de clase, lea la respuesta de @Loocid. No repetiré su excelente post.
Si quiere entrar en un nivel de método porque la diferencia es lo suficientemente pequeña para eso, intente algo como esto:
class Product:
def method(self):
if self.version == "1.0":
return self._methodv1()
elif self.version == "2.0":
return self._methodv2()
else:
raise ValueError("No appropriate method for version {}".format(self.version))
def _methodv1(self):
# implementation
def _methodv2(self):
# implementation
Tenga en cuenta aquí:
- Uso de métodos que comienzan con un guión bajo, para indicar que esos son la implementación.
- Manteniendo los métodos agradables y ordenados sin contaminación entre las versiones.
- Generando un error para versiones inesperadas (crash temprano y duro).
- En mi opinión no tan humilde, esto será mucho más claro para otras personas que lean tu publicación que usar decoradores.
O:
class Product:
def method_old(self):
# transform arguments to v2 method:
return self.method()
def method(self):
# implementation
- En caso de que desee dejar de usar por completo el uso anterior, y desea abandonar el soporte de la versión 1.0 en el futuro.
- Asegúrese de dar advertencias de desaprobación, para no sorprender a los usuarios de una biblioteca con cambios repentinos.
- Podría decirse que la mejor solución si nadie más usa su código.
Me da la sensación de que mi primer método sería más adecuado para su problema actual, pero quería incluir el segundo para la posteridad. Si editas tu código dentro de 10 años, ese te hará más feliz. Si edita el código con el código actual mañana, el primer método lo hará más feliz.