面向对象基础
python对象包含三个部分:
- id
- type
- value
- 属性
- 方法
新建一个类对象 先init再new
构造函数 _ _ i n i t _ _ ( ) \_ \_ init \_\_ () __init__()
用于执行实力对象的初始化工作, 对象创建后初始化当前对象的相关属性, 无返回值;
通常用故意初始化实例对象的实例属性,
通过类名(参数列表)来调用构造函数,将创建好的对象返回给相应的变量
实例属性
从属于实例对象的属性(也可以叫实例变量)
一般在 _ _ i n i t _ _ ( ) \_ \_ init \_\_ () __init__()方法中通过
self.实例属性名 = 初始值
在类中的其他的实例方法中也可以通过self.实例属性名来调用
class Student:#模具
def __init__(self,name,age):
self.name = name
self.age = age
def say_score(self):
print("{0}的年龄是:{1}".format(self.name, self.age))
s1 = Student("Wong",22)
s1.say_score()
s1.salary = 10000 ## 此时s1对象中多了一个salary属性
s2 = Student("xxx",20) ##s2中并没有salary属性,因为模具中无
实例方法
实例对象调用方法的本质:
实例对象 a = Student(), a.say_score()?
有type类型的模具来创造Student类,里面存放了常量,代码之类的
先创建Student类(有属性和方法了),堆中有地址(🎂)了,self代表的是对象本身,赋给栈中的变量a, a在调用say_score方法的时候,需要传一个self给say_score,传的就是这个🎂地址 Student.say_score(a)中a的地址也是🎂, 实际上解释器就是这么调用的
类对象
如果 ss = Student, 就指向了同样的类对象
工厂模式
单例模式
class mysingleton:
__obj = None
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls) ##然后把建好的对象放在了__obj这个类属性里面
return cls.__obj
def __init__(self,name):
print("init...")
self.name = name
现在创建对象:
a = mysingleton('aa')
b = mysingleton('bb')
图略
地址是一个,但初始化方法调了两次, 如果是单例的话,应该只有一个对象
class mysingleton:
__obj = None
#这里加入一个_init_flag作为是否已经执行初始化的标志
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls) ##然后把建好的对象放在了__obj这个类属性里面
return cls.__obj
def __init__(self,name):
if self.__init_flag:
print("init...")
self.name = name
self.__init_flag = False##如果执行到这里,直接改掉了类属性, 执行一次init方法后,__init_flag就变为False了, 不会再到第10行及以后了
#类里面写东西千万不能丢掉self.xxx这个, 如果line 13那个位置不加self就会改不过来__init_flag为False
a = mysingleton('aa')
b = mysingleton('bb')
print(a)
print(b)
#他们的name都是aa,地址也都是一个地址, line15执行过一次初始化了, line16不再执行'init'也只会print一次
工厂模式改单例模式
class carfactory:
__obj = None
__init_flag = True
def create_car(self,brand):
'''这里不能写self.brand
因为self.brand就是类属性了,这里的brand是实例属性'''
if brand == 'BMW':
return bmw()
elif brand == 'Benz':
return benz()
elif brand == 'BYD':
return byd()
else:
return '其他品牌'
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self):
if self.__init_flag:
print("init CarFactory...")
self.__init_flag = False
class benz:
pass
class byd:
pass
class bmw:
pass
文件和IO流
图略
创建文件对象open()
open用于创建文件对象, 基本语法如下
open(文件名[,打开方式])
打开方式如下:
模式 | 描述 |
---|---|
r | 读 |
w | 写:如果文件不存在则创建;存在则重写新内容 |
a | 追加append模式:如果文件不存在则创建;存在则在文件末尾追加内容 |
b | 二进制模式,可与其他模式组合使用 |
+ | 读写模式,可与其他模式组合使用 |
例如f = open(r"d:\test.txt","w")
, r是为了不care转义字符
没有增加'b'
模式则默认创建文本文件对象, 处理的对象基本单元是字符;如果是二进制模式'b'
, 创建的是二进制文件对象, 处理的基本单元是字节.例如f = open(r"d:\test.jpg","b")
文本文件的写入
三个步骤:
- 创建文件对象
- 写入数据
- 关闭文件对象
f = open('a.txt','a')
s = "随便写写 \n 随便写写"
f.write(s)
f.close()
执行py程序需要通过解释器 ⇒ \Rarr ⇒解释器调用操作系统的资源(程序), ⇒ \Rarr ⇒操作系统要操作硬盘文件. 要记得关闭否则会浪费硬盘资源
编码知识
图略
解释器执行程序会调用操作系统(py解释器编码unicode
)生成一个文件,编码是操作系统(Windows)的默认编码格式gbk
. 在这个过程中会把unicode
转化成gbk
, 再次打开时应该使用gbk
.
写入数据
write(a)
: 把字符串a写入到文件中
writelines(b)
: 把字符串列表写入文件中, 要自己加入换行符
f = open(r"b.txt",'w', encoding='utf-8')
s = ["随便写写\n","第二行\n","第三行\n"]
f.writelines(s)
f.close()
close()关闭文件流
结合异常机制, 如果中途程序崩溃, 要走完全部程序流程最后关闭硬盘文件.
try:#放操作
f = open(r"my01.txt","a")
str = "WongYiichun"
f.write(str)
#异常,一旦上面的出问题了,就到下面这部分excpet来执行
except BaseExecption as e:##e是引用名称
print(e)
finally:##必须执行
f.close()
以上述代码为例, 对象f中有一个缓冲区, 调用write(str)
时会把字符串先写到缓冲区, 真实的硬盘上该文件中没有这个str
的, 调用flush()
时,数据会’冲入’文件中(准确地说,是真正写到文件中).
调用close()
方法时
⇒
\Rarr
⇒操作系统会将缓冲区的数据写入文件
⇒
\Rarr
⇒再关闭文件, 释放对象及释放资源
with语句(上下文管理器)
相当于在with
前加了一个还原点, 进with
之前啥子样, 结束with
代码块时就啥子样.
s = ["随便写写\n","第二行\n","第三行\n"]
with open(r"b.txt",'w', encoding='utf-8') as f:
f.writelines(s)
文本文件的读取
read([size])
: 从文件中读取size个字符串(一个字母一个汉字都是一个字符==?==), 并作为结果返回,;如果没有size参数, 则读取整个文件,读取到文件末尾,会返回空字符串
readline()
: 读取一行内容作为结果并返回;读取到文件末尾, 会返回空字符串
readlines()
: 文本文件中, 每一行作为一个字符串存在列表中, 返回该列表
##读取文本文件
with open(r"b.txt","r",encoding = "utf-8")as f:
str = f.read()
print(str)
只读一个字符
with open(r"b.txt","r",encoding = "utf-8")as f:
k = f.read(1)
print(k)
文件较小时一次性读取全部
with open(r"b.txt","r",encoding = "utf-8")as f:
print(f.read(1))
按行读取一个文件
with open(r"b.txt","r",encoding = "utf-8")as f:
while True:
fragment = f.readline()
if not fragment:##不为空
break
else:
print(fragment, end = ",")
使用迭代器(每次返回一行)读取文本文件
with open(r"b.txt","r",encoding = "utf-8")as f:
for a in f:
print(a,end=",")
enumerate()函数和推导式
with open('e.txt','r',encoding = 'utf-8') as f:
lines = f.readlines()
lines = [line.rstrip() +'#'+str(index) for index,line in enumerate(lines)]##函数推导式,rstrip()方法用于去空格
with open('e.txt','w',encoding = 'utf-8') as f:
f.writelines(lines)
文件拷贝
with open('io_file.png','rb') as f:
with open('io_copy.png','wb') as w:
for line in f.readlines():
w.write(line)
print('图片拷贝完成')
文件对象的常用属性和方法
文件对象的属性
属性 | 说明 |
---|---|
name | 返回文件的名字 |
mode | 返回文件的打开模式 |
closed | 若文件已被关闭则返回True(Indicator) |
文件对象的打开模式, 此处不再赘述
文件对象的方法 []内的内容表示可选
方法名 | 说明 |
---|---|
read([size]) | 从文件中读取size个字符或字节的内容并返回; 若省略参数,则读取全部内容 |
readline() | 从文本文件中读取一行内容 |
readlines() | 把文本文件中的每一行都作为独立的字符串对象, 将这些对象放入列表并返回 |
write(str) | 将字符串str写入文件 |
writelines(s) | 将字符串列表s写入文件, 不添加换行符 |
seek(offset[,whence]) | 将文件指针移动到新的位置, offset表示相对于whence的位置.off为正向末尾移动,为负向开始那边移动;whence的含义:0从文件头开始计算(默认值); 1:从当前位置开始计算; 2:从文件尾开始计算 |
tell() | 返回文件指针的当前位置 |
truncate([size]) | 不论指针在什么位置, 只留下指针前size个字节的内容,其余全部删除; 若没有传入参数, 则指针当前位置到文件末尾的内容全部删除 |
flush() | 把缓冲区的内容写入文件, 但不关闭文件 |
close() | 把缓冲区的内容写入文件, 同时关闭文件, 释放文件对象的相关资源 |
使用pickle序列化
import pickle
with open(r"xxxx.dat","wb") as f:
a1 = "Wong"
a2 = 234
a3 = [20,30,40]
pickle.dump(a1,f);pickle.dump(a2,f);pickle.dump(a3,f)
with open(r"xxxx.dat","wb") as f:
b1 = pickle.load(f)
b2 = pickle.load(f)
b3 = pickle.load(f)
print(b1); print(b2); print(b3)
id(a1)##不是一个对象
id(b1)
CSV文件的读取和写入
读取:
import csv
with open("iris.csv","r") as f:
a = csv.reader(f)
#print(list(a)) return一个大列表
##如果执行完print(list(a))后,文件指针已经在文件末尾了, 下一个就print出来就没东西了
for row in a:
print(row)
写入: 一行就是一个列表
c = [[1002,"Q",6],[1003,"W",7]]
with open("ee.csv","w") as f:
b = csv.writer(f)
#但似乎写入的时候每次都会换行,中间会空一行
b.writerow(["id","姓名", "年龄"])
b.writerow(["1001","Wong","18"])
b.writerows(c)#写入一个列表
OS模块
os.path模块
帮助我们对操作系统的进行操作
import os
os.system("notepad.exe")
os.system("regedit")#注册表
os.system("ping www.baidu.com")#出现乱码的话在pycharm里面的settings可以查encoding改为gbk
os.startfile(r"D:\Changyong\WeChat\WeChat.exe")##调用微信等可执行程序
文件和目录操作
os模块下常用的操作文件的方法
方法名 | 描述 |
---|---|
remove(path) | 删除指定的文件 |
rename(src, dest) | 重命名文件或目录 |
stat(path) | 返回文件的所有属性 |
listdir(path) | 返回path目录下的文件和目录列表 |
os模块下常用的操作目录的方法
方法名 | 描述 |
---|---|
mkdir(path) | 创建目录 |
makedirs(path1/path2/…) | 创建多级目录 |
rmdir(path) | 删除目录 |
removedirs(path1/path2/…) | 删除多级目录 |
getcwd() | return当前工作目录 |
chdir(path) | 把path设为工作目录 |
walk() | 遍历目录树 |
sep | 当前操作系统使用的路径分隔符 |
os.path
图略
walk()遍历
#coding = utf-8
import os
allfiles = []
path = os.getcwd()
list_files = os.walk(path)
for dirpath,dirnames,filenames in list_files:
for dir in dirnames:
allfiles.append(os.path.join(dirpath,dir))
for file in filenames:
allfiles.append(os.path.join(dirpath,file))
for file in allfiles:print(file)
shutil拷贝和压缩
拷贝
import shutil
shutil.copy("a.txt","a_copy.txt")
shutil.copytree("iris-data","testcopy")#注意不要和已有目录冲突
#参数ignore, 执行拷贝操作时不copy哪些文件
shutil.copytree("iris-data","testcopy",ignore = ignore_patterns("*.txt","*.html"))
压缩
shutil.make_archive("压缩包所在位置及名字","压缩格式","压缩文件夹名字")###最后一个参数必须是文件夹
shutil.make_archive("testcopy","zip","iris-data/Iris.csv")
zipfile
import zipfile
z1 = zipfile.ZipFile("testcopy/a.zip","w")
z1.write("a.txt");z1.write("b.txt")
z1.close()
解压
import zipfile
z2 = zipfile.ZipFile("testcopy/a.zip","r")
z2.extractall("test")##解压到wd下的叫test的文件夹中
z2.close()
递归算法
递归目录树
#coding = utf-8
import os
allfiles = []
def getallfiles(path,level):
childdir = os.listdir(path)
for file in childdir:
filepath = os.path.join(path,file)
if os.path.isdir(filepath):
getallfiles(filepath,level+1)
allfiles.append(" "*level + filepath)
return (allfiles)
f = getallfiles('testcopy',0)
for n in reversed(f):
print(n)
异常情况
异常的基本结构
try:
被监控的可能引发异常的语句块
except BaseException as e:
异常处理语句块
try:
print("step1")
a = 3/0
print("step2")#这个不会执行
except BaseException as e: #产生异常的类对象
print("step3")
print(e)
print("step4")
多个except结构
前一个except的异常不能时候一个异常的父类
try:
可能出现异常的语句块
except exception1:
处理异常1的语句块
except exception2:
处理异常2的语句块
except exception3:
处理异常1的语句块
#...
except BaseException:
可能出现遗漏的异常的语句块
try… except…else结构
try:
a = input("请输入被除数:")
b = input("除数:")
c = float(a) / float(b)
except ZeroDivisionError:
print('The division coud not be zero')
except NameError:
print("There is no variable named a or b")#例如line3那里b写成了d
except ValueError:
print("No string here")
except BaseException as e:
print("e")
else:
print("结果是:", c)
try… except…finally结构
finally: 无论异常是否发生, finally块都会执行, 通常用来释放try块中申请的资源
try:
a = input("请输入被除数:")
b = input("除数:")
c = float(a) / float(b)
except ZeroDivisionError:
print('The division coud not be zero')
except NameError:
print("There is no variable named a or b")#例如line3那里b写成了d
except ValueError:
print("No string here")
except BaseException as e:
print("e")
else:
print("结果是:", c)
finally:
print("无论是否发生异常我都会执行")
print("执行完毕")
在io流, 文件处理中的应用
try:
f = open("a.txt","r")
content = f.readline()#只读取一行内容
print(content)
except:
print("文件未找到")
finally:
f.close()
更严谨一点的话:
try:
f = open("a.txt","r")
content = f.readline()#只读取一行内容
print(content)
except:
print("文件未找到")
finally:
try:
f.close()
except BasicException as e:
print(e)
print("结束")
常见异常的解决
因为return可以用作结束方法运行的一种方式, 但return要放在方法末尾, 不要放在try或者except这类语句块的里面.(记住即可)
异常 | 错误 |
---|---|
SyntaxError | 语法错误 |
NameError | 尝试访问一个没有声明的变量 |
ZeroDivisionError | 零除错误 |
ValueError | 数值错误 |
TypeError | 类型错误, 例如加号两边必须是数或者必须都是字符串 |
AttributeError | 访问对象的不存在的属性 |
IndexError | 索引越界异常, 例如列表长度是4, index为5就错了 |
KeyError | 字典的Key不存在 |
异常名称 | 说明 |
---|---|
ArithmeticError | 所有数值计算错误的基类 |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
BaseException | 所有异常的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
EnvironmentError | 操作系统错误的基类 |
EOFError | 没有内建输入,到达 EOF 标记 |
Exception | 常规错误的基类 |
FloatingPointError | 浮点计算错误 |
FutureWarning | 关于构造将来语义会有改变的警告 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
ImportError | 导入模块/对象失败 |
IndentationError | 缩进错误 |
IndexError | 序列中没有此索引(index) |
IOError | 输入/输出操作失败 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
KeyError | 映射中没有这个键 |
LookupError | 无效数据查询的基类 |
MemoryError | 内存溢出错误(对于 Python 解释器不是致命的) |
NameError | 未声明/初始化对象 (没有属性) |
NotImplementedError | 尚未实现的方法 |
OSError | 操作系统错误 |
OverflowError | 数值运算超出最大限制 |
OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 |
RuntimeError | 一般的运行时错误 |
RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 |
StandardError | 所有的内建标准异常的基类 StopIteration 迭代器没有更多的值 |
SyntaxError | Python 语法错误 |
SyntaxWarning | 可疑的语法的警告 |
SystemError | 一般的解释器系统错误 |
SystemExit | 解释器请求退出 |
TabError Tab | 和空格混用 |
TypeError | 对类型无效的操作 |
UnboundLocalError | 访问未初始化的本地变量 |
UnicodeDecodeError Unicode | 解码时的错误 |
UnicodeEncodeError Unicode | 编码时错误 |
UnicodeError Unicode | 相关的错误 |
UnicodeTranslateError Unicode | 转换时错误 |
UserWarning | 用户代码生成的警告 |
ValueError | 传入无效的参数 |
Warning | 警告的基类 |
WindowsError | 系统调用失败 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
with上下文管理
前面讲过了
traceback模块 , 异常写入日志文件
import traceback
try:
print("Step1")
num = 1/0
except:
with open("a.txt","a") as f:
traceback.print(file = f)
##将异常情况写入到a.txt中, 大量异常不建议全部输出到控制台
自定义异常
例如定义一个年龄异常:
此处的一个小问题, 我的电脑似乎是GBK去编码, 但是python默认的是utf8编码,所以此处还是在代码起始位置加上一个说明:# -*- coding: GBK -*
,此处参考CSDN中Rachel_nana的博文 .
# -*- coding: GBK -*
class AgeError(Exception): # 继承Exception类
def __init__(self, errorInfo):
Exception.__init__(self) # 重写init方法
self.errorInfo = errorInfo
def __str__(self):
return str(self.errorInfo) + ",年龄错误, 超出阈值"
if __name__ == "__main__": # 如果为True, 则模块作为独立文件运行, 可以执行测试代码
age = int(input("请输入年龄"))
if age < 1 or age > 150:
raise AgeError(age)
else:
print("你输入的年龄是:", age)