20210507

相当于函数的默认调用()?

可以调用的对象

关于 __call__ 方法,不得不先提到一个概念,就是可调用对象(callable),我们平时自定义的函数、内置函数和类都属于可调用对象,但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象,判断对象是否为可调用对象可以用函数 callable

如果在类中实现了 __call__ 方法,那么实例对象也将成为一个可调用对象,

你也许已经知道,在Python中,方法也是一种高等的对象。这意味着他们也可以被传递到方法中就像其他对象一样。这是一个非常惊人的特性。 在Python中,一个特殊的魔术方法可以让类的实例的行为表现的像函数一样,你可以调用他们,将一个函数当做一个参数传到另外一个函数中等等。这是一个非常强大的特性让Python编程更加舒适甜美。 __call__(self, [args...])

允许一个类的实例像函数一样被调用。实质上说,这意味着 x() 与 x.__call__() 是相同的。注意 __call__ 参数可变。这意味着你可以定义 __call__ 为其他你想要的函数,无论有多少个参数。

__call__ 在那些类的实例经常改变状态的时候会非常有效。调用这个实例是一种改变这个对象状态的直接和优雅的做法。用一个实例来表达最好不过了:


class Entity:
'''调用实体来改变实体的位置。'''def __init__(self, size, x, y):self.x, self.y = x, yself.size = sizedef __call__(self, x, y):'''改变实体的位置'''self.x, self.y = x, ye = Entity(1, 2, 3) // 创建实例
e(4, 5) //实例可以象函数那样执行,并传入x y值,修改对象的x y

实例 --- Flask Wtform

在wtform的validators就使用了这个特性
wtform 定义字段的时候可以为每个字段添加校验器, 而每个校验器的特点都是接受两个参数,form, field。
所以我们可以自己自定义校验器,校验器是可调用对象即可,即函数,对象方法,都可以的。
比如function url_validate(form, field)

使用函数定制

下面提供一个my_length_check()函数,用于验证name长达是否长于50个字符。
这个函数按照规定接受两个参数,form, field,然后就可以根据两个参数进行判断。
这样做是可以的,但是问题是:如果我想自定义错误信息怎么办?而且我想限制的数量也能控制,不是写死, 还有如果有最小值,和最大值呢? 这里只能固定接受两个参数,不能再传入自定参数,没得更多的自定义了,这样就会限制住了。


def my_length_check(form, field):if len(field.data) > 50:raise ValidationError('Field must be less than 50 characters')class MyForm(Form):name = StringField('Name', [InputRequired(), my_length_check])

使用闭包和工厂模式

为了解决上面能传入更多自定义参数, 更灵活定制校验,我们进行改造。
使用工厂模式,一个能生产校验器的工厂,而这个工厂能接受其他更灵活的参数,看例子:


def length(min=-1, max=-1, message=None):if not message:message = 'Must be between %d and %d characters long.' % (min, max)def _length(form, field):l = field.data and len(field.data) or 0if l < min or max != -1 and l > max:raise ValidationError(message)return _lengthclass MyForm(Form):name = StringField('Name', [InputRequired(), length(max=50)])

这里涉及两个概念:

  • 工厂模式
    这个例子的length()是一个工厂,他的工作就是执行的时候,生产一个validator校验器,每次调用,传入不同参数,就会生产不同的校验器,所以叫做工厂模式。

  • 闭包
    同时它也是个闭包, 为什么是个闭包?正常情况下执行完length() min, max, message参数就会被回收,不在存在,但是为什么_length()能继续读取min, max, message变量,就是闭包的威力。闭包使得局部变量在函数外被访问成为可能。
    这里的 length() 就是一个闭包,闭包本质上是一个函数,它有两部分组成,_length 函数和变量 min, max, message。闭包使得这些变量的值始终保存在内存中。
    参考:一步一步教你认识Python闭包

这个length(min, max, message) 函数,调用的时候,传入了更多的参数来灵活决定校验器,因为他里面就是返回一个_length的校验器,这个校验器还是遵循规则,只接受form, field 参数, 但是在length() 这个外层却能接受更多参数,而这些参数也能被内层的_length()所使用。


class MyForm(Form):name = StringField('Name', [InputRequired(), length(max=50)])

这里的length(max=5)就是返回了个_length函数,wtform调用校验器的时候实际上就是这样调用的:

length(max=50)(form, field)

使用类方法__call__实现


class Length(object):def __init__(self, min=-1, max=-1, message=None):self.min = minself.max = maxif not message:message = u'Field must be between %i and %i characters long.' % (min, max)self.message = messagedef __call__(self, form, field):l = field.data and len(field.data) or 0if l < self.min or self.max != -1 and l > self.max:raise ValidationError(self.message)class MyForm(Form):name = StringField('Name', [InputRequired(), Length(max=50)])

这次我们使用类来实现,首先实例化这个校验器Length(max=50), 这时返回的是对象实例,然后我们定义了方法__call__方法,说明实例对象是可以调用的, 最后的结果就是Length(max=50)(form, field)。
这样实现方法是也是很妙的。
说到这里,类实现方法跟闭包很像,都是把变量封装起来,让真正的校验器能读取到参数。

闭包避免了使用全局变量,此外,闭包允许将函数与其所操作的某些数据(环境)关连起来。这一点与面向对象编程是非常类似的,在面对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。

__call__其他作用

