同一个类中的同名函数,以优雅的方式确定要调用哪个?

时间:2018-09-10 06:15:54

标签: python version-control python-3.6 python-decorators

出于特定原因,我试图在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')

但是,我无法弄清楚……装饰器似乎无法执行此类操作,或者我只是不知道该怎么做。

有什么优雅的方法吗?

7 个答案:

答案 0 :(得分:32)

继承可能是实现此目的的最佳方法,但是由于您专门询问了装饰器,因此我想向您展示可以使用装饰器来做到这一点。

您将需要使用字典来按版本存储函数,然后在运行时查找要使用的版本。这是一个例子。

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()

答案 1 :(得分:9)

您可以将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()

答案 2 :(得分:7)

作为另一种选择,您可以去一些工厂来创建您的课程。

创建版本化的函数(请注意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>>

答案 3 :(得分:6)

通常不要。 Method overloading在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

在这里注意:

  1. 使用以下划线开头的方法来表示这些是 实施。
  2. 保持方法整洁而没有 版本之间的污染
  3. 引发意外版本(早期崩溃和严重崩溃)的错误。
  4. 以我不太谦逊的观点,与使用装饰器的其他人相比,这对于其他阅读您的帖子的人来说要清晰得多。

或者:

class Product:

    def method_old(self):
        # transform arguments to v2 method:
        return self.method()

    def method(self):
        # implementation
  1. 如果您要完全弃用以前的用法,并希望将来放弃对1.0版的支持。
  2. 请确保发出弃用警告,以免突然改变库的用户。
  3. 如果没有其他人使用您的代码,可以说是更好的解决方案。

我感到共鸣,我的第一种方法会更适合您眼前的问题,但我想为后代提供第二种方法。如果您从现在开始编辑代码10年,那将使您更快乐。如果明天您使用当前代码编辑代码,第一种方法将使您更快乐。

答案 4 :(得分:2)

我不是python开发人员,但我忍不住想知道为什么您不只是做这样的事情:

class Product(object):
    def __init__(self, version):
        self.version = version
    def function(self):
        print('for version ' + self.version)

答案 5 :(得分:2)

或者您可以执行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

答案 6 :(得分:1)

您可以为此使用装饰器

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 '