当前位置: 首页 > news >正文

Python 面向对象高级编程-定制类

目录

__str__

__iter__

__getitem__

__getattr__

__call__

小结


看到类似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的。

__slots__我们已经知道怎么用了,__len__()方法我们也知道是为了能让class作用于len()函数。

除此之外,Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类。

__str__

我们先定义一个Student类,打印一个实例:

>>> class Student(object):
...     def __init__(self, name):
...         self.name = name
...
>>> print(Student('Michael'))
<__main__.Student object at 0x109afb190>

打印出一堆<__main__.Student object at 0x109afb190>,不好看。

怎么才能打印得好看呢?只需要定义好__str__()方法,返回一个好看的字符串就可以了:

>>> class Student(object):
...     def __init__(self, name):
...         self.name = name
...     def __str__(self):
...         return 'Student object (name: %s)' % self.name
...
>>> print(Student('Michael'))
Student object (name: Michael)

这样打印出来的实例,不但好看,而且容易看出实例内部重要的数据。

但是细心的朋友会发现直接敲变量不用print,打印出来的实例还是不好看:

>>> s = Student('Michael')
>>> s
<__main__.Student object at 0x109afb310>

这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。 

解决办法是再定义一个__repr__()。但是通常__str__()__repr__()代码都是一样的,所以,有个偷懒的写法:

class Student(object):def __init__(self, name):self.name = namedef __str__(self):return 'Student object (name=%s)' % self.name__repr__ = __str__

__iter__

如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:

class Fib(object):def __init__(self):self.a, self.b = 0, 1 # 初始化两个计数器a,bdef __iter__(self):return self # 实例本身就是迭代对象,故返回自己def __next__(self):self.a, self.b = self.b, self.a + self.b # 计算下一个值if self.a > 100000: # 退出循环的条件raise StopIteration()return self.a # 返回下一个值

现在,试试把Fib实例作用于for循环: 

>>> for n in Fib():
...     print(n)
...
1
1
2
3
5
...
46368
75025

__getitem__

Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:

>>> Fib()[5]
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: 'Fib' object does not support indexing

现在,就可以按下标访问数列的任意一项了:

>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101

但是list有个神奇的切片方法:

>>> list(range(100))[5:10]
[5, 6, 7, 8, 9]

对于Fib却报错。原因是__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:

class Fib(object):def __getitem__(self, n):if isinstance(n, int): # n是索引a, b = 1, 1for x in range(n):a, b = b, a + breturn aif isinstance(n, slice): # n是切片start = n.startstop = n.stopif start is None:start = 0a, b = 1, 1L = []for x in range(stop):if x >= start:L.append(a)a, b = b, a + breturn L

现在试试Fib的切片:

>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

但是没有对step参数作处理:

>>> f[:10:2]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

也没有对负数作处理,所以,要正确实现一个__getitem__()还是有很多工作要做的。

此外,如果把对象看成dict__getitem__()的参数也可能是一个可以作key的object,例如str

与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

__getattr__

正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。比如定义Student类:

class Student(object):def __init__(self):self.name = 'Michael'

调用name属性,没问题,但是,调用不存在的score属性,就有问题了:

>>> s = Student()
>>> print(s.name)
Michael
>>> print(s.score)
Traceback (most recent call last):...
AttributeError: 'Student' object has no attribute 'score'

错误信息很清楚地告诉我们,没有找到score这个attribute。

要避免这个错误,除了可以加上一个score属性外,Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。修改如下:

class Student(object):def __init__(self):self.name = 'Michael'def __getattr__(self, attr):if attr=='score':return 99

当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值:

>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99

返回函数也是完全可以的:

class Student(object):def __getattr__(self, attr):if attr=='age':return lambda: 25

只是调用方式要变为:

>>> s.age()
25

注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找。

此外,注意到任意调用如s.abc都会返回None,这是因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误:

class Student(object):def __getattr__(self, attr):if attr=='age':return lambda: 25raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)

这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段。

这种完全动态调用的特性有什么实际作用呢?作用就是,可以针对完全动态的情况作调用。

举个例子:

现在很多网站都搞REST API,比如新浪微博、豆瓣啥的,调用API的URL类似:

  • http://api.server/user/friends
  • http://api.server/user/timeline/list

如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。

利用完全动态的__getattr__,我们可以写出一个链式调用:

class Chain(object):def __init__(self, path=''):self._path = pathdef __getattr__(self, path):return Chain('%s/%s' % (self._path, path))def __str__(self):return self._path__repr__ = __str__

试试:

