day16
文件 file
文件是用于数据存储的单位
文件通常用来长期存储数据
文件中的数据是以字节为单位进场顺序存储的
文件的操作流程
1.打开文件
2.读/写文件
3.关闭文件
注:
任何操作系统,一个应用程序同事打开文件的数量有最大数限制
文件的打开函数
open(file, mode='rt')
用于打开一个文件,返回此文件流对象,如果打开文件失败,则会触发OSError错误
文件的关闭方法:
F.close() # 关闭文件,释放系统资源
示例:
# file_open.py
try:
f = open('./myfile.txt')
print('打开文件成功')
s = f.read() # 读取全部内容,形成字符串用s绑定
print('读取到', len(s), '个字符')
print('内容是', s)
f.close()
except OSError:
print('文件打开失败')
文本文件操作
操作模式:文件操作模式要一致
't'
说明:
默认文件中存储的都是为字符数据,在读写过程中会自动进行编解码操作
文本文件以行为单位进行分隔,在python内部统一用'\n'作为换行符进行分隔
对文本文件的读写操作需要用字符串str()进行数据操作
各操作系统= 的换行符
Linux 换行符 '\n'
Widows 换行符 '\r\n'
新Mac OS 换行符 '\n'
文件流对象是可迭代对象,迭代过程中将以换行符'\n'作为分隔符
示例:
f = open('phonenumber.txt')
for x in f:
print(x)
标准输入输出文件
sys.stdin 标准输入文件 # 默认键盘 ctrl+d 输入文件结束符
sys.stdput 标准输出文件 # 默认屏幕终端
sys.stderr 标准错误输出文件 # 默认屏幕
模块名:sys
注:
标准文件不需要打开和关闭就可以使用
示例:
stdin.py
import sys
while True:
s = sys.stdin.readline() # 类似input
if lens < 2:
break
print('刚才读到的是', len(s), '个字符')
print('读的内容是', s)
sys.stdout.write('hello')
sys.stdout.write(' ')
sys.stdout.write('world!\n')
print('结束')
在Linux命令行中 ~$ python3 stderr.py > mystdout.txt 2> myerr.txt
> 标准输出重定向
2> 标准错误输出重定向
二进制文件操作:
二进制文件操作模式字符: 'b'
默认文件中存储的是以字节为单位数据,通常有人为规定的格式
二进制文件操作需要用字节串进行读写
F.read() / F.readline() / F.readlines() 返回类型
对于文本文件,F.read()等函数返回为字符串(str)
对于二进制文件,F.read()等函数返回为字节串(bytes)
F.write() 对于二进制文件也需要用字节串来操作
F.tell() 返回当前的读写位置(从文件头以字节为单位)
F.seek(偏移量, whence=相对位置) 设置读写位置
偏移量
大于0的数代表向文件末尾方向移动的字节数
小于0的数代表向文件头方向移动的字节数
相对位置
0 代表从文件头开始偏移
1 代表从文件当前读写位置开始偏移
2 代表从文件尾开始偏移
汉字编码(讲两种)
国标系列:
GB18030 (二字节或四字节编码,共27533个字)
GBK (二字节编码,共21003个字)
GB2312 (二字节编码,共6763个汉子)
汉子编码一定大于 7F
(Windows 常用)
国际标准: UNICODE <-------> UTF-8
(Linux / Mac OS X / IOS / Android 等常用)
python 编码字符串
'gb2312'
'gbk'
'gb18030'
'utf8'
'ascii'
...
以上字符串用于encode和decode中
编码注释:
在pyth on源文件的第一行或第二行写入如下内容:
# -*- coding:gbk -*-
# 设置源文件编码格式为gbk
或
# -*- coding:utf-8 -*-
# 设置源文件编码格式为utf-8
作用:
告诉解释执行器,此文件的编码是什么
练习
1.写文件实现复制文件的功能
要求:
1.要考虑特大文件的问题
2.要关闭文件
3.要能二进制文件
如:
请输入源文件路径名: /home/tarena/xxx.tar.gz
请输入目标文件路径名: ./a.tar.gz
显示:
文件已成功复制
2.修改学生信息管理程序,加入两个功能
9) 保存信息到文件(si.txt)
10)从文件中读取数据(si.txt)
文本模式
把文件的字节码自动转换为字符串
换行符会自动转换为'\n'
二进制模式
通过字节串(字节数组)进行读写
day17
面向过程编程:
函数做单位
面向对象编程 Object-Oriented Programing
对象是指现实中的物体或实体
对象有很多属性(名词,形容词)
eg:姓名,年龄,性别...
对象有很多行为(动词)
eg:学习,吃饭,睡觉,工作...
面向对象
把一切都看成对象(或实例)用各种对象之间的关系来描述事物
什么是类:
拥有相同属性和行为的对象分为一组,即为一个类
类是用来描述对象的工具,用类可以创建此类的对象(实例)
面向对象示意:
车(类) -----> BYD E5(湘A11111) 实例(对象)
\
\--> BMW X5(湘F22222) 实例(对象)
类的创建语句:
class 类名(继承列表):
'类的文档字符串'
实例方法的定义
类变量的定义
类方法的定义(@classmethod)
静态方法的定义(@ststicmethod)
作用:
创建一个类
类用于描述对象的行为和属性
类用于创建此类的一个或多个对象(实例)
说明:
类名必须为标识符
类名实质就是变量,它绑定一个类
实例见:
myclass.py
构造函数
构造函数的调用表达式
语法:
类名([创建传参列表])
作用:
创建这个类的实例对象,并返回此实例的引用关系
示例:
myclass.py
实例对象说明:
实例有自己的作用域和名字空间,可以为该类的对象添加实例变量(也叫属性)
实例可以调用类方法和实例方法
实例可以访问类变量和实例变量
实例方法 method
语法:
class 类名(继承列表):
def 实例方法名(self, 参数1, 参数2,...):
'文档字符串'
语句
作用:
用于描述一个对象的行为,让此类型的全部对象都拥有相同的行为
说明:
实例方法实质是函数,是定义在类内的函数
实例方法至少有一个形参,第一个形参代表调用这个方法的实例,一般命名为'self'
实例方法的调用语法:
实例.实例方法名(调用传参)
或
类名.实例方法名(实例,调用传参)
示例:
myclass.py
实例属性 attribute(也叫实例变量)
每个实例对象可以有自己的变量,称为实例变量(也叫属性)
语法:
实例.属性名
赋值规则:
首次为属性赋值则创建此属性
再次为属性赋值则修改属性的绑定关系
作用:
记录每个对象自身的数据
示例:
myclass.py
删除属性 del 语句
del 对象.实例变量名
del 语句:
del 变量名
del 列表[整数表达式]
del 字典[key]
del 对象.属性
初始化方法:
作用:
对新创建的对象添加属性
语法:
class 类名(继承列表):
def __init__(self [,形参列表]):
语句块
说明:
初始化方法比必须为'__init__'不可改变
初始化方法会在构造函数创建实例后自动调用,且将实例自身通过第一个参数 self 传入 '__init__' 方法
构造函数的实参将通过 __init__ 方法的参数列表传入到'__init__'方法中
初始化方法内如果需要return 语句返回,则只能返回None
示例:
init_method.py
析构方法:
语法:
class 类名(继承列表):
def __del__(self):
...
说明:
析构方法在对象被销毁时被自动调用
python建议不要在对象销毁时做任何事情,因为销毁的时间难以确定
预置实例属性
__dict__ 属性
__dict__属性绑定一个存储此实例自身变量的字典
示例:
class Dog:
pass
dog1 = Dog()
print(dog1.__dict__) # {}
dog1.kinds = '京巴'
print(dog1.__dict__) # {'kinds':'京巴'}
__class__ 属性
此属性用于绑定创建这些实例的类
作用:
可以借助于此属性来访问创建此实例的类
示例:
class Dog:
pass
dog1 = Dog()
print(dog1.__class__)
dog2 = dog1.__class__()
print(dog2.__class__)
用于类的函数:
isinstance(obj, class or tuple)
返回这个对象obj是否是某个类的对象或某些类中的一个类的对象,如果是则返回True,否则返回False
isinstance(123, (int, str, list)) # True
isinstance(123.0, int) # False
type(obj) 返回对象的类型
练习
修改原来的学生信息管理程序,将原来的字典存储学生信息
先改用对象来存储学生信息
(要求类Student 要写在模块 student.py 中)
day18
类变量
类变量是类的属性,此属性属于类,不属于此类的实例
作用:
通常用来存储该类创建的对象的共有属性
说明:
类变量可以通过类直接访问
类变量可以通过类的实例直接访问
类变量可以通过此类的对象的__class__属性间接访问
示例:
class_variable.py
类的文档字符串
类内第一个没有赋值给任何变量的字符串为类的文档字符串
类的文档字符串可以用类的 __doc__ 属性访问
类的 __slots__ 列表
作用:
限定一个类创建的实例只能有固定的属性(实例变量)
不允许对象添加列表意外的属性(实例变量)
防止用户因写错属性名而发生程序错误
说明:
__slots__ 列表绑定一个字符串列表
含有 __slots__列表的类所创建的实例对象没有__dict__属性,即此实例不用字典来存储对象的属性(实例变量)
示例:
# slots.py
class Human:
__slots__ = ['name', 'age'] # 限制此类的对象只能有'name'和'age'属性
def __init__(self, name, age):
self.name = name
self.age = age
h1 = Human('liu', 18)
print(h1.age) # 18
# h1.Age = 19 # 出错,AttributeError
print(h1.age) # 18
# print(h1.__dict__) # 出错,AttributeError
类方法 @classmethod
类方法是用于描述类的行为的方法,类方法属于类,不属于类的实例
说明:
类方法需要使用@classmethod装饰器定义
类方法至少有一个形参,第一个形参用于绑定类,约定写为'cls'
类和该类的实例都可以调用类方法
类方法不能访问此类创建的实例属性
示例:
class_method.py
静态方法 @staticmethod
静态方法是定义在类内部的函数,此函数作用域是类的内部
说明:
静态方法需要使用 @staticmethod装饰器定义
静态方法与普通函数定义相同,不需要传入self实例参数和cls参数
静态方法只能凭借该类或类创建的实例调用
静态方法不能访问类变量和实例变量(属性)
示例:
static_method.py
实例方法,类方法,静态方法,函数 小结
不想访问 类内 和 实例内 的变量,用静态方法
只想访问 类内变量,不想访问 实例变量,用类方法
既要访问 类变量,也想访问 实例变量 用实例方法
函数与静态方法相同,只是静态方法的作用域定义在类内
继承 inheritance 和 派生 derived
继承是指从已有的类中派生出新的类,新类具有原类的行为,并能扩展新的行为
派生类就是从一个已有类中衍生称新类,在新类上可以添加新的属性和行为
作用:
1.用继承派生机制,可以将一些共有功能加在基类中,实现代码的共享
2.在不改变基类的代码的基础上改变原有的功能
名词:
基类(base class) / 超类(super class) / 父类(father class)
派生类(derived class) / 子类(child class)
单继承:
语法:
class 类名(基类名):
语句块
说明:
单继承是指由一个基类衍生出的新的类
示例:
inherit.py
继承说明:
python3任何类都直接或间接的继承自object类
object类是一切的超类
类的__base__属性
__base__属性用来记录此类的基类
python内建的类详见:
>>> help(__builtins__)
覆盖 override
覆盖是指在有继承关系的类中,子类中实现了与基类同名的方法,在子类的实例调用该方法时,实际调动的是子类中的覆盖版本,这种现象叫做覆盖
示例:
override.py
子类对象显式调用基类(被覆盖)方法的方式:
基类名.方法名(实例, 实际调用传参)
super 函数
super(cls, obj) 返回绑定超类的实例(要求obj必须是cls类型的实例)
super() 返回绑定超类的实例,等同于:super(__class__,实例方法的第一个参数),必须在方法内调用
作用:
借助super() 返回的实例间接调用其父类的覆盖方法
显式调用基类的初始化方法
当子类中实现了 __init__方法,基类的构造方法并不会被调用
def __init__(self,...)
示例见:
super_init.py
1.学生管理系统用学生对象封装
(要求,不要在外部直接操作学生的属性,操作行为用方法代替)
day19
用于类的函数
issubclass(cls, class or tuple)
判断一个类是否继承自其他类,如果此cls 是 class or tuple 的 子类则返回true
封装 enclosure
封装是指隐藏类的实现细节,让使用者不关心这些细节
封装的目的是让使用者通过尽可能少的方法(或属性)操作对象
私有属性和私有方法
python类中以双下划线(__)开头,不以双下划线结尾的标识符为私有成员,私有成员或只能用类内的方法进行访问和修改
以__开头的实例变量是私有属性
以__开头的方法为私有方法
示例:
enclosure.py
多态 polymorphic
字面意思:多种状态
多态是指在有继承/派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖方法的现象叫多态
状态:
静态(编译时状态)
动态(运行时状态)
说明:
多态调用方法与对象相关,不与类相关
Python的全部对象都只有'运行时状态(动态)'
没有'C++'里的'编译时状态(静态)'
示例:
poly.py
面向对象的语言的特征
继承
封装
多态 # 同一函数 在不同对象会调用不同方法
多继承 multiple inheritance
多继承是指一个子类继承自两个或两个以上的基类
语法:
class 类名(基类名1, 基类名2,...):
...
说明:
一个子类同时继承自多个父类,父类中的方法可以同时被继承下来
如果两个父类中有同名的方法,而在子类中又没有覆盖此方法时,调用结果难以确定
多继承的问题(缺陷)
标识符(名字空间)冲突的问题
要谨慎使用继承
多继承的 MRO(Method Resolution Order) 问题
类的 __mro__ 属性
此属性用来记录类的方法查找顺序
super.方法 是根据 调用对象的 __mro__ 来确定调用顺的
示例:
class A:
def go(self):
print(1)
class B:
def go(self):
print(2)
super().go()
class C:
def go(self):
print(3)
super().go()
class D:
def go(self):
print(4)
super().go()
d = D()
d.go()
print(d.__mro__)
函数重写
在自定义类内添加相应的方法,让自定义类创建的实例能像内建对象一样进行内建函数操作
对象转字符串函数
repr(obj) 返回一个能代表此对象的表达式字符串,通常
eval(repr(obj)) == obj
这个字符串通常是给python解释执行器运行用的
str(obj) 通过给定的对象返回一个字符串(这个字符串通常是给人看的)
>>> s = 'hello'
>>> s1 = str(s)
>>> s2 = repr(s)
>>> print(s1, s2)
对象转字符串函数的重写方法
repr(obj) 函数的重写方法:
def __repr__(self):
str(obj) 函数的重写方法:
def __str__(self):
说明:
1.str(obj) 函数先查找,obj.__str__()方法,调用此方法并返回结果
2.如果没有__str__方法时,则返回__repr__方法的结果并返回
3.如果obj.__repr__方法不存在,则调用object类的__repr__实例方法显示<__main__.XXXX object at 0xaabbccdd>
示例:
myfile.py
其他内建函数的重写方法:
__abs__ abs(obj)
__len__ len(obj) 必须返回整数
__reversed__ reversed(obj) 必须为可迭代对象
__round__ round(obj)
数值转换函数的重写
__complex__ complex(obj)
__int__ int()
__float__ float()
__bool__ bool()
布尔测试函数重写
格式:
__bool__
作用:
用于bool(obj)函数的取值
用于if 语句的真值表达式中
用于while语句的真值表达式中
说明:
1.当自定义的类内有 __bool__(self)方法时,以此方法的返回值作为bool(obj)的返回值
2.当不存在__bool__(self) 方法时,bool(x)返回__len__(self)方法的返回值是否为0来测试布尔值
3.当不存在__len__(self) 方法时,直接返回True
迭代器(高级)
可以通过next(it)函数取值的对象就是迭代器
迭代器协议
迭代器协议是指对象能够使用next函数获取下一项数据,在没有下一项数据时,触发一个StopIteration异常来终止迭代的约定
迭代器协议实现方法:
__next__(self) 方法来实现迭代器协议
语法形式:
class MyIterator:
def __next__(self):
迭代器协议
return 数据
什么是可迭代对象
是指能用iter(obj)函数返回迭代器的对象(实例)
可迭代对象内部需要定义__iter__(self)方法来返回迭代器对象
1.修改原有学生信息管理系统,将学生对象的,全部属性都变为私有属性不让外部直接访问,实现封装
2.写一个列表类Mylist实现存储整数列表
class MyList:
def __init__(self, iterator):
self.data = ...
让此类对象能用for 语句进行迭代访问
L = MyList(range(5))
print(L)
L2 = [x ** 2 for x in L]
print(L2)
3.写一个类 Fibonacci 实现迭代器协议,此类对象可以作为可迭代对象生成相应的斐波那契数
1 1 2 3 5
class Fibonacci:
def __init__(self,n) # n代表个数
实现如下
for x in Fibonacci(10):
print(x)
L = [x for x in Fibonacci(50)]
print(L)
F = fibonacci(30)
print(sum(F))
day20
对象的属性管理函数:
getattr(obj, name[,default])
从一个对象得到对象的属性,getattr(x,'y')等同于x.y,当属性不存在时,如果给出default参数,则返回default,如果没有给出default,则产生一个AttributeError
hasattr(obj, name)
用给定的name返回对象obj是否有此属性,此种做法可以避免在getattr(obj, name)是引发错误
setattr(obj, name, value)
给对象obj的名为name的属性设置相应的值value; setattr(x,'y',z)等同于 x.y = z
delattr(obj, name)
删除对象obj中的name属性,delattr(x,'y')等同于 del x.y
异常(高级)
可以用于异常的语句:
try-except # 捕获异常
try-finally # 任何流程都要执行的语句
raise # 抛出异常
assert # 根据条件抛出异常
with 语句
语法:
with 表达式1 [as 变量1], 表达式2 [as 变量2],...
语句块
作用:
使用于对资源进行访问的场合,确保使用过程中不管是否发生异常都会执行必要的'清理操作',并释放资源
(如:文件使用自动关闭,线程中锁的自动获取和释放等)
说明:
执行表达式 as子句中的变量绑定生成的对象
with语句并不改变异常的状态
示例:
# with.py
with open('a.txt') as f:
for x in f:
print(f)
环境管理器
类内有__enter__和__exit__实例方法的类被成为环境管理器
能够用with进行管理的对象必须是环境管理器
说明:
__enter__将在进入with语句是被调用并返回有as变量绑定的对象
__exit__将在离开with语句时调用,且可以用参数来判断在离开with语句时是否有异常发生并做出相应的处理
运算符重载
让自定义的类生成的对象(实例)能够使用运算符进行操作
作用:
1.让自定义的类的实例能够进行运算符操作
2.让程序简洁易读
3.对自定义对象将运算符赋予新的运算规则
说明:
运算符重载方法的参数已经有固定的含义,不介意改变原有的意义
算术运算符重载
方法名 运算符和表达式 说明
__add__(self, rhs) self + rhs 加法
__sub__(self, rhs) self - rhs 减法
__mul__(self, rhs) self * rhs 乘法
__truediv__(self, rhs) self / rhs 除法
__floordiv__(self, rhs) self // rhs 地板除
__mod__(self, rhs) self % rhs 求余
__pow__(self, rhs) self ** rhs 幂
反向算术运算符的重载
当运算符的左侧为内建类型,右侧为自定义类型的对象进行算术运算符运算时,会出现TypeError错误
因无法修改内建类型的代码来实现运算符重载,此时需要使用反向运算符重载
方法名 运算符和表达式 说明
__radd__(self, lhs) lhs + self 加法
__rsub__(self, lhs) lhs - self 减法
__rmul__(self, lhs) lhs * self 乘法
__rtruediv__(self, lhs) lhs / self 除法
__rfloordiv__(self, lhs) lhs // self 地板除
__rmod__(self, lhs) lhs % self 求余
__rpow__(self, lhs) lhs ** self 幂
复合赋值算术运算符的重载
以复合赋值算术运算符 x += y 为例,此运算会优先调用 x.__iadd__(y)方法,如果没有__iadd__方法时会将复合赋值运算拆解为 x = x + y,然后调用 x = x.__add__(y)方法,如果不存在__add__方法则会触发 TypeError异常
方法名 运算符和表达式 说明
__iadd__(self, rhs) self += rhs 加法
__isub__(self, rhs) self -= rhs 减法
__imul__(self, rhs) self *= rhs 乘法
__itruediv__(self, rhs) self /= rhs 除法
__ifloordiv__(self, rhs) self //= rhs 地板除
__imod__(self, rhs) self %= rhs 求余
__ipow__(self, rhs) self **= rhs 幂
比较运算符重载
方法名 运算符和表达式 说明
__lt__(self, rhs) self < rhs
__le__(self, rhs) self <= rhs
__gt__(self, rhs) self > rhs
__ge__(self, rhs) self >= rhs
__eq__(self, rhs) self == rhs
__ne__(self, rhs) self != rhs
位运算符重载
__invert__(self) ~self
__and__(self, rhs) self & rhs
__or__(self, rhs) self | rhs
__xor__(self, rhs) self ^ rhs
__lshift__(self, rhs) self << rhs
__rshift__(self, rhs) self >> rhs
反向位运算符重载
__rand__(self, lfs) lfs & self
__ror__(self, lfs) lfs | self
__rxor__(self, lfs) lfs ^ self
__rlshift__(self, lfs) lfs << self
__rrshift__(self, lfs) lfs >> self
一元运算符重载
__invert__(self) ~self
__pos__(self) +self
__neg__(self) -self
in / not in 运算符的重载
重载方法:
def __contains__(self, e):
....
索引 / 切片运算符的重载
方法名 运算符和表达式 说明
__getitem__(self, i) x = self[i] 索引/切片取值
__setitem__(self, i, val) self[i] = val 索引/切片赋值
__delitem__(self, i) del self[i] 删除索引/切片
说明:
让自定义的类的对象能够支持索引和切片操作
slice 函数
用于创建一个slice切片对象,此对象存储切片的信息
slice(start=None, Stop=None, Step=None)
slice对象的属性
s.start 切片的起始值,默认为None
s.stop 切片的终止值,默认为None
s.step 切片的步长,默认为None
特性属性 @property
实现其他语言所拥有的 getter 和 setter 的功能
作用:
用来模拟一个属性
通过 @property 装饰器可以对模拟属性的取值和赋值加以控制
示例:
property.py
练习
实现有序集合类 OrderSet() 能是实现两个集合的交集&,全集|,补集-,对称补集^,==/!=,in/not in,等操作
要求集合内部用list存储
class OrderSet:
...
s1 = OrderSet([1, 2, 3, 4])
s2 = OrderSet([3, 4, 5])
print(s1 & s2)
print(s1 | s2)
print(s1 ^ s2)
if OrderSet([1, 2, 3]) != OrderSet([3, 4, 5]):
print('不相等')
if s2 == OrderSet([3, 4, 5]):
print('s2 == OrderSet([3, 4, 5]) is true')
if 2 in s1:
print('2在s1中')