目录
01函数
1.1,函数的概述
- 函数也是一个对象
- 对象是内存中专门用来存储数据的一块区域
- 函数可以用来保存一些可执行的代码,并且可以在需要时,对这些语句进行多次的调用
- 创建函数:
def 函数名([形参1,形参2,...形参n]) :
代码块
- 函数名必须要符号标识符的规范:(可以包含字母、数字、下划线、但是不能以数字开头)
- 函数中保存的代码不会立即执行,需要调用函数代码才会执行
- 定义函数一般都是要实现某种功能的
1.2,函数的参数
- 在定义函数时,可以在函数名后的()中定义数量不等的形参, 多个形参之间使用
,
隔开 - 形参(形式参数),定义形参就相当于在函数内部声明了变量,但是并不赋值
- 实参(实际参数)
- 如果函数定义时,指定了形参,那么在调用函数时也必须传递实参,实参将会赋值给对应的形参,简单来说,有几个形参就得传几个实参
- 实参的传递方式:位置参数和关键字参数
- 位置参数:就是将对应位置的的实参传递给对应位置的形参,也就是第一个实参传递给第一个形参,第二个实参传递给第二个形参,以此类推,不需要给形参预先指定默认值
def function1(a,b,c):
print('a=',a);
print('b=',b)
print('c=',c)
function1(1,2,3)
- 关键字参数:可以不按照形参定义的顺序传递直接根据参数名来传递参数,使用关键字参数的时候要给形参预先赋默认认值
def function2(a=0,b=0,c=0):
print('a=',a);
print('b=',b)
print('c=',c)
function2(1,c=2)
- 不定长参数:在定义函数时,可以在形参前面加上一个
*
号,这样这个形参将会获取到所有实参,将它保存到一个元组中,带*
的形参只能接受位置型参数
注意:如果有不定长参数,一定要放到最后
特殊情况:如果不放在最后,带*
的参数后面的所有参数,必须以关键字参数的形式传递
def function2(a, b, c):
def function2(*a):
def function2(*a, b=0, c=0):
def function2(a, *b, c=0):
def function2(a, b, *c):
**
形参,可以接收其他的关键字参数,它会将这些参数统一保存到一个字典中,注意:**
形参必须写在所有参数的最后,并且只能有一个
1.3,参数的解包(序列参数)
- 传递实参时,也可以在序列类型的参数前添加星号
*
,这样它会自动将序列中的元素依次作为参数传递,这里要求序列中元素的个数必须和形参的个数的一致。 - 通过
**
来对一个字典进行解包操作
# 参数的解包(拆包)
def function(a,b,c):
print('a =',a)
print('b =',b)
print('c =',c)
# 创建一个元组
t = (10,20,30)
function(*t)
# 创建一个字典
d = {'a':100,'b':200,'c':300}
# 通过 **来对一个字典进行解包操作
function(**d)
1.4,函数的返回值
- 返回值,返回值就是函数执行以后返回的结果
- 通过 return 来指定函数的返回值
- 当一个函数有返回值的时候,可以通过一个变量来接收函数的返回值
- 如果仅仅写一个return 或者 不写return,则相当于return None
- return后的代码都不会执行,return 一旦执行函数自动结束
# 函数的返回值
def function(a):
print('a =',a)
return a
b=function(521) # 使用变量接受函数的返回值
print(b)
1.5,文档字符串
- 文档字符串:就是函数的说明
help()
函数:Python中的内置函数
通过help()
函数可以查询python中的函数的用法
语法:help(函数对象)
- 文档字符串,在定义函数时,可以在函数内部编写文档字符串,文档字符串就是函数的说明, 当我们编写了文档字符串时,就可以通过
help()
函数来查看函数的说明 - 文档字符串编写非常简单,其实直接在函数的第一行写一个字符串就是文档字符串
- 注意:参数列表中冒号后面的变量类型仅是提示作用,并不起到限定作用,箭头后面是提示返回值类型,也不起到限定作用。
def function(a:int,b:bool,c:str='hello') -> int:
'''
这是一个文档字符串的示例
函数的作用:。。。。。
函数的参数:
a,作用,类型,默认值。。。。
b,作用,类型,默认值。。。。
c,作用,类型,默认值。。。。
'''
return 10
help(function)
1.6,作用域(scope)
- 作用域(scope):作用域指的是变量生效的区域
- 在Python中一共有两种作用域
- 全局作用域:
全局作用域在程序执行时创建,在程序执行结束时销毁
所有函数以外的区域都是全局作用域
在全局作用域中定义的变量,都属于全局变量,全局变量可以在程序的任意位置被访问 - 函数作用域
函数作用域在函数调用时创建,在调用结束时销毁
函数每调用一次就会产生一个新的函数作用域
在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问 - 变量的查找
当我们使用变量时,会优先在当前作用域中寻找该变量,如果有则使用,如果没有则继续去上一级作用域中寻找,如果有则使用,如果依然没有则继续去上一级作用域中寻找,以此类推,直到找到全局作用域,依然没有找到,则会抛出异常:NameError: name 'a' is not defined
- 注意:如果局部变量和全局变量同名,当函数调用时,会首先找到局部变量而不是全局变量,如果想要在函数内部修改全局变量的值要使用关键字:
global
a = 20
def function():
a = 10 # 修改局部变量
print('函数内部:','a =',a)
def function2():
global a
a = 10 # 修改全局变量
print('函数内部:','a=',a)
function()
print('函数外部:','a =',a)
function2()
print('函数外部:','a =',a)
运行结果:
函数内部: a = 10
函数外部: a = 20
函数内部: a= 10
函数外部: a = 10
1.7,命名空间(了解)
- 命名空间(namespace):指的是变量存储的位置,每一个变量都需要存储到指定的命名空间当中
- 每一个作用域都会有一个它对应的命名空间
- 全局命名空间,用来保存全局变量。函数命名空间用来保存函数中的变量
- 命名空间实际上就是一个字典,是一个专门用来存储变量的字典
locals()
函数:用来获取当前作用域的命名空间
如果在全局作用域中调用locals()
则获取全局命名空间
如果在函数作用域中调用locals()
则获取函数命名空间globals()
函数:可以用来在任意位置获取全局命名空间
# 获取命名空间
scope=locals()
print(type(scope))
print(scope)
# 向scope中添加一个key-value,相当于在全局中创建了一个变量(一般不建议这么做)
scope['c'] = 1000
print(c)
def function():
a = 10
# 在函数内部调用locals()会获取到函数的命名空间
scope = locals()
# 可以通过scope来操作函数的命名空间(不建议这么做)
scope['b'] = 20
#globals() #函数可以用来在任意位置获取全局命名空间
global_scope = globals()
# print(global_scope['a'])
global_scope['a'] = 30
# print(scope)
function()
1.8,高阶函数(了解)
1.8.1,高阶函数概述
- 高阶函数:接收函数作为参数,或者将函数作为返回值的函数是高阶函数
- 当我们使用一个函数作为参数时,实际上是将指定的代码传递进了目标函数
# 创建一个列表
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def function(func, lst):
'''
fn()函数可以将指定列表中的所有偶数获取出来,并保存到一个新列表中返回
参数:
lst:要进行筛选的列表
'''
# 创建一个新列表
new_list = []
# 对列表进行筛选
for n in lst:
# 判断n的奇偶
if func(n):
new_list.append(n)
return new_list # 返回新列表
# 定义一个函数,用来检查一个任意的数字是否是偶数
def function1(i):
if i % 2 == 0:
return True
return False
# 定义一个函数,用来检查一个任意数是否是3的倍数
def function2(i):
return i % 3 == 0
new_list1=function(function1,list)
print('筛选偶数:',new_list1)
new_list2=function(function2,list)
print('筛选3的倍数:',new_list2)
运行结果:
筛选偶数: [2, 4, 6, 8, 10]
筛选3的倍数: [3, 6, 9]
1.8.2,Python内置函数:filter
-
filter()
函数:Python内置函数,可以从序列中过滤出符合条件的元素,保存到一个新的序列中
参数:
1.函数,根据该函数来过滤序列(可迭代的结构)
2.需要过滤的序列(可迭代的结构)
返回值:
过滤后的新序列(可迭代的结构) -
注意:filter返回的是一个可迭代的结构,不能直接打印
# 创建一个列表
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 定义一个函数,用来检查一个任意的数字是否是偶数
def function1(i):
if i % 2 == 0:
return True
return False
# function1是作为参数传递进filter()函数中
# 而function1实际上只有一个作用,就是作为filter()的参数
# filter()调用完毕以后,function1就已经没用
# 注意:filter返回的是一个可迭代的结构,不能直接打印
print(list(filter(function1,l)))
运行结果:
[2, 4, 6, 8, 10]
1.8.3,浅谈lambda表达式
- 匿名函数
lambda表达式
:(语法糖)
lambda函数表达式专门用来创建一些简单的函数,它是函数创建的又一种方式
语法:lambda 参数列表 : 返回值
- 匿名函数一般都是作为参数使用,其他地方一般不会使用
# 创建一个列表
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 此lambda表达式为求取两个数的和
num=(lambda a,b : a + b)(10,20)
print(num)
# 也可以将匿名函数赋值给一个变量,一般不会这么做
function2 = lambda a, b: a + b
print(function2(10,30))
# 此lambda表达式:lambda i: i%2==0,获取偶数
r = filter(lambda i: i%2==0, l)
# 将r转换为列表并打印出来
print(list(r))
1.8.4,Python内置函数:map
map()
函数:Python内置函数
对可跌代对象中的所有元素做指定的操作,然后将其添加到一个新的对象中返回。
例如对列表中的所有元素都取平方,然后返回新的列表
# 创建一个列表
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
r = map(lambda i : i ** 2 , l)
print(list(r))
运行结果:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
1.8.5,Python内置函数:sort
sort()
函数:Python内置函数
该方法用来对列表中的元素进行排序
sort()方法默认是直接比较列表中的元素的大小
在sort()可以接收一个关键字参数key,需要一个函数作为参数,当设置了函数作为参数,每次都会以列表中的一个元素作为参数来调用函数,并且使用函数的返回值来比较元素的大小
l = ['bb','aaaa','c','ddddddddd','fff']
l.sort()
print('默认排序:',l)
# 使用长度进行排序
l.sort(key=len)
print('使用长度排序',l)
l = [2,5,'1',3,'6','4']
l.sort(key=int)
print('int排序:',l)
1.8.6,Python内置函数:sorted
sorted()
函数:Python内置函数
此函数和sort()的用法基本一致,但是sorted()可以对任意的序列进行排序,并且使用sorted()排序不会影响原来的对象,而是返回一个新对象
l = [2,5,'1',3,'6','4']
print('排序前:',l)
print(sorted(l,key=int))
print('排序后:',l)
1.8.7,闭包
- 将函数作为返回值返回,也是一种高阶函数,这种高阶函数我们叫做闭包
- 通过闭包可以创建一些只有当前函数能访问的变量,以将一些私有的数据藏到的闭包中
def function():
a = 10
# 函数内部再定义一个函数
def inner():
print('我是inner' , a)
# 将内部函数 inner作为返回值返回
return inner
# r是一个函数,是调用function()后返回的函数
# 这个函数是在function()内部定义,并不是全局函数
# 所以这个函数总是能访问到function()函数内的变量
r = function()
r()
运行结果:
我是inner 10
- 形成闭包的要件
① 函数嵌套
② 将内部函数作为返回值返回
③ 内部函数必须要使用到外部函数的变量
1.8.8,装饰器
- 当一个函数功能不能满足我们的需求,我们就要对其进行修改,我们可以直接通过修改函数中的代码,但是会产生以下一些问题
① 如果要修改的函数过多,修改起来会比较麻烦
② 并且不方便后期的维护
③ 并且这样做会违反开闭原则(OCP) - 程序的设计要求,对程序的扩展开放,对程序的修改关闭
- 所以我们希望在不修改原函数的情况下,来对函数进行扩展
# 我们希望在不修改原函数的情况下,来对函数进行扩展
def function():
print('我是fn函数....')
# 只需要根据现有的函数,来创建一个新的函数
def function2():
print('函数开始执行~~~')
function()
print('函数执行结束~~~')
function2()
- 上边的方式,已经可以在不修改源代码的情况下对函数进行扩展了但是,这种方式要求我们每扩展一个函数就要手动创建一个新的函数,实在是太麻烦了,为了解决这个问题,我们创建一个函数,让这个函数可以自动的帮助我们生产函数
def add(a,b):
return a+b
# 用来对其他函数进行扩展,参数:要扩展的函数对象
def begin_end(old):
# 创建一个新函数:参数为可变参数
def new_function(*args , **kwargs):
print('开始执行~~~~')
# 调用被扩展的函数
result = old(*args , **kwargs)
print('执行结束~~~~')
# 返回函数的执行结果
return result
# 返回新函数
return new_function
f = begin_end(add) # f为我们想要的函数
r = f(123,456)
print(r)
- 像
begin_end()
这种函数我们就称它为装饰器 - 通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展
- 在定义函数时,可以通过
@装饰器
,来使用指定的装饰器,来装饰当前的函数 - 可以同时为一个函数指定多个装饰器,这样函数将会按照从内向外的顺序被装饰。
- 实例:使用
function1和function2
为装饰器,对say_hello
进行增强
def function1(old):
# 创建一个新函数
def new_function(*args , **kwargs):
print('func1函数装饰~开始执行~~~~')
# 调用被扩展的函数
result = old(*args , **kwargs)
print('func1函数装饰~执行结束~~~~')
# 返回函数的执行结果
return result
# 返回新函数
return new_function
def function2(old):
# 创建一个新函数
def new_function(*args , **kwargs):
print('func2函数装饰~开始执行~~~~')
# 调用被扩展的函数
result = old(*args , **kwargs)
print('func2函数装饰~执行结束~~~~')
# 返回函数的执行结果
return result
# 返回新函数
return new_function
# function1和function2为装饰器,对say_hello进行增强
@function1
@function2
def say_hello():
print('大家好~~~')
say_hello()
运行结果:
func1函数装饰~开始执行~~~~
func2函数装饰~开始执行~~~~
大家好~~~
func2函数装饰~执行结束~~~~
func1函数装饰~执行结束~~~~
02,面向对象(类)
2.1,什么是对象?
- 对象是内存中专门用来存储数据的一块区域。
- 对象中可以存放各种数据(比如:数字、布尔值、代码)
- 对象由三部分组成:
1.对象的标识(id)
2.对象的类型(type)
3.对象的值(value)
2.2,什么是面向对象(oop)?
-
Python是一门面向对象的编程语言
-
所谓的面向对象的语言,简单理解就是语言中的所有操作都是通过对象来进行的
-
面向过程的编程的语言,面向过程指将我们的程序的逻辑分解为一个一个的步骤,通过对每个步骤的抽象,来完成程序
-
面向过程的编程思想将一个功能分解为一个一个小的步骤, 我们通过完成一个一个的小的步骤来完成一个程序, 这种编程方式,符合我们人类的思维,编写起来相对比较简单, 但是这种方式编写代码的往往只适用于一个功能, 如果要在实现别的功能,即使功能相差极小,也往往要重新编写代码,所以它可复用性比较低,并且难于维护
-
面向对象的编程语言,面向对象的编程语言,关注的是对象,而不关注过程。
-
对于面向对象的语言来说,一切都是对象
2.3,类
2.3.1,类的概述
- 我们目前所学习的对象都是Python内置的对象
- 但是内置对象并不能满足所有的需求,所以我们在开发中经常需要自定义一些对象
- 类,简单理解它就相当于一个图纸。在程序中我们需要根据类来创建对象
- 类就是对象的图纸!
- 我们也称对象是类的实例(instance)
- 如果多个对象是通过一个类创建的,我们称这些对象是一类对象
- 像
int() float() bool() str() list() dict() ....
这些都是类,a = int(10)
创建一个int类的实例 等价于 a = 10 - 我们自定义的类都需要使用大写字母开头,使用大驼峰命名法(帕斯卡命名法)来对类命名
- 类也是一个对象, 类就是一个用来创建对象的对象!
2.3.2,如何定义一个类?
- 类和对象都是对现实生活中的事物或程序中的内容的抽象
- 实际上所有的事物都由两部分构成:
1.数据(属性)
2.行为(方法) - 在类的代码块中,我们可以定义变量和函数,
变量:为该类实例的公共属性,所有的该类实例都可以通过对象.属性名
的形式访问
函数:为该类实例的公共方法,所有该类实例都可以通过对象.方法名()
的形式调用方法 - 注意:
方法调用时,第一个参数由解析器自动传递,所以定义方法时,至少要定义一个形参! - 属性和方法查找的流程:
当我们调用一个对象的属性时,解析器会先在当前对象中寻找是否含有该属性,如果有,则直接返回当前的对象的属性值,如果没有,则去当前对象的类对象中去寻找,如果有则返回类对象的属性值,如果类对象中依然没有,则报错! - 类对象和实例对象中都可以保存属性(方法)
- 如果这个属性(方法)是所有的实例共享的,则应该将其保存到类对象中
- 如果这个属性(方法)是某个实例独有,则应该保存到实例对象中
- 一般情况下,属性保存到实例对象中,而方法需要保存到类对象中
- 类的定义模板:
class 类名([父类]) :
公共的属性...
# 对象的初始化方法
def __init__(self,...):
...
# 其他的方法
def method_1(self,...):
...
def method_2(self,...):
...
- 实用类创建对象的流程
p1 = Person()的运行流程
1.创建一个变量
2.在内存中创建一个新对象
3.__init__(self)方法执行
4.将对象的id(内存地址)赋值给变量
- 方法调用时,第一个参数由解析器自动传递,所以定义方法时,至少要定义一个形参! ,这个形参的名字可以随便起,但是一般我们都将将其命名为
self
。
第一个参数,就是调用方法的对象本身
如果是p1调的,则第一个参数就是p1对象
如果是p2调的,则第一个参数就是p2对象
# 定义一个Person类
class Person():
# 定义公共属性
name='AISMALL'
# 定义公共方法
def say_hello(self):
print('我是',self.name)
p1=Person()
p2=Person()
p1.name='aismall' # 修改属性值
p1.say_hello()
p2.say_hello()
运行结果:
我是 aismall
我是 AISMALL
2.3.3,对象的初始化
- 在上面的例子中,对于Person类来说name属性是必须要有的,并且每一个对象中的name属性基本上都是不同的
如果我们在定义类的时候给name属性赋一个默认值,我们在创建对象的时候如果忘记修改name属性就会有很多对象的name属性值相同(为默认值)
如果我们在定义对象以后,手动添加name到对象中,这种方式很容易出现错误
所以,我们希望,在创建对象时,必须设置name属性,如果不设置对象将无法创建,并且属性的创建应该是自动完成的,而不是在创建对象以后手动完成,这就是对象的初始化 - 如何实现对象的初始化?
使用Python的魔术方法(特殊方法),类比java或者C++中的有参构造方法 - 何为特殊方法?
特殊方法都是以__开头,__结尾
的方法
特殊方法不需要我们自己调用(能调用但是不需要调用)
特殊方法将会在特殊的时刻自动调用 - 学习特殊方法:
1.特殊方法什么时候调用
2.特殊方法有什么作用
创建对象的流程:
p1 = Person()的运行流程
1.创建一个变量
2.在内存中创建一个新对象
3.__init__(self)方法执行
4.将对象的id赋值给变量
init方法的作用:
init会在对象创建以后自动执行
init可以用来向新创建的对象中初始化属性
调用类创建对象时,类后边的所有参数都会依次传递到init()中
- 例子
# 定义一个Person类
class Person():
# 定义魔术方法(构造器)
def __init__(self,name):
self.name=name
def say_hello(self):
print('我是',self.name)
# 创建对象的时候初始化对象
# p1=Person() # 这种创建会报错
p1=Person('AISMALL')
print(p1.name)
p1.say_hello()
运行结果:
AISMALL
我是 AISMALL
2.3.4,封装—类的三大特性之一
- 封装:指的是隐藏对象中一些不希望被外部所访问到的属性或方法,类比java和C++中的私有属性
- 如何隐藏一个对象中的属性?
将对象的属性名,修改为一个外部不知道的名字 - 如何获取对象中的隐藏属性?
需要提供一个getter和setter方法
使外部可以访问到隐藏属性
getter 获取对象中的指定属性(get_属性名)
setter 用来设置对象的指定属性(set_属性名) - 使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性
1.隐藏了属性名,使调用者无法随意的修改对象中的属性
2.增加了getter和setter方法,很好的控制的属性是否是只读的,只读的,则直接去掉setter方法,不能被外部访问,则直接去掉getter方法
3.使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
4.使用getter方法获取属性,使用setter方法设置属性
5.可以使用getter方法可以表示一些计算的属性
# 定义一个Person类
class Person():
# 定义魔术方法(构造器)
def __init__(self,name):
# 修改变量名,目的隐藏属性不能被外部访问
self.hidden_name=name
# 提供setter方法用来设置属性值
def set_name(self,name):
self.hidden_name=name
# 提供getter方法用来获取属性值
def get_name(self):
return self.hidden_name
def say_hello(self):
print('我是',self.hidden_name)
# 创建对象的时候初始化对象
p1=Person('AISMALL')
print('修改前:',p1.get_name())
p1.set_name('aismall')
print('修改后:',p1.get_name())
p1.say_hello()
运行结果:
修改前: AISMALL
修改后: aismall
我是 aismall
- 在上面的例子中虽然给属性改了名字,但是依然可以通过对象访问
- 为了使外部无法通过对象直接访问,可以为对象的属性使用双下划线开头,
__xxx
,双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问 - 其实隐藏属性只不过是Python自动为属性改了一个名字,实际上是将名字修改为了,
_类名__属性名
,比如_Person__name
,修改过之后只不过无法直接通过对象访问,但是知道名字还可以访问(防不胜防)。
# 定义一个Person类
class Person():
# 定义魔术方法(构造器)
def __init__(self,name):
# 修改变量名,目的隐藏属性不能被外部访问
self.__name=name
# 提供setter方法用来设置属性值
def set_name(self,name):
self.__name=name
# 提供getter方法用来获取属性值
def get_name(self):
return self.__name
def say_hello(self):
print('我是',self.__name)
# 创建对象的时候初始化对象
p1=Person('AISMALL')
# print(p1.__name) # 调用报错
print(p1._Person__name) # 可以被访问被修改的名字
print('修改前:',p1.get_name())
p1.set_name('aismall')
print('修改后:',p1.get_name())
p1.say_hello()
运行结果:
AISMALL
修改前: AISMALL
修改后: aismall
我是 aismall
2.3.5,继承—类的三大特性之一
2.3.5.1,继承的概述
- 如果有一个类,能够实现我们需要的大部分功能,但是不能实现全部功能
- 如何能让这个类来实现全部的功能呢?
① 直接修改这个类,在这个类中添加我们需要的功能,修改起来会比较麻烦,并且会违反OCP原则
② 直接创建一个新的类,创建一个新的类比较麻烦,并且需要大量的进行复制粘贴,会出现大量的重复性代码
③ 直接来继承它的属性和方法 - 通过继承我们可以使一个类获取到其他类中的属性和方法
- 如何继承一个类?
在定义类时,可以在类名后的括号中指定当前类的父类(超类、基类、super)
被继承的类为父类,集成的类为子类,子类可以直接继承父类中的所有的属性和方法
# 继承
# 定义一个类 Animal(动物)
# 动物会跑,会睡觉:run() sleep()
class Animal:
def run(self):
print('动物会跑~~~')
def sleep(self):
print('动物睡觉~~~')
# 定义一个类 Dog(狗),
# 狗会跑,会睡觉,会叫:run() sleep() bark()
# 为了不重复写会跑和会睡觉的方法,我们使用狗类继承动物类
# 这样狗类就可以使用动物类中的属性和方法
class Dog(Animal):
def bark(self):
print('汪汪汪~~~')
d = Dog()
d.run()
d.sleep()
d.bark()
运行结果:
动物会跑~~~
动物睡觉~~~
汪汪汪~~~
- 从上面的结果可以看出,虽然子类可以访问父类中的属性,但是感觉不太对,我们需要的结果应该是:狗会跑,狗会叫,狗会睡觉
2.3.5.2,重写
- 如何实现上面的结果,就要引出一个新的概念:
重写
如果子类中有和父类同名的方法,则通过子类实例去调用方法时,会调用子类的方法而不是父类的方法,这个特点我们叫做方法的重写(覆盖,override) - 当我们调用一个对象的方法时,会优先去当前对象中寻找是否具有该方法,如果有则直接调用,如果没有,则去当前对象的父类中寻找,如果父类中有则直接调用父类中的方法,如果没有,则去父类的父类中寻找,以此类推,直到找到object,如果依然没有找到,则报错
# 继承
# 定义一个类 Animal(动物)
# 动物会跑,会睡觉:run() sleep()
class Animal:
def run(self):
print('动物会跑~~~')
def sleep(self):
print('动物睡觉~~~')
# 定义一个类 Dog(狗),
# 狗会跑,会睡觉,会叫:run() sleep() bark()
class Dog(Animal):
def run(self):
print('狗会跑~~~~')
def sleep(self):
print('狗会睡觉~~~~')
def bark(self):
print('汪汪汪~~~')
d = Dog()
d.run()
d.sleep()
d.bark()
运行结果:
狗会跑~~~~
狗会睡觉~~~~
汪汪汪~~~
isinstance
:检查一个对象是否属于一某个类
如果这个类是这个对象的父类,也会返回True
在创建类时,如果省略了父类,则默认父类为object,object是所有类的父类,所有类都继承自objectissubclass
:检查一个类是否是另一个类的子类
# 继承
class Animal:
def run(self):
print('动物会跑~~~')
def sleep(self):
print('动物睡觉~~~')
class Dog(Animal):
def bark(self):
print('汪汪汪~~~')
d=Dog()
# isinstance检查一个对象是否属于一某个类
print('d是否为Animal的实例:',isinstance(d , Animal))
print('d是否为Dog的实例:',isinstance(d,Dog))
# issubclass检查一个类是否是另一个类的子类
print('Animal是否为Dog的子类:',issubclass(Animal , Dog))
print('Dog是否为Animal的子类:',issubclass(Dog ,Animal))
print('Animal是否为object的子类:',issubclass(Animal , object))
print('Dog是否为object的子类',issubclass(Dog , object))
运行结果:
d是否为Animal的实例: True
d是否为Dog的实例: True
Animal是否为Dog的子类: False
Dog是否为Animal的子类: True
Animal是否为object的子类: True
Dog是否为object的子类 True
2.3.5.3,super关键字
- 在继承的时候,父类中的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法
- 如何调用父类中方法?
super()
:可以用来获取当前类的父类,可以通过super()来调用父类中的方法,并且通过super()调用父类方法时,不需要传递self
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('动物会跑~~~')
def sleep(self):
print('动物睡觉~~~')
# 装饰器:只用之后可以向调用类属性一样调用类方法
@property
def name(self):
return self._name
# 装饰器:使用此装饰器之前要先有前面的装饰器
@name.setter
def name(self,name):
self._name = name
# 父类中的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法
class Dog(Animal):
def __init__(self,name,age):
# 希望可以直接调用父类的__init__来初始化父类中定义的属性
# super() 可以用来获取当前类的父类,
# 并且通过super()返回对象调用父类方法时,不需要传递self
super().__init__(name)
self._age = age
def bark(self):
print('汪汪汪~~~')
def run(self):
print('狗跑~~~~')
@property
def age(self):
return self._age
@age.setter
def age(self,age):
self._age = age
d = Dog('旺财',18)
print(d.name)
print(d.age)
运行结果:
旺财
18
2.3.5.4,多重继承
- 在Python中是支持多重继承的,也就是我们可以为一个类同时指定多个父类
- 如何多继承?
可以在类名的()后边添加多个类,来实现多重继承 - 多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法
- 在开发中没有特殊的情况,应该尽量避免使用多重继承,因为多重继承会让我们的代码过于复杂
- 如果多个父类中有同名的方法,则会现在第一个父类中寻找,然后找第二个,然后找第三个。。。 前边父类的方法会覆盖后边父类的方法
2.3.6,多态—类的三大特性之一
- 多态:从字面上理解是多种形态
例如:狗(狼狗、藏獒、哈士奇、古牧 。。。) - 一个对象可以以不同的形态去呈现
面向对象的三大特征:
- 封装
确保对象中的数据安全 - 继承
保证了对象的可扩展性 - 多态
保证了程序的灵活性
2.4,类属性,实例属性,类方法,实例方法,静态方法
- 类属性:直接在类中定义的属性是类属性
类属性可以通过类或类的实例访问到, 但是类属性只能通过类对象来修改,无法通过实例对象修改 - 实例属性:通过实例对象添加的属性属于实例属性
实例属性只能通过实例对象来访问和修改,类对象无法访问修改 - 实例方法:在类中定义,以self为第一个参数的方法都是实例方法
实例方法在调用时,Python会将调用对象作为self传入
实例方法可以通过实例和类去调用
当通过实例调用时,会自动将当前调用对象作为self传入
当通过类调用时,不会自动传递self,此时我们必须手动传递self - 类方法:在类内部使用
@classmethod
来修饰的方法属于类方法
类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象
类方法和实例方法的区别,实例方法的第一个参数是self,而类方法的第一个参数是cls
类方法可以通过类和实例调用,没有区别 - 静态方法:在类中使用
@staticmethod
来修饰的方法属于静态方法
静态方法不需要指定任何的默认参数,静态方法可以通过类和实例去调用
静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数
静态方法一般都是一些工具方法,和当前类无关
# 定义一个类
class A(object):
# 类属性
count = 0
def __init__(self):
# 实例属性
self.name = 'AISMALL'
# 实例方法
def test(self):
print('这是实例方法~~~ ', self)
# 类方法
@classmethod
def test_2(cls):
print('这一个类方法~~~ ',cls)
print(cls.count)
# 静态方法
@staticmethod
def test_3():
print('这是一个静态方法~~~')
# 创建类的实例对象
a = A()
print('修改前',A.count)
# 对于类属性,实例和类都可以访问,但是只能通过类来修改
a.count = 10 # 使用实例修改
print('使用实例修改:',a.count)
A.count = 100 # 使用类修改
print('使用类修改:',A.count)
# 实例属性,只能通过实例来访问和修改,类对象无法访问和修改
# print('通过类访问实例属性:,',A.name)
print('通过实例访问实例属性:',a.name)
# 实例方法,类和对象都可以访问,只是类访问需要传递参数(类对象)
# a.test()等价于A.test(a)
a.test()
A.test(a)
# 类方法,类和对象都可以访问
# A.test_2() 等价于 a.test_2()
A.test_2()
a.test_2()
# 静态方法,类和对象都可以访问
A.test_3()
a.test_3()
2.5,垃圾回收
- 就像我们生活中会产生垃圾一样,程序在运行过程当中也会产生垃圾
- 程序运行过程中产生的垃圾会影响到程序的运行的运行性能,所以这些垃圾必须被及时清理
- 在程序中没有被引用的对象就是垃圾,这种垃圾对象过多以后会影响到程序的运行的性能,所以我们必须进行及时的垃圾回收,所谓的垃圾回收就是将垃圾对象从内存中删除
- 在Python中有自动的垃圾回收机制,它会自动将这些没有被引用的对象删除,所以我们不用手动处理垃圾回收
class A:
def __init__(self):
self.name = 'A类'
# del是一个特殊方法,它会在对象被垃圾回收前调用
def __del__(self):
print('A()对象被删除了~~~',self)
a = A()
print(a.name)
# 将a设置为了None,此时没有任何的变量对A()对象进行引用,它就是变成了垃圾
a = None
# del方法手动回收
# del a
input('回车键退出...')
2.6,特殊方法(了解)
- 我们在前面也提到过特殊方法(魔术方法),特殊方法都是使用__开头和结尾的
- 特殊方法的特征:不需要我们手动调用,需要在一些特殊情况下自动执行
object.__new__(cls[, ...])
object.__init__(self[, ...])
object.__del__(self)
object.__repr__(self)
object.__str__(self)
object.__bytes__(self)
object.__format__(self, format_spec)
object.__bool__(self)
object.__hash__(self)
object.__len__(self)
object.__add__(self, other)
object.__sub__(self, other)
object.__mul__(self, other)
object.__matmul__(self, other)
object.__truediv__(self, other)
object.__floordiv__(self, other)
object.__mod__(self, other)
object.__divmod__(self, other)
object.__pow__(self, other[, modulo])
object.__lshift__(self, other)
object.__rshift__(self, other)
object.__and__(self, other)
object.__xor__(self, other)
object.__or__(self, other)
object.__lt__(self, other) 小于 <
object.__le__(self, other) 小于等于 <=
object.__eq__(self, other) 等于 ==
object.__ne__(self, other) 不等于 !=
object.__gt__(self, other) 大于 >
object.__ge__(self, other) 大于等于 >=
# 定义一个Person类
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
# 这个特殊方法会在尝试将对象转换为字符串的时候调用
# 它的作用可以用来指定对象转换为字符串的结果 (print函数)
def __str__(self):
return 'Person [name=%s , age=%d]' % (self.name, self.age)
# 这个特殊方法会在对当前对象使用repr()函数时调用
# 它的作用是指定对象在 ‘交互模式’中直接输出的效果
def __repr__(self):
return 'Hello'
# 这个特殊方法会在将对象装换为布尔值得时候调用
# 它的作用是通过bool来指定对象转换为布尔值的情况
def __bool__(self):
return self.age > 17
# 在对象做大于比较的时候调用,该方法的返回值将会作为比较的结果
# 他需要两个参数,一个self表示当前对象,other表示和当前对象比较的对象
def __gt__(self, other):
return self.age > other.age
# 创建两个Person类的实例
p1 = Person('孙悟空', 18)
p2 = Person('猪八戒', 28)
# 打印象时,实际上打印的是对象的中特殊方法 __str__()的返回值
print(p1) # <__main__.Person object at 0x04E95090>
print(p2)
# repr__()
print(repr(p1))
# __gt__
print(p1 > p2)
# __bool__
print(bool(p1))
# __bool__
if p1 :
print(p1.name,'已经成年了')
else :
print(p1.name,'还未成年了')
2.6,模块(了解)
- 模块可以类比C++里面的分文件编写
- 模块化,模块化指将一个完整的程序分解为一个一个小的模块,通过将模块组合,来搭建出一个完整的程序
- 为什么采用模块化?
不采用模块化,统一将所有的代码编写到一个文件中,不利于修改和维护 - 模块化的优点:
① 方便开发
② 方便维护
③ 模块可以复用 - 在Python中一个py文件就是一个模块,要想创建模块,实际上就是创建一个python文件
- 注意:模块名要符号标识符的规范
- 如何在一个模块中引入外部模块
① import 模块名 (模块名,就是python文件的名字,注意不要py)
② import 模块名 as 模块别名 - 引入模块时的注意事项:
可以引入同一个模块多次,但是模块的实例只会创建一个
import可以在程序的任意位置调用,但是一般情况下,import语句都会统一写在程序的开头
在每一个模块内部都有一个__name__
属性,通过这个属性可以获取到模块的名字
__name__属性值为 __main__的模块是主模块
,一个程序中只会有一个主模块
主模块就是我们直接通过 python 执行的模块 - 访问模块中的变量:
模块名.变量名
import test_module as test
# 获取模块的名字
print(test.__name__)
print(__name__)
- 引入模块的其他方式
假如m 是一个模块
第一种方式:引入模块的全部内容
语法:import m
访问方式:
访问模块中的变量:模块名.变量名
访问模块中的方法:模块名.方法()
访问模块中的类属性:模块名.类名.变量名
访问模块中的类的方法:模块名.类名.方法()
第二种方式:引入模块中的部分内容
语法 from m import 变量,方法,类....
访问方式:
访问模块中的变量:变量
访问模块中的方法:方法()
访问模块中的类属性:类名.变量名
访问模块中的类的方法:类名.方法()
当引入的变量或者方法给当前类中的变量或者方法重名,当前类中的会被覆盖,
为了不被覆盖我们可以使用给引入的变量或者方法起别名的方式
语法:from 模块名 import 变量 as 别名
引入到模块中所有内容,一般不会使用
语法:from m import *
- 测试模块代码编写模板:
# 当模块为主模块的时候才执行
if __name__=='__main__':
代码块
2.7,包(了解)
- 包 (Package):包也是一个模块
- 当我们模块中代码过多时,或者一个模块需要被分解为多个模块时,这时就需要使用到包, 普通的模块就是一个py文件,而包是一个文件夹
- 当我们创建包的时候会生成两个文件:
__init__.py
文件和__pycache__
文件
__init__.py
文件:这个文件中可以包含有包中的主要内容 __pycache__
文件:是模块的缓存文件
py代码在执行前,需要被解析器先转换为机器码,然后再执行,所以我们在使用模块(包)时,也需要将模块的代码先转换为机器码然后再交由计算机执行, 而为了提高程序运行的性能,python会在编译过一次以后,将代码保存到一个缓存文件中,这样在下次加载这个模块(包)时,就可以不再重新编译而是直接加载缓存中编译好的代码即可
# testPackage是一个包,包中有连个py文件:a.py和b.py
from testPackage import a , b
print(a.c)
print(b.d)
2.8,Python标准库(了解)
- Python标准库:就是Python为我们提供的一个一个模块,我们直接可以使用
- 标准库会随Python的安装一同安装
- Python标准库可以在Python参考文档的
Global Module Index
里面查找 - 常用模块介绍
sys模块,它里面提供了一些变量和函数,使我们可以获取到Python解析器的信息
或者通过函数来操作Python解析器
引入sys模块:import sys
pprint 模块,它给我们提供了一个方法 pprint() 该方法可以用来对打印的数据做简单的格式化
引入pprint模块:import pprint
os 模块,让我们可以对操作系统进行访问
引入os模块:import os
- 模块的简单使用:
import sys
import pprint
import os
# sys.argv
# 获取执行代码时,命令行中所包含的参数
# 该属性是一个列表,列表中保存了当前命令的所有参数
print(sys.argv)
# sys.modules
# 获取当前程序中引入的所有模块
# modules是一个字典,字典的key是模块的名字,字典的value是模块对象
# print(sys.modules) # 直接打印
# pprint.pprint(sys.modules)
# sys.path
# 它是一个列表,列表中保存的是模块的搜索路径
# pprint.pprint(sys.path)
# sys.platform
# 表示当前Python运行的平台
# print(sys.platform)
# sys.exit()
# 函数用来退出程序
# sys.exit('程序出现异常,结束!')
# os.environ
# 通过这个属性可以获取到系统的环境变量
pprint.pprint(os.environ['path'])
# os.system()
# 可以用来执行操作系统的名字
os.system('dir') # 打开文件夹
#os.system('notepad') #打开记事本
03,异常
3.1,异常的概述
- 何为异常:
程序在运行过程当中,不可避免的会出现一些错误,
比如:使用了没有赋值过的变量,使用了不存在的索引,除0,这些错误在程序中,我们称其为异常。 - 程序运行过程中,一旦出现异常将会导致程序立即终止,异常以后的代码全部都不会执行!
print('hello')
print(10/0)
print('你好')
3.2,如何处理异常
- 程序运行时出现异常,目的并不是让我们的程序直接终止, Python是希望在出现异常时,我们可以编写代码来对异常进行处理,处理过之后程序可以正常运行。
try语句
try:
代码块(可能出现错误的语句)
except 异常类型 as 异常名:
代码块(出现错误以后的处理方式)
except 异常类型 as 异常名:
代码块(出现错误以后的处理方式)
except 异常类型 as 异常名:
代码块(出现错误以后的处理方式)
else:
代码块(没出错时要执行的语句)
finally:
代码块(该代码块总会执行)
- try是必须的 ,else语句有没有都行, except和finally至少有一个
- 可以将可能出错的代码放入到try语句,这样如果代码没有错误,则会正常执行, 如果出现错误,则会执行expect子句中的代码,这样我们就可以通过代码来处理异常,避免因为一个异常导致整个程序的终止
print('程序运行')
l = []
try:
# print(c)
# l[10]
# 1 + 'hello'
print(10/0)
except NameError:
# 如果except后不跟任何的内容,则此时它会捕获到所有的异常
# 如果在except后跟着一个异常的类型,那么此时它只会捕获该类型的异常
print('出现 NameError 异常')
except ZeroDivisionError:
print('出现 ZeroDivisionError 异常')
except IndexError:
print('出现 IndexError 异常')
except Exception as e :
# Exception 是所有异常类的父类,所以如果except后跟的是Exception,他也会捕获到所有的异常
# 可以在异常类后边跟着一个 as xx 此时xx就是异常对象
print('未知异常',e,type(e))
finally :
print('finally语句:无论是否出现异常,该子句都会执行')
print('运行结束')
3.3,异常的传播
- 当在函数中出现异常时,如果在函数中对异常进行了处理,则异常不会再继续传播,如果函数中没有对异常进行处理,则异常会继续向函数调用处传播,如果函数调用处处理了异常,则不再传播,如果没有处理则继续向调用处传播,直到传递到全局作用域(主模块)如果依然没有处理,则程序终止,并且显示异常信息
- 当程序运行过程中出现异常以后,所有的异常信息会被保存一个专门的异常对象中,而异常传播时,实际上就是异常对象抛给了调用处
比如 :
ZeroDivisionError
类的对象专门用来表示除0的异常
NameError
类的对象专门用来处理变量错误的异常
Python为我们提供了多个异常对象 - 下面这个事例:异常本来在func1中,由于没有处理,逐步被抛出到调用处,在func3出还没有处理,异常被抛出。
def func1():
print('Hello func1')
# print(10/0)
def func2():
print('Hello func2')
func1()
def func3():
print('Hello func3')
func2()
func3()
3.4,抛出异常和自定义异常
- 自定义异常:创建一个类继承Exception即可,Exception是所有异常类的父类
- 抛出异常:使用
raise 语句
来抛出异常,raise语句后需要跟一个异常类 或 异常的实例 - 抛出异常的目的:告诉调用者这里调用时出现问题,希望你自己处理一下
# 也可以自定义异常类,只需要创建一个类继承Exception即可
class MyError(Exception):
pass
def add(a, b):
# 如果a和b中有负数,就向调用处抛出异常
if a < 0 or b < 0:
# raise用于向外部抛出异常,后边可以跟一个异常类,或异常类的实例
# raise Exception
# 抛出异常的目的,告诉调用者这里调用时出现问题,希望你自己处理一下
raise MyError('自定义的异常:有负数抛出')
# 也可以通过if else来代替异常的处理
# return None
r = a + b
return r
print(add(-123, 456))
04,文件
- 通过Python程序来对计算机中的各种文件进行增删改查的操作
- I/O(Input / Output)
- 操作文件的步骤:
① 打开文件
② 对文件进行各种操作(读、写),然后保存
③ 关闭文件
4.1,打开文件
open()
函数:Python内置函数
参数:
file 要打开的文件的名字(路径)
返回值:
返回一个对象,这个对象就代表了当前打开的文件- 如果目标文件和当前文件在同一级目录下,则直接使用文件名即可
- 在windows系统使用路径时,可以使用
/
来代替\
, 或者可以使用\\
来代替\
,或者也可以使用原始字符串
# 创建一个变量,来保存文件的名字
# 如果目标文件和当前文件在同一级目录下,则直接使用文件名即可
file_name = 'demo.txt'
# 在windows系统使用路径时
# file_name = 'hello\\demo.txt'
# 表示路径,可以使用..来返回一级目录
# file_name = '../hello/demo.txt'
# 如果目标文件距离当前文件比较远,此时可以使用绝对路径
# 绝对路径应该从磁盘的根目录开始书写
# file_name = r'C:\Users\AISMALL\Desktop\hello.txt'
file_obj = open(file_name,encoding='utf-8') # 打开 file_name 对应的文件
# print(file_obj)
4.2,关闭文件
- 当我们打开文件后,所有的操作都应该使用文件对象来进行操作
read()
函数:用来读取文件中的内容,它会将内容全部保存为一个字符串返回close()
方法:关闭文件
# 打开文件
file_name = 'demo.txt'
file_obj = open(file_name,encoding='utf-8')
# 当我们获取了文件对象以后,所有的对文件的操作都应该通过对象来进行
content = file_obj.read()
print(content)
# 调用close()方法来关闭文件
file_obj.close()
- 文件的另一种打开方式使用:
with....as
语句
# with ... as 语句
# with open(file_name) as file_obj :
# # 在with语句中可以直接使用file_obj来做文件操作
# # 此时这个文件只能在with中使用,一旦with结束则文件会自动close()
# print(file_obj.read())
file_name = 'demo.txt'
try:
with open(file_name,encoding='utf-8') as file_obj :
print(file_obj.read())
except FileNotFoundError:
print(f'{file_name} 文件不存在~~')
4.3,文件读取
- 调用
open()
来打开一个文件,可以将文件分成两种类型
一种,是纯文本文件(使用utf-8等编码编写的文本文件)
一种,是二进制文件(图片、mp3、ppt等这些文件) open()
打开文件时,默认是以文本文件的形式打开的,但是open()默认的编码为None,所以处理文本文件时,必须要指定文件的编码
例如:open(file_name,encoding='utf-8')
- 通过
read()
来读取文件中的内容,它会将文本文件的所有内容全部都读取出来,如果要读取的文件较大的话,会一次性将文件的内容加载到内存中,容易导致内存泄漏,所以对于较大的文件,不要直接调用read() read()
可以接收一个size作为参数,该参数用来指定要读取的字符的数量
默认值为-1,它会读取文件中的所有字符,可以为size指定一个值,这样read()会读取指定数量的字符,每一次读取都是从上次读取到位置开始读取的,如果字符的数量小于size,则会读取剩余所有的,如果已经读取到了文件的最后了,则会返回’'空串"
file_name = 'demo.txt'
try:
with open(file_name,encoding='utf-8') as file_obj:
# help(file_obj.read)
# content = file_obj.read(-1)
content = file_obj.read(6)
print(content)
print(len(content))
content = file_obj.read(6)
print(content)
print(len(content))
content = file_obj.read(6)
print(content)
print(len(content))
except FileNotFoundError :
print(f'{file_name} 这个文件不存在!')
- 读取大文件的时候使用下面这种方式:
# 读取大文件的方式
file_name = 'demo.txt'
try:
with open(file_name, encoding='utf-8') as file_obj:
# 定义一个变量,来保存文件的内容
file_content = ''
# 定义一个变量,来指定每次读取的大小
chunk = 100
# 创建一个循环来读取文件内容
while True:
# 读取chunk大小的内容
content = file_obj.read(chunk)
# 检查是否读取到了内容
if not content:
# 内容读取完毕,退出循环
break
# 输出内容
# print(content,end='')
file_content += content
except FileNotFoundError:
print(f'{file_name} 这个文件不存在!')
print(file_content)
readline()
:该方法可以用来读取一行内容
file_name = 'demo.txt'
with open(file_name , encoding='utf-8') as file_obj:
# readline():该方法可以用来读取一行内容
# end='',不让print执行之后换行
print(file_obj.readline(),end='')
print(file_obj.readline())
print(file_obj.readline())
readlines()
:该方法用于一行一行的读取内容,它会一次性将读取到的内容封装到一个列表中返回
import pprint
import os
file_name = 'demo.txt'
with open(file_name , encoding='utf-8') as file_obj:
r = file_obj.readlines()
pprint.pprint(r[0])
pprint.pprint(r[1])
pprint.pprint(r[2])
- for 循环逐行读取文件内容
file_name = 'demo.txt'
with open(file_name , encoding='utf-8') as file_obj:
for t in file_obj:
print(t,end='')
4.4,文件写入
- 使用open()打开文件时必须要指定打开文件所要做的操作(读、写、追加)
如果不指定操作类型,则默认是读取文件 , 而读取文件时是不能向文件中写入的
r
:表示只读的
w
:表示是可写的,使用w来写入文件时,如果文件不存在会创建文件,如果文件存在则会截断文件,截断文件指删除原来文件中的所有内容
a
:表示追加内容,如果文件不存在会创建文件,如果文件存在则会向文件中追加内容
x
:用来新建文件,如果文件不存在则创建,存在则报错
+
:为操作符增加功能
r+
:即可读又可写,文件不存在会报错
w+
:写的时候可读
a+
:追加的时候可读
示例:
open(file_name , 'w' , encoding='utf-8')
open(file_name , 'r+' , encoding='utf-8')
write()
函数:来向文件中写入内容,
如果操作的是一个文本文件的话,则write()需要传递一个字符串作为参数
该方法会可以分多次向文件中写入内容
写入完成以后,该方法会返回写入的字符的个数
file_name = 'demo.txt'
with open(file_name,'w',encoding='utf-8') as file_obj:
# \n为换行
file_obj.write('aaa\n')
r = file_obj.write(str(123)+'123123\n')
print(r)
r = file_obj.write('今天天气真不错')
print(r)
4.5,二进制文件操作
open()
函数的读取模式
t
:读取文本文件(默认值)
b
:读取二进制文件
例如:
读文本文件:rt
,可简写为:r
,读二进制文件:rb
写文本文件:wt
,可简写为:w
,写二进制文件:wb
- 读取文本文件时,size是以字符为单位的
- 读取二进制文件时,size是以字节为单位
file_name = '小朋友你是否有很多问号.mp3'
with open(file_name , 'rb') as file_obj:
print(file_obj.read(100))
# 将读取到的内容写出来
# 定义一个新的文件
new_name = 'aa.mp3'
with open(new_name , 'wb') as new_obj:
# 定义每次读取的大小
chunk = 1024 * 100
while True :
# 从已有的对象中读取数据
content = file_obj.read(chunk)
# 内容读取完毕,终止循环
if not content :
break
# 将读取到的数据写入到新对象中
new_obj.write(content)
tell()
方法:用来查看当前读取的位置
例如:file_obj.tell()
seek()
方法:可以修改当前读取的位置,指定从哪个位置开始读
seek()需要两个参数
第一个 是要切换到的位置
第二个 计算位置方式
可选值:- 0 从头计算,默认值
- 1 从当前位置计算
- 2 从最后位置开始计算
例如:file_obj.seek(9)
,从第9个字符开始读
with open('demo.txt','rb') as file_obj:
print(file_obj.read(30))
# tell() 方法用来查看当前读取的位置
print('当前读取到了 -->', file_obj.tell())
# seek():可以修改当前读取的位置
file_obj.seek(55)
# file_obj.seek(80, 0)
# file_obj.seek(70, 1)
# file_obj.seek(-10, 2)
print(file_obj.read())
4.6,文件的其他操作
import os
from pprint import pprint
# os包中的方法
# os.listdir() 获取指定目录的目录结构
# 需要一个路径作为参数,会获取到该路径下的目录结构,默认路径为 . 当前目录
# 该方法会返回一个列表,目录中的每一个文件(夹)的名字都是列表中的一个元素
# r = os.listdir()
# os.getcwd() 获取当前所在的目录
r = os.getcwd()
# os.chdir() 切换当前所在的目录 作用相当于 cd
# os.chdir('c:/')
# 创建目录
# os.mkdir("aaa") # 在当前目录下创建一个名字为 aaa 的目录
# 删除目录
# os.rmdir('abc')
# 打开文件
# open('aa.txt','w')
# 删除文件
# os.remove('aa.txt')
# os.rename('旧名字','新名字') 可以对一个文件进行重命名,也可以用来移动一个文件
# os.rename('aa.txt','bb.txt')
# os.rename('bb.txt','c:/users/AISMALL/desktop/bb.txt')
pprint(r)