>>> Chain().status.user.timeline.list
'/status/user/timeline/list'

这样,无论API怎么变,SDK都可以根据URL实现完全动态的调用,而且,不随API的增加而改变!

还有些REST API会把参数放到URL中,比如GitHub的API:

GET /users/:user/repos

调用时,需要把:user替换为实际用户名。如果我们能写出这样的链式调用:

Chain().users('michael').repos

就可以非常方便地调用API了。有兴趣的童鞋可以试试写出来。

__call__

一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用。能不能直接在实例本身上调用呢?在Python中,答案是肯定的。

任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。请看示例:

class Student(object):def __init__(self, name):self.name = namedef __call__(self):print('My name is %s.' % self.name)

调用方式如下:

>>> s = Student('Michael')
>>> s() # self参数不要传入
My name is Michael.

__call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

如果你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。

那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例:

>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('str')
False

通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

小结

Python的class允许定义许多定制方法,可以让我们非常方便地生成特定的类。

本节介绍的是最常用的几个定制方法,还有很多可定制的方法,请参考Python的官方文档。

相关文章:

Python 面向对象高级编程-定制类

目录 __str__ __iter__ __getitem__ __getattr__ __call__ 小结 看到类似__slots__这种形如__xxx__的变量或者函数名就要注意&#xff0c;这些在Python中是有特殊用途的。 __slots__我们已经知道怎么用了&#xff0c;__len__()方法我们也知道是为了能让class作用于len()…...

qt creator示例空白

通常情况下&#xff0c;进入qt后&#xff0c;就会弹出以下窗口&#xff1a; 但如果出现示例空白&#xff0c;那可能是因为 Qt Creator 无法正确识别 Qt 的安装路径或配置。 解决&#xff1a; 点击“添加”&#xff1a; 然后跳转到你的qmake.exe的目录&#xff0c;例如我的qmak…...

MyBatis-Plus 与 Spring Boot 的最佳实践

在现代 Java 开发中,MyBatis-Plus 和 Spring Boot 的结合已经成为了一种非常流行的技术栈。MyBatis-Plus 是 MyBatis 的增强工具,提供了许多便捷的功能,而 Spring Boot 则简化了 Spring 应用的开发流程。本文将探讨如何将 MyBatis-Plus 与 Spring Boot 进行整合,并分享一些…...

TDengine 中的标签索引

简介 本节说明 TDengine 的索引机制。在 TDengine 3.0.3.0 版本之前&#xff08;不含&#xff09;&#xff0c;默认在第一列 TAG 上建立索引&#xff0c;但不支持给其它列动态添加索引。从 3.0.3.0 版本开始&#xff0c;可以动态地为其它 TAG 列添加索引。对于第一个 TAG 列上…...

工业自动化核心:BM100 信号隔离器的强大力量

安科瑞 吕梦怡 18706162527 BM100系列信号隔离器可以对电流、电压等电量参数或温度、电阻等非电量参数进行快速精确测量&#xff0c;经隔 离转换成标准的模拟信号输出。既可以直接与指针表、数显表相接&#xff0c;也可以与自控仪表&#xff08;如PLC&#xff09;、各种 A/D …...

Ascend开发板镜像烧录、联网、其他设备访问

Ascend开发板镜像烧录、联网、外部访问 1.1 Ascend开发板制卡方式一&#xff1a;镜像烧录 SD卡插入读卡器&#xff0c;读卡器插入PC的USB接口 烧录镜像前&#xff0c;先格式化一下SD卡 参考教程&#xff1a;格式化SD卡、修复烧写系统失败的SD卡 WinR&#xff0c;输入cmd DIS…...

Llama-Factory框架下的Meta-Llama-3-8B-Instruct模型微调

目录 引言 Llama - Factory 训练框架简介&#xff1a; Meta - Llama - 3 - 8B - Instruct 模型概述&#xff1a; Lora 方法原理及优势&#xff1a; 原理 优势 环境准备: 部署环境测试&#xff1a; 数据准备&#xff1a; 模型准备&#xff1a; 模型配置与训练&#xff1…...

MySQL进阶-分析查询语句EXPLAIN

概述 能做什么&#xff1f; 表的读取顺序 数据读取操作的操作类型 哪些索引可以使用 哪些索引被实际使用 表之间的引用 每张表有多少行被优化器查询 官网介绍 https://dev.mysql.com/doc/refman/5.7/en/explain-output.html https://dev.mysql.com/doc/refman/8.0/…...

Python 高级编程与实战:构建数据可视化应用

