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

Tim*_*Lin 35 python version-control python-decorators python-3.6

我试图在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}')
Run Code Online (Sandbox Code Playgroud)

因此,我想做类似下面的事情来分离具有相同名称的功能.

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')
Run Code Online (Sandbox Code Playgroud)

我正在考虑使用装饰器来实现这个目的:

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')
Run Code Online (Sandbox Code Playgroud)

但是,我没弄明白怎么样......看起来装饰师不能做这种操作或者我只是不明白怎么做.

有没有优雅的方法来做到这一点?

Bi *_*ico 33

继承可能是执行此操作的最佳方式,但由于您专门询问了装饰器,我想告诉您可以使用装饰器执行此操作.

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

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()
Run Code Online (Sandbox Code Playgroud)

  • 你可能想要`f .__ qualname__`来避免`Product.function`和`Ambassadorial.function`之间的冲突...... (4认同)
  • @Bergi方法只是该类的成员.每个声明都取代了最后一个版本,但没关系因为,就像你说的那样,它们都是一样的:) (3认同)

Loo*_*cid 9

你能把你的Product班级分成两个模块,v1和v2,然后有条件地导入它们吗?

例如:

Productv1.py

class Product(object):
    def function():
        print('for version 1.0')
Run Code Online (Sandbox Code Playgroud)

Productv2.py

class Product(object):
    def function():
        print('for version 2.0')
Run Code Online (Sandbox Code Playgroud)

然后在你的主文件中:

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()
Run Code Online (Sandbox Code Playgroud)

  • @TimmyLin在这种情况下,你可以有一个基类,比如说`Product`,它包含所有重复的内容,然后有一个`Productv1`和`Productv2`继承它,只是拥有不同的东西.这样就没有重复的代码了. (2认同)

shm*_*mee 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}
Run Code Online (Sandbox Code Playgroud)

添加包含实现的静态部分的基类和实用程序类,以在以下位置创建实例:

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
Run Code Online (Sandbox Code Playgroud)

使用这个:

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>>
Run Code Online (Sandbox Code Playgroud)


Glo*_*eye 6

一般来说,不要. 在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
Run Code Online (Sandbox Code Playgroud)

请注意:

  1. 使用以下划线开头的方法来指示那些是实现.
  2. 保持方法整洁,没有版本之间的污染
  3. 引发意外版本的错误(早期崩溃和硬盘崩溃).
  4. 在我不那么谦虚的意见中,对于阅读你帖子的其他人而言,这比使用装饰者要清楚得多.

要么:

class Product:

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

    def method(self):
        # implementation
Run Code Online (Sandbox Code Playgroud)
  1. 如果您想完全弃用以前的用法,并希望将来删除1.0版支持.
  2. 请务必提供弃用警告,以免突然更改库的用户.
  3. 如果没有其他人使用您的代码,可以说是更好的解决方案.

我得到的氛围我的第一种方法更适合你手头的问题,但我想把第二种方法包括在后代中.如果您在10年后编辑代码,那将使您更快乐.如果明天使用当前代码编辑代码,第一种方法会让你更开心.