То же имя функционирует в том же классе, элегантный способ определить, на какой вызов?
Я пытаюсь сделать контроль над версиями продуктов в сценариях Python по определенной причине, но я не мог понять, как это сделать элегантным способом - пожалуйста, помогите.
В настоящее время я делаю что-то вроде ниже. Однако при сохранении содержимого версии сценарии трудно поддерживать.
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}')
Поэтому я хочу сделать что-то вроде ниже, чтобы отделить функции с тем же именем.
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')
Я думал об использовании декоратора, чтобы добиться этого:
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')
Тем не менее, я не смог понять, как... похоже, что декоратор не может выполнить эту операцию или я просто не понимаю, как это сделать.
Есть ли элегантный способ сделать это?
Ответы
Ответ 1
Наследование, вероятно, лучший способ сделать это, но поскольку вы спросили конкретно об декораторах, я хотел показать, что вы можете сделать это с помощью декораторов.
Вам нужно будет использовать словарь для хранения ваших функций по версии, а затем посмотрите, какую версию использовать во время выполнения. Вот пример.
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()
Ответ 2
Не могли бы вы поместить свой класс Product
в два модуля, v1 и v2, а затем импортировать их условно?
Например:
Productv1.py
class Product(object):
def function():
print('for version 1.0')
Productv2.py
class Product(object):
def function():
print('for version 2.0')
Затем в вашем основном файле:
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()
Ответ 3
В качестве другого варианта вы можете пойти на какой-нибудь завод, чтобы создать свой класс.
Создайте свои версии (обратите внимание на параметр self
). Это можно сделать в другом модуле. Кроме того, добавьте некоторую коллекцию для извлечения функции на основе номера версии.
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}
Добавьте базовый класс, содержащий статические части вашей реализации, и класс утилиты для создания ваших экземпляров в:
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
Используя это:
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>>
Ответ 4
В общем, не надо. Перегрузка метода не рекомендуется в Python. Если вам нужно дифференцироваться на уровне класса, прочитайте @Loocid ответ. Я не буду повторять его отличный пост.
Если вы хотите на уровне метода, потому что разница достаточно мала для этого, попробуйте что-то вроде этого:
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
Обратите внимание:
- Использование методов, начинающихся с подчеркивания, чтобы указать, что это реализация.
- Хранение методов хорошо и аккуратно, без загрязнения между версиями
- Принятие ошибки для неожиданных версий (сбой рано и сложно).
- По моему не очень скромному мнению, это будет намного яснее для других людей, читающих ваш пост, чем с помощью декораторов.
Или же:
class Product:
def method_old(self):
# transform arguments to v2 method:
return self.method()
def method(self):
# implementation
- Если вы хотите полностью отказаться от предыдущего использования и хотите отказаться от поддержки версии 1.0 в будущем.
- Обязательно дайте предупреждения об устаревании, чтобы не удивлять пользователей библиотеки с внезапными изменениями.
- Возможно, лучшее решение, если никто не использует ваш код.
Я получаю вибрацию, мой первый метод будет более подходящим для вашей проблемы, но я хотел включить вторую для потомков. Если вы отредактируете свой код через 10 лет, это сделает вас счастливее. Если вы завтра измените код с использованием текущего кода, первый способ сделает вас счастливее.
Ответ 5
Вы можете использовать декораторы для этого
def v_decorate(func):
def func_wrapper(name):
return func(name)
return func_wrapper
А также
@v_decorate
def get_version(name):
return "for version {0} ".format(name)
Вы можете назвать это
get_version(1.0)
'for version 1.0 '
get_version(2.0)
'for version 2.0 '
Ответ 6
Я не разработчик python, но я не могу не задаться вопросом, почему вы не просто делаете что-то вроде этого:
class Product(object):
def __init__(self, version):
self.version = version
def function(self):
print('for version ' + self.version)
Ответ 7
Или вы можете сделать, dict.get
вызвать функцию и print
в lambda
если ничего не будет правильно:
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}'))()
Пример:
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()
Выход:
for version 1.0
for version 2.0
function not support 3.0