在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧、数据科学、机器学习、Web 开发、API 设计、网络编程、异步IO、并发编程、设计模式与软件架构、性能优化与调试技巧、分布式系统、微服务架构、自动化测试框架以及 RESTf…...

学习threejs,Animation、Core、CustomBlendingEquation、Renderer常量汇总

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️Animation常量汇总1.1.1 循…...

Java直通车系列14【Spring MVC】(深入学习 Controller 编写)

目录 基本概念 编写 Controller 的步骤和要点 1. 定义 Controller 类 2. 映射请求 3. 处理请求参数 4. 调用业务逻辑 5. 返回响应 场景示例 1. 简单的 Hello World 示例 2. 处理路径变量和请求参数 3. 处理表单提交 4. 处理 JSON 数据 5. 异常处理 基本概念 Cont…...

【蓝桥杯集训·每日一题2025】 AcWing 5539. 牛奶交换 python

AcWing 5539. 牛奶交换 Week 3 3月6日 题目描述 农夫约翰的 N N N 头奶牛排成一圈&#xff0c;使得对于 1 , 2 , … , N − 1 1,2,…,N−1 1,2,…,N−1 中的每个 i i i&#xff0c;奶牛 i i i 右边的奶牛是奶牛 i 1 i1 i1&#xff0c;而奶牛 N N N 右边的奶牛是奶牛 …...

Mybatis缓存机制(一级缓存和二级缓存)

前言 为什么要学习Mybatis 缓存机制&#xff1f; 学习Mybatis 缓存机制&#xff0c;可以有效解决 数据库的压力&#xff0c;提高数据库的性能。 例如&#xff1a;你要 对tb_user 表 &#xff0c;查询 所有用户的信息,并且多次查询所有用户信息。我们知道第一次查询表信息流…...

设计模式--单例模式

一、单例模式代码实现 public class DatabaseConnection {// 1. 私有静态实例变量private static DatabaseConnection instance;// 2. 私有构造函数&#xff0c;防止外部直接创建实例private DatabaseConnection() {// 初始化数据库连接System.out.println("Database con…...

ubuntu22.04本地部署OpenWebUI

一、简介 Open WebUI 是一个可扩展、功能丰富且用户友好的自托管 AI 平台&#xff0c;旨在完全离线运行。它支持各种 LLM 运行器&#xff0c;如 Ollama 和 OpenAI 兼容的 API&#xff0c;并内置了 RAG 推理引擎&#xff0c;使其成为强大的 AI 部署解决方案。 二、安装 方法 …...

2025-3-7二叉树的线索化

一、中序线索化 代码其实就是和中序遍历相似&#xff0c;增加了两个标志位 ltag rtag。 完整的代码&#xff1a; 二、先序线索化&#xff1a; 三、后序线索化&#xff1a; 总结&#xff1a;其核心其实还是遍历算法的改造。 并且注意处理最后一个被访问的节点。...

以商业思维框架为帆,驭创业浪潮前行

创业者踏入商海&#xff0c;如同航海家奔赴未知海域&#xff0c;需有清晰的思维罗盘指引方向。图中“为什么—用什么—怎么做—何人做—投入产出”的商业框架&#xff0c;正是创业者破解商业谜题的密钥&#xff0c;从需求洞察到落地执行&#xff0c;为创业之路铺就逻辑基石。 …...

海思Hi3516DV300交叉编译opencv

OpenCV是一个开源的跨平台计算机视觉库&#xff0c;支持C、Python等多种语言&#xff0c;适用于图像处理、目标检测、机器学习等任务。其核心由C编写&#xff0c;高效轻量&#xff0c;提供实时视觉处理功能&#xff0c;广泛应用于工业自动化、医疗影像等领域。 1 环境准备 1…...

基于NIST后量子算法的混合加密系统

目录 基于NIST后量子算法的混合加密系统一、前言二、后量子密码学概述2.1 后量子密码学的背景2.2 NIST候选后量子算法 三、混合加密系统的设计原理3.1 混合加密的基本思想3.2 数学公式与证明3.3 混合加密系统的优势 四、工程实现与优化策略4.1 算法层面优化4.2 工程实现优化 五…...

uni-app 开发ios 使用testFlight 进行分发测试

一、生成ipa 首先你要生成一个ipa包,怎么生成这个包,可以在uniapp打包安卓和iOS包 二、上传到分发平台 在这里我使用的是Transporter ,当然你也可以看下其他分发平台 在mac电脑app store中下载Transporter,双击打开, 点击添加,将打包好的ipa文件放上去,注意打包的时…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...