Python学习(六)
魔法方法
在python中,有一些内置好的特定的方法,这些方法在进行特定的操作时会自动被调用,称之为魔法方法。
构造和析构
魔法方法总是被双下横线包围,例如__init__;
是面向对象的Python的一切;
他们总是能在适当的时候被调用。
__init__(self[, …])
相当于其他编程语言的构造方法,类在实例化对象的时候首先会调用的一个方法。
__new__(cls[, …])
init并不是实例化对象调用的第一个方法,new方法才是,它的第一个参数是cls,通常情况下是返回cls类的对象,也可以返回其他类的对象。
new方法是极少去重写它的,Python会默认执行,但是当需要继承一个不可变类型又需要修改的时候,那么就需要重写了。
__del__(self)
当对象将要被销毁的时候这个方法会自动被调用,但是
del x
并不等于调用了
x.__del__()
del方法是当垃圾回收机制,即当没有任何变量去引用这个对象的时候,垃圾回收机制会自动销毁,这时才会调用对象的self方法。
注意:内置的__del__()方法并不是发生del操作的时候就会调用,当对象生成后,所有对它的引用都被del后才会启动垃圾回收机制,才会调用__del__()方法。
算数运算
在Python2.2之前类和类型是分开的,类是属性和方法的封装,类型是如整型、浮点型、字符串这些类型,但是在Python2.2后,试图对两者进行统一,做法就是将int、float、string、list、tuple这些内置函数通常转化为工厂函数。
- 什么是工厂函数
参考
原因: 当a+b,识别到加法会先调用前者a的add,返回self+other,即返回了a+b,就又运行了加法add,进入了无限递归。
所以在实现的时候,一定要注意避免出现无限递归的情况。
稍作修改
- 一些算数运算
方法 | 相应二元算数运算符 |
---|---|
__add__(self, other) | 定义加法的行为:+ |
__sub__(self, other) | 定义减法的行为:- |
__mul__(self, other) | 定义乘法的行为:* |
__truediv__(self, other) | 定义真除法的行为:/ |
__floordiv__(self, other) | 定义整数除法的行为:// |
__mod__(self, other) | 定义取模算法的行为:% |
__divmod__(self, other) | 定义当被divmod()调用时的行为【divmod(a, b)返回值是一个元组(a//b, a%b)】 |
__pow__(self, other[, modulo]) | 定义当被pow()调用或**运算时的行为 |
__lshift__(self, other) | 定义按位左移位的行为:<< |
__rshift__(self, other) | 定义按位右移位的行为:>> |
__and__(self, other) | 定义按位与操作的行为:& |
__xor__(self,other) | 定义按位异或操作的行为:^ |
__or__(self, other) | 定义按位或操作的行为:| |
通过对指定魔法方法的重写,可以让Python根据自己的意图来实现程序
魔法方法参考
- 反运算
这里是3-1,并不是1-3,如果想让1-3,那么就要互换int.sub()中self, other的位置,举例说明
定制一个简单的类
- 需要的资源
(1)使用time模块的localtime方法获取时间
time模块详解
(2)time.localtime返回struct_time的时间格式
(3)表现你的类:重写__str__和__repr__
- 程序MyTimer
import time as tclass MyTimer():def __str__(self):return self.prompt__repr__ = __str__# 开始计时def start(self):self.start = t.localtime()print("计时开始...")# 停止计时def stop(self):self.stop = t.localtime()self._calc()print("计时结束...")# 内部方法,计算运行时间def _calc(self):self.lasted = []self.prompt = "总共运行了"for index in range(6):self.lasted.append(self.stop[index] - self.start[index])self.prompt += str(self.lasted[index])
但是这里有一个问题就是,如果定以后直接调用,就会报错
因为这时,prompt还没有被定义,这时就需要所有属于实例对象的变量先在init中定义
import time as tclass MyTimer():# 添加init定义def __init__(self):self.prompt = "未开始计时!"self.lasted = []self.start = 0self.stop = 0def __str__(self):return self.prompt__repr__ = __str__# 开始计时def start(self):self.start = t.localtime()print("计时开始...")# 停止计时def stop(self):self.stop = t.localtime()self._calc()print("计时结束...")# 内部方法,计算运行时间def _calc(self):self.lasted = []self.prompt = "总共运行了"for index in range(6):self.lasted.append(self.stop[index] - self.start[index])self.prompt += str(self.lasted[index])
这时在执行虽然不会直接调用t1出错了,但是运行起来却又出现“整型不能被调用”问题,这里是由于在init中将self.start定义为0导致,因为类的方法名和属性名一样时,属性会覆盖方法,这里就认为start是属性。
这时只需要将start和stop改一下名字即可
并且在这里改变一下显示的方式和累加计时
import time as tclass MyTimer():def __init__(self):self.unit = ['年', '月', '天', '小时', '分钟', '秒']self.prompt = "未开始计时!"self.lasted = []self.begin = 0self.end = 0def __str__(self):return self.prompt__repr__ = __str__def __add__(self, other):prompt = "总共运行了"result = []for index in range(6):result.append(self.lasted[index] + other.lasted[index])if result[index]:prompt += (str(result[index]) + self.unit[index])return prompt# 开始计时def start(self):self.begin = t.localtime()self.prompt = "提示:请先调用 stop() 停止计时!"print("计时开始...")# 停止计时def stop(self):if not self.begin:print("提示:请先调用start()进行计时!")else:self.end = t.localtime()self._calc()print("计时结束...")# 内部方法,计算运行时间def _calc(self):self.lasted = []self.prompt = "总共运行了"for index in range(6):self.lasted.append(self.end[index] - self.begin [index])if self.lasted[index]: # 为0不显示 self.prompt += str(self.lasted[index]) + self.unit[index]# 为下一轮计时初始化变量self.begin = 0self.end = 0
- 代码存在的问题
(1)生成的时间会存在负数的情况
(2)精度不够,只能到秒
属性访问
(1)直接访问属性
(2)通过getattr()访问
(3)利用property(),以属性的方式访问属性
- __getattr__(self, name)
定义当用户试图获取一个不存在的属性时的行为 - __getattribute__(self, name)
定义当该类的属性被访问时的行为 - __setattr__(self, name, value)
定义当一个属性被设置时的行为 - __delattr__(self, name)
定义当一个属性被删除时的行为
>>> class C:def __getattribute__(self, name):print("getattribute")return super().__getattribute__(name)def __getattr__(self, name):print("getattr")def __setattr__(self, name, value):print("setattr")super().__setattr__(name, value)def __delattr__(self, name):print("delattr")super().__delattr__(name)
- 练习
class Rectangle:def __init__(self, width=0, height=0):self.width = widthself.height = heightdef __setattr__(self, name, value):if name == 'square':self.width = valueself.height = valueelse:self.name = valuedef getArea(self): # 获得面积return self.width * self.height
# 输入 r1 = Rectangle(4, 5)
# 这样写会出现一个无限递归,因为执行__init__中的self.width和self.height赋值语句,会触发__setattr__中的else后的语句self.name = value,再重复调用__setattr__,这样就会无限递归下去
# =====================================================================================================
# 下面进行改进
class Rectangle:def __init__(self, width=0, height=0):self.width = widthself.height = heightdef __setattr__(self, name, value):if name == 'square':self.width = valueself.height = valueelse:super().__setattr__(name, value)def getArea(self): # 获得面积return self.width * self.height
另一种改进方法就是给一个特殊属性dict,dict是以字典的形式显示出当前对象的所有属性以及对应的值
class Rectangle:def __init__(self, width=0, height=0):self.width = widthself.height = heightdef __setattr__(self, name, value):if name == 'square':self.width = valueself.height = valueelse:self.__dict__[name] = valluedef getArea(self): # 获得面积return self.width * self.height
描述符
描述符就是将某种特殊类型的类的实例指派给另一个类的属性。
__get__(self, instance, owner)
# 用于访问属性,返回属性的值
__set__(self, instane, value)
# 将在属性分配操作中调用,不返回任何内容
__delete__(self, instance)
# 控制删除操作,不返回任何内容
- 实例
class MyDescriptordef __get__(self, instance, owner):print("getting...", self, instance, owner)def __set__(self, instance, value):print("setting...", self, instance, value)def __delete__(self, instnce):print("deleting...", self, instance)class Test:x = MyDescriptor()
【ps:这里改用spyder编辑了,输入处显示方式改变,实际操作同python idle相同】
这里就是将某种特殊类型的类(MyDescriptor)的实例(MyDescriptor())指派给另一个类(Test)的属性(x),就说明MyDescriptor就是x的描述符。
实例化对象后,用text.x强制打印
可以看到打印出三个参数,第一个是self的参数描述符类MyDescriptor本身的实例,第二个是instance的参数类的拥有者Test的实例test,第三个就是拥有者类Test本身
验证一下
对实例化对象进行赋值,出现赋值调用set的特殊方法,打印self、instance和value
del同理,打印self和instance
- 定义一个MyProperty
之前提到的property其实就是一个描述符
class MyProperty:def __init__(self, fget=None, fset=None, fdel=None):self.fget = fgetself.fset = fsetself.fdel = fdeldef __get__(self, instance, value):return self.fget(instance)def __set__(self, instance, value):self.fset(instance, value)def __delete__(self, instance):self.fdel(instance)class C:def __init__(self):self._x = Nonedef getX(self):return self._xdef setX(self, value):self._x = valuedef delX(self):del self._xx = MyProperty(getX, setX, delX)
同样的这里将MyProperty的实例MyProperty()指派给类C的属性x,对类C的实例对象c的x属性赋值,调用setX返回c._x再进行操作。
- 练习
class Celsius:def __init__(self, value = 26.0):self.value = float(value)def __get__(self, instance, owner):return self.valuedef __set__(self, instance, value):self.value = float(value)class Fahrenheit: def __get__(self, instance, owner):return instance.cel * 1.8 +32def __set__(self, instance, value):instance.cel = (float(value) - 32) / 1.8class Temperature:cel = Celsius()fah = Fahrenheit()
定制容器
- 协议
协议Protocols相似于接口,规定了必须要定义的方法。而在Python中协议更像是一种指南。 - 容器类型的协议
(1)定制不可变容器
只需定义__len__()和__getitem__()方法
(2)定制可变容器
除__len__()和__getitem__()方法外,还需定义__setitem__()和__delitem__()两个方法
Python魔法方法详解
- 练习
编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数。
class CountList:def __init__(self, *args): # 星号代表参数是可变数量的self.values = [x for x in args] # 依次取列表中元素self.count = {}.fromkeys(range(len(self.values)),0)# fromkeys 用于创建一个新字典def __len__(self):return len(self.values)def __getitem__(self, key):self.count[key] += 1return self.values[key]
迭代器
提供迭代方法的容器称之为迭代器。
通常的迭代器有序列、列表、元组、字符串、字典、文件。
- for语句迭代
- 字典迭代
- 关于迭代操作,Python提供了两个BIF内置函数inter()和next()
inter() 即iteration
对于容器对象调用iter就得到它的迭代器,调用next()就会返回下一个值,当迭代器没有值可以返回了,Python就会抛出一个“StopIteration”的异常,此时迭代结束。
这样就可以知道for语句是如何执行的
利用while语句来模拟for语句的执行
- 迭代器的魔法方法
# 两个魔法方法分别对应两个BIF容器的实现
iter()
--> __iter__() # 返回迭代器本身
next()
--> __next__() # 决定迭代器的迭代规则
- 实现斐波那契数列的打印
class Fibs:def __init__(self, n=10):self.a = 0self.b = 1self.n = ndef __iter__(self):return selfdef __next__(self):self.a, self.b = self.b, self.a + self.bif self.a > self.n:raise StopIterationreturn self.a
生成器
生成器并不涉及魔法方法、类和对对象,只通过普通的函数实现。生成器实际上是迭代器的一种实现。
生成器延续了Python简洁的特点,并且使协同程序的概念得以实现,协同程序就是可以运行的独立函数调用,函数可以暂停或挂起,并在需要的时候从程序离开的地方继续或者重新开始。
Generator 实例
def myGen():print("生成器被执行!")yield 1yield 2# 一旦一个函数中出现yield语句# 那么就说明这个函数被定义为生成器# yield就相当于普通函数中的return# 和return的区别:# 出现yield,就将yield后的参数返回,并暂停在yield处
- 实现斐波那契数列
def libs():a = 0b = 1while True:a, b = b, a + byield a # 由于有yield,所以while True不会变成死循环
- 推导式
(1)列表推导式
(2)字典推导式
有“:”的是字典,没有的是集合
(3)集合推导式
(4)没有字符串推导式
(5)元组(tuple)推导式
打印元组e发现e是一个生成器推导式
生成器推导式如果作为函数的参数,是可以直接写推导式,不需要加括号
【100以内不能被2整除的整数和】
[扩展阅读] 提高你的 Python:解释 yield 和 Generators(生成器)
Python学习(六)相关推荐
- Python学习六:面向对象编程(上)
文章目录 前言 一.面向对象编程: 1. oop [object oriented programming] 是一种python的编程思路 2. 解释 3. 面向对象 和面向对象编程 二.类和对象 1 ...
- python学习六:数据结构
数据结构 列表 将列表当做堆栈使用 列表方法使得列表可以很方便的作为一个堆栈来使用,堆栈作为特定的数据结构,最先进入的元素最后一个被释放(后进先出).用 append() 方法可以把一个元素添加到堆栈 ...
- Python学习(六) Python数据类型:字典(重要)
字典dict: 字典其实就相当于java里面的Map,用来存储键值对的.其中存储的数据时无序的. 假如有这样的数据: t1=['name','age','sex'] t2=['tom',30,'mal ...
- Python学习系列(六)(模块)
Python学习系列(六)(模块) Python学习系列(五)(文件操作及其字典) 一,模块的基本介绍 1,import引入其他标准模块 标准库:Python标准安装包里的模块. 引入模块的几种方式: ...
- 孤荷凌寒自学python第六十三天学习mongoDB的基本操作并进行简单封装2
孤荷凌寒自学python第六十三天学习mongoDB的基本操作并进行简单封装2 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第九天. 今天继续学习mongoDB的简单操作, ...
- Python学习笔记六——画小猪佩奇
目录 Python学习笔记六--画小猪佩奇 画布 画笔 属性设置 操纵命令 运动命令 画笔控制命令 全局控制命令 其他命令 Python学习笔记六--画小猪佩奇 使用Python的turtle库可以绘 ...
- python学习笔记(六)字典
python学习笔记(六)字典 1.字典初见 python中的字典实际上就是一系列的"键-值"对(key-value),它们之间时无序的,每一个键都必须有与之对应的值,这个值可以是 ...
- Python 学习第六讲作业2020-12-28
Python 学习第六讲作业 lis1 =["name", "author", "introduce"] lis2 =["NOWE ...
- Python学习第六课-列表
Python学习第六课-列表 一.序列 1.1 概念 1.2分类 二.列表 2.1 概念 2.2 练习 三.切片 3.1 可切片对象的索引方式 3.2切片操作 3.3 练习 四.通用操作 4.1 序列 ...
- Python学习笔记(六)
1. IO编程 1.1 文件读写 1.2 StringIO和BytesIO 1.3 操作文件和目录 1.4 序列化 2. 进程和线程 2.1 多进程 2.2 多线程 2.3 ThreadLocal 2 ...
最新文章
- mysql中如何设置时区_如何设置MySQL的时区?
- @Component、@Repository、@Service、@Controller区别
- java 回调模式_总结!!!总结!!!java回调以及future模式
- 利用Phtoshop去掉图片中的线性渐变背景
- SpringMVC处理模型数据
- 管理Win2003sp1防火墙的一点小技巧
- CakePHP 2.x十分钟博客教程
- IIS6.0服务器架站无法访问解决方案总结
- 一个设置容器和网格布局的小技巧
- tongweb自动部署_将web应用迁到TongWeb
- Android之复合按钮CompoundButton
- Nodejs页面访问加载静态资源
- java8实现map遍历,map转list,list转map
- 迈达斯导出html计算书,MIDAS计算书整理正文..doc
- version magic 不一致问题
- TotalCommander常用操作
- 苹果台式电脑怎么使用计算机,MAC电脑连接台式电脑显示器怎么操作
- OUTLOOK 邮箱发件人请求已读回执
- 使用MicrobiomeAnalyst统计和功能分析微生物组数据
- 微信小程序与公众号区别PHP,微信小程序和微信公众号的区别是什么?