02.python的基础知识(二)
原创大约 18 分钟
BUG
Bug的由来及其分类
Bug的常见类型
- 粗心导致的语法错误:SyntaxError
- 末尾的冒号
- 缩进错误
- 英文写成中文
- 字符串拼接
- 没有定义变量
- ==和=混用
- 知识点不熟练的错误
- 索引越界的错误:IndexError
- append()方法使用不熟练的错误
- 思路不清楚导致的问题
- 被动掉坑:程序逻辑没有问题,只是因为用户错误操作或者一些“例外操作”而导致程序崩溃
不同异常类型的处理方式
- python提供了异常处理机制,可以在异常出现时及时捕获,然后内部消化,让程序继续运行
- 捕获异常的顺序按照先子类后父类的顺序,为了避免可能出现的异常,可以在最后增加
BaseException - try...except...else结构
- 如果try块中没有抛出异常,则执行else块,如果try中抛出异常,执行except块
try:
n1 = int(input('请输入一个整数:'))
n2 = int(input('请输入一个整数:'))
result = n1 / n2
print("结果为",result)
except ZeroDivisionError:
print('输入的整数不能为0')
except ValueError:
print('输入的不是整数')
except BaseException as e:
print('未知错误',e)
- try...except...else...finally结构
- finally块无论是否发生异常都会被执行,通常用来释放try块中申请的资源
try:
n1 = int(input('请输入一个整数:'))
n2 = int(input('请输入一个整数:'))
result = n1 / n2
except BaseException as e:
print('未知错误',e)
else:
print("结果为",'%.2f'%result)
finally:
print('无论程序是否出错,都会执行的代码')
- python常见的异常类型
- ZeroDivisionError:除(或取模)零(所有的数据类型)数学运算时的异常
- IndexError:序列中没有此索引(index)
- KeyError:映射中没有这个键
- NameError:未声明/初始化对象(没有属性)
- SyntaxError:Python的语法错误
- ValueError:传入无效的参数
异常处理机制
- traceback模块
- 使用traceback模块打印异常信息
import traceback as back
try:
print(1/0)
except Exception as e:
back.print_exc()
PyCharm的调试模式
- 断点
- 程序运行到此处,暂时挂起,停止执行,可以详细观察程序的运行情况,方便做出进一步的判断
- 进入调试视图
- 进入调试视图的三种方式
- 点击工具栏上的按钮
- 右键单击编辑区,点击“debug模块名”
- 快捷键Shift+F9
- 进入调试视图的三种方式
编程界的俩大思想
俩大编程思想
- 面向过程
- 事务比较简单,可以用线性思维去解决问题
- 面向对象
- 事务比较复杂,使用简单的线性思维无法解决问题
- 面向过程和面向对象都是解决实际问题的一种思维方式,二者相辅相成,不是对立的,解决复杂问题,通过面向对象方式便于我们从宏观上把握事物之间的复杂关系,方便我们分析整个系统,具体到微观操作,我们仍然使用面向过程方式来处理
类和对象的创建
- 类:是多个类似事物组成的群体的统称,能够帮助我们快速处理和判断事物的性质
- 对象:Python一切皆对象
- 类创建的语法:
- 类名可以由一个或多个单词组成,每个单词首字母大写,其余都小写
class 类名:
pass
class Stuednt:
pass
print(id(Stuednt)) #1168198039840
print(type(Stuednt)) #<class 'type'>
print(Stuednt) #<class '__main__.Stuednt'>
- 类的组成
- 类属性
- 直接写在类里的变量,称为类属性
- 类中方法外的变量称为属性,被该类的所有对象所共享
- 实例方法
- 在类之外定义的称为函数,在类之外定义的称为方法
- 静态方法
- 使用@static修饰的方法,使用类名直接访问的方法
- 类方法
- 使用@classmethod修饰的方法,使用类名直接访问的方法
- 类属性
class Stuednt:
# 类属性
native_pace = '吉林' # 直接写在类里的变量,称为类属性
#初始化方法
def __init__(self,name,age):
# 实例属性
self.name = name #self.name称为实例属性,进行了一个赋值操作,将局部变量的name的值赋值给实例变量的name
self.age = age
#实例方法
def eat(self): # self 形参,代表类的实例对象
print('我是eat方法')
# 类方法
@classmethod
def cm(cls):
print('我是一个类方法,因为我使用了@classmethod装饰器')
#静态方法
@staticmethod
def sm():
print('使用了@staticmethod定义的方法,我是一个静态方法')
对象的创建
- 对象的创建又称为对象的实例化
- 语法
实例化=类名() class Stuednt: # 类属性 native_pace = '吉林' # 直接写在类里的变量,称为类属性 #初始化方法 def __init__(self,name,age): # 实例属性 self.name = name #self.name称为实例属性,进行了一个赋值操作,将局部变量的name的值赋值给实例变量的name self.age = age #实例方法 def eat(self): # self 形参,代表类的实例对象 print('我是eat方法') # 类方法 @classmethod def cm(cls): print('我是一个类方法,因为我使用了@classmethod装饰器') #静态方法 @staticmethod def sm(): print('使用了@staticmethod定义的方法,我是一个静态方法') # 创建Stuednt类的实例对象 stul = Stuednt('张三',20) print(id(stul)) # id()可以获取对象的内存地址 print(type(stul)) # type()可以获取对象的类型 print(stul) # 打印对象,会自动调用对象的__str__()方法 stul.eat() # 调用对象中的eat()方法 Stuednt.eat(stul) # 用类调用对象中的eat()方法动态绑定属性和方法
- python语言是动态语言,在创建对象之后,可以动态地绑定属性和方法
class Student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def eat(self):
print(self.name+"吃饭")
student1 = Student("张三", 18, 100)
student1.gender = "男"
def show():
print("我是show函数")
student1.show = show
print(student1.name, student1.age, student1.score,student1.gender,student1.show)
面向对象
封装
- 封装:提高程序的安全性
- 将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。这样,无需关心方法内部的具体实现细节,从而隔离了复杂度
- 在Python中没有专门的修饰符用于属性的私有性,如果该属性不希望在类对象的外部被访问,前边使用俩个“_”.
class Car:
def __init__(self, brand, color):
self.brand = brand
self.__color = color # 加__会变为,私有属性
def run(self):
print(self.brand + " is running")
car01 = Car("BMW", "red")
car01.run()
print(car01.brand)
# print(car01.__color) # 会报错,因为__color是私有属性,不能直接访问
print(dir(car01)) # 通过dir()函数可以查看对象的所有属性
print(car01._Car__color) # 通过这种方式可以访问私有属性
继承
继承:提高代码的复用性
语法格式:
class 子类类名(父类1,父类2...): pass如果一个类没有继承任何类,则默认继承object
Python支持多继承
定义之类时,必须在其构造函数中调用父类的构造函数
class animal:
def __init__(self, name, color):
self.name = name
self.color = color
def info(self):
print(self.name, self.color)
class reptile(animal):
def __init__(self, name, color, length):
super().__init__(name, color)
self.length = length
def run(self):
print("爬行")
animal01 = reptile("蜥蜴", "绿色", 0.5)
print(animal01.name,animal01.color,animal01.length)
多态
- 多态:提高程序的可扩展性和可维护性
- 简单地说,多态就是“具有多种形态”,它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用对象的类型,动态地决定调用哪个对象中的方法
面试题
静态语言(java)和动态语言(Python)关于多态的区别
- 静态语言实现多态的三个必要条件
- 继承
- 方法重写
- 父类引用指向子类对象
- 动态语言的多态崇尚“鸭子类型”,在鸭子类型中,不需要关心是什么类型,到底是不是鸭子,只关心对象的行为
方法重写(继承)
- 如果子类对继承自父类的某个属性或方法不满意时,可以在子类中对其(方法体)进行重新编写
- 子类重写后的方法中可以通过
super().xxx()调用父类中被重写的方法
class animal:
def __init__(self, name, color):
self.name = name
self.color = color
def info(self):
print(self.name, self.color)
class reptile(animal):
def __init__(self, name, color, length):
super().__init__(name, color)
self.length = length
def info(self):
super().info()
print('长度:', self.length)
animal01 = reptile("蜥蜴", "绿色", 0.5)
animal01.info()
object类
- Object类是所有类的父类,因此所有的类都有object类的属性和方法
- 内置的dir()函数可以查指定对象的所有属性
- Object有一个
__ str __()方法,用于返回一个对于“对象的描述”,对应于内置函数str()经常用于print()方法,帮我们查看对象的信息,所以我们经常会对__ str __()进行重写
class animal:
def __init__(self, name, color):
self.name = name
self.color = color
def info(self):
print(self.name, self.color)
def __str__(self):
return 'animal{0}{1}'.format(self.name, self.color)
class reptile(animal):
def __init__(self, name, color, length):
super().__init__(name, color)
self.length = length
def info(self):
super().info()
print('长度:', self.length)
animal01 = reptile("蜥蜴", "绿色", 0.5)
print(dir(animal01))
print(animal01) # 默认会调用__str__方法
特殊方法和特殊属性
| 名称 | 描述 | |
|---|---|---|
| 特殊属性 | __ dict __ | 获得类对象或实例对象所绑定的所有属性和方法的字典 |
| __ class __ | 输出对象所属的类 | |
| __ base __ | 输出第一个父类类型所属的类(所有基类) | |
| __ bases __ | 输出所有父类类型所属的类(第一个基类) | |
| __ mor __ | 输出类的层次结构 | |
| __ subclasses __ | 输出所有子类类型所属的类 | |
| 特殊方法 | __ len __ | 通过重写__ len __()方法,可以让内置函数len()的参数可以是自定义类型 |
| __ add __ | 通过重写__ add __()方法,可使用自定义对象具有“+”功能 | |
| __ new __ | 用于创建对象 | |
| __ init __ | 对创建的对象进行初始化 |
class Student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def __add__(self, other):
return self.score + other.score
def __len__(self):
print(self)
return len(self.name)
student1 = Student('张三', 18, 100)
student2 = Student('李四人', 20, 90)
print(student1.score,student2.score)
score1 = int(student1.score) + int(student2.score) # 两个对象的成绩相加(因为在类中定义了__add__方法,所以可以直接相加)
score2 = (int(student1.score)).__add__(int(student2.score))
print(score1,score2)
print(len(student1.name),student2.name.__len__()) # 打印学生姓名的长度(因为在类中定义了__len__方法,所以可以直接调用len方法)
class Student:
def __new__(cls, *args, **kwargs):
print('__new__被调用执行了,cls的id值为{0}'.format(id(cls))) #3.__new__被调用执行了,cls的id值为1753397861392
obj = super().__new__(cls)
print('创建的对象的id为:{0}'.format(id(obj))) #4.创建的对象的id为:1753398456400
return obj
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
print('__init__被调用执行了,self的id值为{0}'.format(id(self)))# 5.__init__被调用执行了,self的id值为1753398456400
print('object这个类对象的id为:{0}'.format(id(object))) #1.object这个类对象的id为:140718958743024
print('Student这个类对象的id为:{0}'.format(id(Student))) #2.Student这个类对象的id为:1753397861392
student1 = Student('张三', 20, 100)
print('student1这个对象的id为:{0}'.format(id(student1))) #6.student1这个对象的id为:1753398456400
类的浅拷贝和深拷贝
变量的赋值操作
- 只是形成俩个变量,实际上还是指向同一个对象
浅拷贝
- Python拷贝一般都是浅拷贝,拷贝时,对象包含的子对象内容不拷贝,因此,源对象与拷贝对象会引用同一个子对象
深拷贝
- 使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象,源对象和拷贝对象的所有子对象也不相同
class CPU:
def __init__(self, model, frequency, core):
self.model = model
self.frequency = frequency
self.core = core
def show(self):
print("CPU型号:", self.model)
print("CPU频率:", self.frequency)
print("CPU核数:", self.core)
class Disk:
def __init__(self, model, capacity):
self.model = model
self.capacity = capacity
def show(self):
print("硬盘型号:", self.model)
print("硬盘容量:", self.capacity)
class Computer:
def __init__(self, cpu, disk):
self.cpu = cpu
self.disk = disk
def show(self):
print("电脑CPU信息:")
self.cpu.show()
print("电脑硬盘信息:")
self.disk.show()
#类对象的赋值操作
cpu1 = CPU("Intel i5", "3.5GHz", 4)
cpu2 =cpu1
print(cpu1,cpu2) # 两个变量指向同一个对象<__main__.CPU object at 0x000001A703CC3A10> <__main__.CPU object at 0x000001A703CC3A10>
#类对象的浅拷贝
disk = Disk("三星", "1T")#创建一个Disk对象
computer1 = Computer(cpu1, disk)#computer1对象的cpu属性指向cpu1对象,disk属性指向disk对象
import copy
computer2 = copy.copy(computer1)#浅拷贝,computer2对象的cpu属性指向cpu1对象,disk属性指向disk对象
print(computer1, computer1.cpu,computer1.disk) #<__main__.Computer object at 0x000001A703CC3B50> <__main__.CPU object at 0x000001A703CC3A10> <__main__.Disk object at 0x000001A703CC3A90>
print(computer2, computer2.cpu,computer2.disk) #<__main__.Computer object at 0x000001A703CC3B80> <__main__.CPU object at 0x000001A703CC3A10> <__main__.Disk object at 0x000001A703CC3A90>
#类对象的深拷贝
computer3 = copy.deepcopy(computer1)#深拷贝,computer3对象的cpu属性指向cpu1对象,disk属性指向disk对象
print(computer3, computer3.cpu,computer3.disk) #<__main__.Computer object at 0x000001A703CC3C10> <__main__.CPU object at 0x000001A703CC3A50> <__main__.Disk object at 0x000001A703CC3AF0>
模块化
模块的定义
- 模块的应为为
Modules - 函数与模块的关系
- 一个模块中可以包含N多个函数
- 在Python中一个扩展名为.py的文件就是一个模块
- 使用模块的好处
- 方便其他程序和脚本的导入并使用
- 避免函数名和变量名的冲突
- 提高代码的可维护性
- 提高代码的可重用性
自定义模块
- 创建自定义模块
- 新建一个.py文件,名字尽量不要与Python自带的标准模块名称相同
- 导入模块
import 模块名称 [as 别名]
from 模块名称 import 函数/变量/类
以主程序的形式执行
- 以主程序形式运行程序
- 在每个模块的定义中都包括一个记录模块名称的变量__ name __ ,程序可以检查到该变量,已确定他们在哪个模块中执行。如果一个模块不是被导入到其他程序中执行,那么它可能在解释器的顶级模块中执行。顶级模块的__ name __ 变量的值为__ main __
if __name__ == '__main__':
print(add(1, 2)) #只有在当前模块中才会执行
python中的包
包时一个分层次的一个目录,它将一组功能相近的模块组织在一个目录下
作用
- 代码规范
- 避免模块名称的冲突
包与目录的区别
- 包含__ init __.py文件的目录称为包
- 目录里通常不包含__ init __.py文件
包的导入
import 包名.模块名导入带有包的模块时注意事项
- 使用import方式进行导入时,只能导入包名或者模块名
- 使用form...import导入时可以导入函数/变量/模块名
python中常用的内置模块
| 模块名 | 描述 |
|---|---|
| sys | 与Python解释器及其环境操作相关的标准库 |
| time | 提供与时间相关的各种函数的标准库 |
| os | 提供了访问操作系统服务功能的标准库 |
| calendar | 提供了与日期相关的各种函数的标准库 |
| urllib | 用于读取来自网上(服务器)的标准数据库 |
| json | 用于使用JSON序列化和反序列化对象 |
| re | 用于在字符串中执行正则表达式匹配和替换 |
| math | 提供标准运算函数的标准库 |
| decimal | 用于进行精确控制运算精度,有效数位和四舍五入操作的十进制运算 |
| logging | 提供了灵活的记录事件,错误,警告和调试信息等日志信息的功能 |
第三方模块的安装与使用
第三方模块的安装
pip install 模块名使用
import 模块名
文件
编码格式的介绍
- 常见的字符编码格式
- Python的解释器使用的是Unicode(内存)
- .py文件在磁盘上使用的UTF-8存储(外存)
- 常见的编码
- ASCII:7位表示1个字符,最高位是0,只能表示128字符
- ISO8859-1:8位表示1个字符,能表示256个字符,兼容ASCII
- Unicode:字长编码,2个字节表示1个字符,不兼容ISO8859-1
- GB2312,GBK,GB18030兼容ISO8859-1英文1个字节,汉字2个字节
- UTF-8:变长编码,1-4个字节表示一个1个字符,英文1个字节,汉字3个字节,UTF-8是基于Unicode实现的
文件的读写原理
- 文件的读写俗称“IO操作”
- 文件读写操作流程
- 操作原理
- python操作文件——》打开或新建文件——》读写文件——》关闭资源
文件的读写操作
内置函数open()函数创建文件对象
语法
file = open(filename [,mode,encoding]) file = open('a.txt', 'r' ,encoding='utf-8') print(file.readlines()) # 读取所有行['中国\n', '美丽'] print(file.readline()) # 读取一行 file.close()
常用的文件打开模式
- 文件类型
- 按文件中数据的组织形式,文件可以分为以下俩大类
- 文本文件:存储的是普通“字符”文本,默认为unicode字符集,可以使用记事本程序打开
- 二进制文件:把数据中内容用“字节”进行存储,无法用记事本打开,必须使用专业的软件打开,eg:mp3,jpg,doc
- 按文件中数据的组织形式,文件可以分为以下俩大类
| 打开模式 | 描述 |
|---|---|
| r | 以只读模式打文件,文件的指针会放在文件的开头 |
| w | 以只写模式打开文件,如果文件不存在则创建文件,如果文件存在,则覆盖原有内容,文件的指针在文件的开头 |
| a | 以追加模式打开文件,如果文件不存在则创建文件,文件指针在文件开头,如果文件存在,则在文件末尾追加内容,文件指针在原文件末尾 |
| b | 以二进制方式打开文件,不能单独使用,需要与其他模式一起使用,rb或wb |
| + | 以读写方式打开文件,不能单独使用,需要与其它模式一起使用,a+ |
文件对象的常用的方法
| 方法名 | 说明 |
|---|---|
| read([size]) | 从文件中读取size个字节或者字符的内容返回,若省略size,则读取到文件末尾,即一次读取文件的所有内容 |
| readline() | 从文本文件中读取第一行内容 |
| readlines() | 把文本文件中每一行都作为独立的字符串对象,并将这些对象放入列表返回 |
| write(str) | 将字符串str内容写入文件 |
| writelines(s_list) | 将字符串列表s_list写入文本文件,不添加换行符 |
| seek(offset,[,whence]) | 把文件指针移动到新的位置,offset表示相对于whence的位置:offset:为正往结束方向移动,为负往开始方向移动,whence不同的值代表不同含义,0:从文件头开始计算,1:从当前位置开始计算,2:从文件尾开始计算 |
| tell() | 返回文件指针的位置 |
| flush() | 把缓存区的内容写入文件,但不关闭文件 |
| close() | 把缓存区的内容写入文件,同时关闭文件,释放文件对象相关资源 |
with语句(上下文管理器)
with语句可以自动管理上下文资源,不论什么原因跳出with块,都能确保文件正确的关闭,以此来达到释放资源的目的
语法
with 上下文表达式 as 格式化文件名 with open('b.txt', 'r', encoding='utf-8') as file: print(file.read())原理
""" MyContentMgr类中定义了__enter__和__exit__方法,这个类遵守了上下文管理器协议,这个类对象的实例对象称为上下文管理器 """ class MyContentMgr: def __enter__(self): print('enter方法被调用执行了') return self def __exit__(self, exc_type, exc_val, exc_tb): print('exit方法被调用执行了') def show(self): print('show方法被调用执行了') with MyContentMgr() as file: file.show() """ enter方法被调用执行了 show方法被调用执行了 exit方法被调用执行了 """图片的复制
# 1.打开文件 with open('logo1.png', 'rb') as file1: # 2.读取文件 with open('logo2.png', 'wb') as file2: # 3.写入文件 file2.write(file1.read())
目录操作
- OS模块式Python内置的与操作系统功能和文件系统相关的模块,该模块中的语句的执行结果通常与操作系统有关,在不同的操作系统上运行,得到的结果可能不一样。
- OS模块与os.path模块用于对目录或文件进行操作。
#OS模块是Python内置的与操作系统功能和文件系统相关的模块
import os
os.system('notepad.exe') #执行记事本打开
# os.system('calc.exe') #执行计算器打开
#直接调用可执行文件
os.startfile('C:\Users\pinia\Desktop\python\python\pageage\demo1.py')
os模块操作目录相关函数
| 函数 | 说明 |
|---|---|
| getcwd() | 返回当前的工作目录 |
| listdir(path) | 返回指定路径下的文件和目录信息 |
| mkdir(path[,mode]) | 创建目录 |
| makedirs(path1/path2...[,mode]) | 创建多级目录 |
| rmdir(path) | 删除目录 |
| removedirs(path1/path2...) | 删除多级目录 |
| chdir(path) | 将path设置为当前工作目录 |
| walk(path) | 深层遍历文件目录 |
os.path模块操作目录相关函数
| 函数 | 说明 |
|---|---|
| adspath(path) | 用于获取文件或目录的绝对路径 |
| exists(path) | 用于判断文件或目录是否存在,如果存在返回True,否则返回False |
| join(path,name) | 将目录与目录或者文件名拼接起来 |
| split() | 分离目录名与文件名 |
| splitext() | 分离文件名和扩展名 |
| basename(path) | 从一个目录中提取文件名 |
| dirrname(path) | 从一个路径中提取文件路径,不包括文件名 |
| isdir(path) | 用于判断是否为路径 |