实例对象也可以像函数一样作为可调用对象来用,那么,这个特点在什么场景用得上呢?这个要结合类的特性来说,类可以记录数据(属性),而函数不行(闭包某种意义上也可行),利用这种特性可以实现基于类的装饰器,在类里面记录状态,比如,下面这个例子用于记录函数被调用的次数:


class Counter:def __init__(self, func):self.func = funcself.count = 0def __call__(self, *args, **kwargs):self.count += 1return self.func(*args, **kwargs)@Counter
def foo():passfor i in range(10):foo()print(foo.count)  # 10

首先这里的@Counter是装饰器,执行起来顺序是 foo = Counter(foo), 实例化,把foo函数传到类Counter里面,并存到对象属性里面,然后返回foo = Counter实例。 这时foo已经是Counter实例,而不是本身foo函数。
当执行foo()的时候,其实已经变成了,执行__call__函数,而这个函数里面是执行了本身的self.func 即foo的实际逻辑, 而且加上了计算调用次数。这样就记录状态了。
太厉害了,这样的实现方式。

https://wtforms.readthedocs.io/en/stable/validators.html#custom-validators
https://stackoverflow.com/questions/9663562/what-is-the-difference-between-init-and-call-in-python
一步一步教你认识Python闭包
简述 initnewcall 方法

作者:大富帅
链接:https://www.jianshu.com/p/e1d95c4e1697
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

Python __call__详解相关推荐

  1. python区块链开发_Fabric区块链Python开发详解

    Hyperledger Fabric是最流行的联盟区块链平台.Fabric区块链Python开发详解课程 涵盖Fabric区块链的核心概念.Fabric网络搭建.Node链码开发.Python应用开发 ...

  2. python装饰器setter_第7.27节 Python案例详解: @property装饰器定义属性访问方法getter、setter、deleter...

    上节详细介绍了利用@property装饰器定义属性的语法,本节通过具体案例来进一步说明. 一.    案例说明 本节的案例是定义Rectangle(长方形)类,为了说明问题,除构造函数外,其他方法都只 ...

  3. 【python】详解类class的继承、__init__初始化、super方法

    原文链接; https://blog.csdn.net/brucewong0516/article/details/79121179?utm_medium=distribute.pc_relevant ...

  4. python与golang_Golang与python线程详解及简单实例

    Golang与python线程详解及简单实例 在GO中,开启15个线程,每个线程把全局变量遍历增加100000次,因此预测结果是 15*100000=1500000. var sum int var ...

  5. python 最小二乘法_最小二乘法及其python实现详解

    最小二乘法Least Square Method,做为分类回归算法的基础,有着悠久的历史(由马里·勒让德于1806年提出).它通过最小化误差的平方和寻找数据的最佳函数匹配.利用最小二乘法可以简便地求得 ...

  6. 【python】详解multiprocessing多进程-Pool进程池模块(二)

    [python]详解multiprocessing多进程-process模块(一) [python]详解multiprocessing多进程-Pool进程池模块(二) [python]详解multip ...

  7. 【python】什么是序列,Python序列详解

    什么是序列,Python序列详解 概述 序列索引 序列切片 序列相加 序列相乘 检查元素是否包含在序列中 序列相关的内置函数 range 快速初始化数字列表 概述 所谓序列,指的是一块可存放多个值的连 ...

  8. python多线程详解 Python 垃圾回收机制

    文章目录 python多线程详解 一.线程介绍 什么是线程 为什么要使用多线程 总结起来,使用多线程编程具有如下几个优点: 二.线程实现 自定义线程 守护线程 主线程等待子线程结束 多线程共享全局变量 ...

  9. Python线程详解

    Python线程详解 线程简介 开启多线程 线程之间共享 GIL全局解释器锁 线程间通信 线程简介 线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元. ...

最新文章

  1. js 地址的封装以及地址栏的参数获取
  2. python下使用epoll
  3. html弹窗超链接,点出超链接弹出一个小窗口
  4. 关于C#中实现两个应用程序消息通讯的问题
  5. web前端 如何入门人工智能算法
  6. NOI数据结构:最小树形图
  7. 组件注册@ComponentScan的自动扫描和指定扫描规则
  8. dubbo优势_Dubbo的作用和特点
  9. nagios监控详解
  10. 算法实践:波兰表达式
  11. 无线路由如何快速设置WDS扩展网络
  12. 电机qudong电路
  13. 移动开发技术(Android)——综合实验
  14. 家庭光纤宽带有必要升级千兆双频路由器吗?
  15. VS Code 网易云音乐插件 没有声音 无法播放的解决办法
  16. 综述 | 图像计算传感器
  17. 对接微信二维码支付流程
  18. 搜狗拼音输入法7.2c正式版(支持win8)_去广告优化版
  19. 服务器整体爆率如何修改,传奇私服如何调整爆率
  20. 职工工资管理系统php,员工工资管理系统源代码.doc

热门文章

  1. 2022-2028年中国抗盐粘土行业发展现状调查及前景战略分析报告
  2. 2022-2028年中国饮水机市场投资分析及前景预测报告
  3. 第五周周记(国庆第七天)
  4. LeetCode简单题之按奇偶排序数组 II
  5. 你想了解的Cookie和Session就在这~
  6. DPU(Data Processing Unit)数据处理器
  7. 将TVM集成到PyTorch上
  8. HarmonyOS系统概述
  9. 推理芯片的性能建立在优化的存储子系统设计上
  10. 激光雷达和毫米波雷达