Python __call()__ 实践例子

发布于 2020-06-08  733 次阅读


Django有一些默认的内置验证器,比如电子邮件验证器、url验证器等,这些都属于正则表达式验证器。为了干净地实现这些,Django求助于可调用的类(而不是函数)。它在RegexValidator中实现默认的Regex验证逻辑,然后为其他验证扩展这些类

class RegexValidator(object):
    def __call__(self, value):
        # validation logic

class URLValidator(RegexValidator):
    def __call__(self, value):
        super(URLValidator, self).__call__(value)
        #additional logic

class EmailValidator(RegexValidator):
    # some logic

这个例子使用了记忆,基本上是将值存储在一个表中(本例中是字典),这样您可以稍后查找它们,而不是重新计算它们。

这里我们使用一个带有__call__方法的简单类来计算阶乘(通过可调用对象),而不是使用包含静态变量的阶乘函数(因为在Python中这是不可能的)。

class Fac:
    def __init__(self):
        self.cache = {}

    def __call__(self, n):

        if n not in self.cache:
            if n == 0:
                self.cache[n] = 1
            else:
                self.cache[n] = n * self.__call__(n-1)

        return self.cache[n]

fact = Fac()

for i in range(10):
    print("{}! = {}".format(i, fact(i)))

########################################################
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880

__call__还用于在python中实现decorator类。在这种情况下,当使用decorator的方法被调用时,类的实例被调用

class Counter:
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        return self.func(*args, **kwargs)

@Counter
def foo():
    pass

for i in range(10):
    foo()

print(foo.count)  # 10
class EnterExitParam(object):

    def __init__(self, p1):
        self.p1 = p1

    def __call__(self, f):
        def new_f():
            print("Entering", f.__name__)
            print("p1=", self.p1)
            f()
            print("Leaving", f.__name__)
        return new_f


@EnterExitParam("foo bar")
def hello():
    print("Hello")

从其他线程获取任务并按顺序执行它们的相对复杂的工作线程

class Worker(object):
    def __init__(self, *args, **kwargs):
        self.queue = queue.Queue()
        self.args = args
        self.kwargs = kwargs

    def add_task(self, task):
        self.queue.put(task)

    def __call__(self):
        while True:
            next_action = self.queue.get()
            success = next_action(*self.args, **self.kwargs)
            if not success:
               self.add_task(next_action)

我们可以使用__call()__方法将其他类方法用作静态方法

class _Callable:
    def __init__(self, anycallable):
        self.anycallable = anycallable

    def __call__(self, *args, **kwargs):
        return self.anycallable

class Model:

    def get_instance(host='127.0.0.1', user='root', passwd='123456', table_name='users'):

        """ do something"""
        print('连接成功-------')

    get_instance = _Callable(get_instance)

provs_fac = Model.get_instance()

一个常见的例子是functools.partial函数工具,这是一个简化版本(Python>=3.5):

class partial:
    """New function with partial application of the given arguments and keywords."""

    def __new__(cls, func, *args, **kwargs):
        if not callable(func):
            raise TypeError("the first argument must be callable")
        self = super().__new__(cls)

        self.func = func
        self.args = args
        self.kwargs = kwargs
        return self

    def __call__(self, *args, **kwargs):
        return self.func(*self.args, *args, **self.kwargs, **kwargs)

def add(x, y):
    return x + y

inc = partial(add, y=1)
print(inc(41))  # 42

一名测试工作者,专注接口测试、自动化测试、性能测试、Python技术。