python基础(五)

python基础(五)

21 函数

21.1 函数的概念和分类

(1)函数的概念

函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段。函数能提高应用的模块性和代码的重复利用率。函数的本质就是一段有特定功能、可以重复使用的代码,这段代码已经被提前编写好了,并且为其起一个“好听”的名字。在后续编写程序过程中,如果需要同样的功能,直接通过起好的名字就可以调用这段代码。

(2)函数的分类

● 内置函数:Python 提供的直接可以拿来使用的函数,例如 list,len,str 等;
● 标准库函数:Python 提供的可在源程序中调用的函数,需要用 import 语句进行导入,例如 time,os 等;
● 第三方库:需要另外下载到本地的库,需要用 import 导入,例如 opencv 库等;
● 自定义函数:自己在模块里定义的函数。

21.2 函数的定义和调用

(1)函数的定义

函数的定义以 def 关键词开头,语法格式为:

def 函数名(参数列表):
函数体
[return [返回值]]

各参数含义如下:

● 函数名:一个符合 Python 语法的标识符,但不建议使用 a、b、c 这类简单的标识符作为函数名,函数名最好能够体现出该函数的功能;
● 参数列表:任何传入参数和自变量必须放在圆括号中间,用于定义参数;可以定义多少个,多个参数之间用逗号 , 分隔。
● [return [返回值] ]:整体作为函数的可选参数,用于设置该函数的返回值。return 语句用于将函数处理结果返回,或者返回一些其他数据。当 return 被执行,代表函数调用结束,也就是说 return 语句的作用之二就是结束函数的调用,所以 return 后的返回代码只能是一行。如果函数体里不写 return,默认返回 None。

# 定义函数:返回 a 和 b 中的最大值
def max_ab(a, b):
    if type(a) == int and type(b) == int:
        return a if a >= b else b
    else:
        return '类型错误'

(2)函数的调用

定义了函数之后,就相当于有了一个具有某些功能的代码,想要让这些代码能够执行,需要调用它。调用函数很简单的,语法格式为:函数名()

# 定义函数:返回 a 和 b 中的最大值
def max_ab(a, b):
    if type(a) == int and type(b) == int:
        return a if a >= b else b
    else:
        return '类型错误'
# 调用 max_ab 函数
c = max_ab(1, 2)
print(c)
d = max_ab(5, "n")
print(d)

执行以上代码,输出结果为:

2
类型错误

21.3 函数的值传递和引用传递

(1)形式参数和实际参数

● 形式参数(形参):定义函数时,函数名后面括号中的参数就是形式参数;
● 实际参数(实参):调用函数时,函数名后面括号中的参数称为实际参数,也就是函数的调用者给函数的参数。

# 定义函数:返回 a 和 b 中的最大值
def max_ab(a, b):
    if type(a) == int and type(b) == int:
        return a if a >= b else b
    else:
        return '类型错误'
# 调用 max_ab 函数
c = max_ab(1, 2)
print(c)

以上代码中,定义函数时,a 和 b 就是形式参数;调用函数时,c 就是实际参数。

(2)值传递和引用传递

Python 中,根据实际参数的类型不同,函数参数的传递方式可分为两种:

● 值传递:适用于实参类型为不可变类型(字符串、数字、元组);
● 引用传递:适用于实参类型为可变类型(列表,字典)。
值传递和引用传递的区别是,函数参数进行值传递后,若形参的值发生改变,不会影响实参的值;而函数参数继续引用传递后,改变形参的值,实参的值也会一同改变。

# 值传递
def print_copy(a):
    a += a
    print("形参值为:", a)
b = "Hello"
print("b的值为:", b)
print_copy(b)
print("实参值为:", b)

# 引用传递
def print_copy(a):
    a += a
    print("形参值为:", a)
b = [1, 2, 3, 4]
print("b的值为:", b)
print_copy(b)
print("实参值为:", b)

执行以上代码,输出结果为:

b的值为: Hello
形参值为: HelloHello
实参值为: Hello
b的值为: [1, 2, 3, 4]
形参值为: [1, 2, 3, 4, 1, 2, 3, 4]
实参值为: [1, 2, 3, 4, 1, 2, 3, 4]

21.4 位置参数

Python 调用函数时可使用的正式参数类型有四种,分别是位置参数、关键字参数、默认参数和可变长参数。

位置参数,也称必须参数,指的是必须按照正确的顺序将实际参数传到函数中,换句话说,调用函数时传入实际参数的数量和位置都必须和定义函数时的形式参数保持一致。

● 实参和形参数量必须一致
● 实参和形参位置必须一致

# 定义一个函数求梯形面积
def area_trapezoid(a, b, h):
    s = (a + b) * h / 2
    print(s)
area_trapezoid(1, 2, 6)

执行以上代码,输出结果为:

9.0

21.5 关键字参数

关键字参数是指使用形式参数的名字来确定输入的参数值。通过此方式指定函数实参时,不再需要与形参的位置完全一致,只要将参数名写正确即可。

● 不能在关键字参数后面使用非关键字参数
● 不能对通过一个参数进行多次赋值

# 定义一个函数求梯形面积
def area_trapezoid(a, b, h):
    s = (a + b) * h / 2
    print(s)
area_trapezoid(a=1, b=2, h=6)
area_trapezoid(b=2, h=6, a=1)
area_trapezoid(1, b=2, h=6)

执行以上代码,输出结果为:

9.0
9.0
9.0

21.6 默认参数

在调用函数时如果不指定某个参数,Python 解释器会抛出异常。为了解决这个问题,Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值,语法格式为:

def 函数名(…,形参名, 形参名=默认值):
代码块

● 定义函数时默认参数必须要放在位置参数后面
● 未传入形式参数的使用默认参数,传入形式参数的直接使用形式参数

# 定义一个函数求梯形面积
def area_trapezoid(a, b, h=6):
    s = (a + b) * h / 2
    print(s)
area_trapezoid(a=1, b=2)
area_trapezoid(a=1, b=2, h=8)

执行以上代码,输出结果为:

9.0
12.0

21.7 可变长参数

可变长参数是指长度(数量)可变的参数,函数参数不可知的时候,就需要写一个可变长参数的函数。

(1)可变长参数在形参的作用

如果此时有一串数据要传入函数,不知道有多少个,那么应该如何定义函数呢?

*args

可以接受多余的位置参数,并将多余参数转换成元组类型赋值给 * 后面的形参 args(* 后的形参名约定俗成为 args)。

● 位置参数只能多,不能少;
● 定义可变长形参时,*args 要放在位置参数后面。

def print_obj(a, b, *args):
    print(a, b, args)
print_obj(1, 2, 3)
print_obj(1, 2, 3, 4, 5, 6)

执行以上代码,输出结果为:

1 2 (3,)
1 2 (3, 4, 5, 6)

**② kwargs

可以接受多余的关键字参数,并将多余参数转换成字典类型赋值给 ** 后面的形参 kwargs(** 后的形参名约定俗成为 kwargs)。

● 位置参数要对应;
● 多余的参数必须要用关键字传参;
● 定义可变长形参时,**kwargs 要放在位置参数后面。

def print_obj(a, b, **kwargs):
    print(a, b, kwargs)
print_obj(1, 2)
print_obj(1, 2, c=3)
print_obj(1, 2, c=3, d=4, e=5, f=6)

执行以上代码,输出结果为:

1 2 {}
1 2 {'c': 3}
1 2 {'c': 3, 'd': 4, 'e': 5, 'f': 6}

**③ *args 和 kwargs

可接受多余位置参数和关键字参数,并将多余参数转换成元组类型赋值给 * 后面的形参 args,转换成字典类型赋值给 ** 后面的形参 kwargs。

● 位置参数要对应;
● 定义可变长形参时, *args 和 **kwargs 要放在位置参数后面;
● 定义可变长形参时,**kwargs 要放在 *args 后面。

def func(normal_arg, *args, **kwargs):
    print(f"Normal arg: {normal_arg}")
    for arg in args:
        print(f"Another positional arg: {arg}")
    for key, value in kwargs.items():
        print(f"Keyword argument {key} is {value}")

func(0, 1, 2, 3, a=4, b=5, c=6)

执行以上代码,输出结果为:

Normal arg: 0
Another positional arg: 1
Another positional arg: 2
Another positional arg: 3
Keyword argument a is 4
Keyword argument b is 5
Keyword argument c is 6

(2)可变长参数在实参的作用

① *

类似于for循环一次性拿到容器类型里的数据值,再传递给位置形参中。

def print_obj(*args):
    print(args)
print_obj("hello")
print_obj(*"hello")
print_obj(1, 2, 3, 4, 5)
print_obj(*(1, 2, 3, 4, 5))
print_obj([1, 2, 3, 4, 5])
print_obj(*([1, 2, 3, 4, 5]))
print_obj({1, 2, 3, 4, 5})
print_obj(*({1, 2, 3, 4, 5}))
print_obj({"hello": "a", "world": "b"})
print_obj(*{"hello": "a", "world": "b"})

执行以上代码,输出结果为:

('hello',)
('h', 'e', 'l', 'l', 'o')
(1, 2, 3, 4, 5)
(1, 2, 3, 4, 5)
([1, 2, 3, 4, 5],)
(1, 2, 3, 4, 5)
({1, 2, 3, 4, 5},)
(1, 2, 3, 4, 5)
({'hello': 'a', 'world': 'b'},)
('hello', 'world')

② **

把字典的键值对以关键字参数的形式传递给关键字参数中。

def print_obj1(**kwargs):
    print(kwargs)
print_obj1(**{"hello": "a", "world": "b"})
def print_obj2(a, b, c = 0, *args, **kwargs):
    print(a)
    print(b)
    print(c)
    print(args)
    print(*args)
    print(kwargs)
print_obj2(1, 2, 3, 4, 5, d=6, e=7, f=8)

执行以上代码,输出结果为:

{'hello': 'a', 'world': 'b'}
1
2
3
(4, 5)
4 5
{'d': 6, 'e': 7, 'f': 8}

21.8 变量作用域

作用域(Scope),是指变量的有效范围,就是变量可以在哪个范围以内使用,变量的作用域由变量的定义位置决定,在不同位置定义的变量的作用域是不一样的。

(1)局部变量和全局变量

局部变量:局部变量是指在函数内部申明的变量,只能在函数内部调用,超过使用范围就会报错。
全局变量:就是在整个 Python 文件中申明的变量,可以在全局使用。
在局部,如果局部变量和全局变量名相同,优先使用局部变量

result_obj = 100  # 全局变量
def sum_obj(a, b):
    result_obj = a + b  # 局部变量
    print('函数内的result值为:', result_obj)  # result在这里是局部变量
    return result_obj
# 调用sum函数
sum_obj(100, 200)
print('函数外的变量result是全局变量,等于', result_obj)

执行以上代码,输出结果为:

函数内的result值为: 300
函数外的变量result是全局变量,等于 100

(2)获取指定作用域范围中的变量

在一些特定场景中,我们可能需要获取某个作用域内(全局范围内或者局部范围内)所有的变量。

① globals 函数

globals 函数可以返回一个包含全局范围内所有变量的字典,该字典中的每个键值对,键为变量名,值为该变量的值。

#全局变量
name1 = "111"
name2 = "222"
def text1():
    #局部变量
    name1 = "111"
    name2 = "222"
print(globals())

执行以上代码,输出结果为:

... 'name1': '111', 'name2': '222',...}

② locals 函数

locals 函数可以返回一个包含当前作用域内所有变量的字典。这里所谓的“当前作用域”指的是,在函数内部调用 locals 函数,会获得包含所有局部变量的字典;而在全局范文内调用 locals 函数,其功能和 globals 函数相同。

#全局变量
name1 = "111"
name2 = "222"
def text1():
    #局部变量
    name1 = "111"
    name2 = "222"
    print("函数内部的 locals:")
    print(locals())
text1()
print("函数外部的 locals:")
print(locals())

执行以上代码,输出结果为:

函数内部的 locals:
{'name1': '111', 'name2': '222'}
函数外部的 locals:
...'name1': '111', 'name2': '222', ...}

21.9 None空值

在 Python 中,有一个特殊的常量 None(N 必须大写)。和 False 不同,它不表示 0,也不表示空字符串,而表示没有值,也就是空值。

a = type(None)
print(a)

执行以上代码,输出结果为:

<class 'NoneType'>

对于所有没有 return 语句的函数定义,Python 都会在末尾加上 return None,使用不带值的 return 语句(也就是只有 return 关键字本身),那么就返回 None。

此外,如果在条件判断语句的条件表达式中出现了 None,判断为 False。

if None:
    print("Hello")
else:
    print("判断为 False")

执行以上代码,输出结果为:

判断为 False

21.10 闭包函数

比如,求比特币一个时间段的平均收盘价(6000、7000、8000…),分别采用定义普通函数和定义闭包函数的方式来求解。

# 采用定义普通函数的方式
list_obj = []                                   # 全局变量
def average_obj(money):
    list_obj.append(money)
    return sum(list_obj) / len(list_obj)
print(average_obj(6000))
print(average_obj(7000))
print(average_obj(8000))

执行以上代码,输出结果为:

6000.0
6500.0
7000.0

由以上程序可以看出,list_obj 为全局变量,可以在整个文件中修改,所以说该程序是不安全的,需要进一步升级,将全局变量 list_obj 放到函数内部,这就是闭包函数的定义形式。

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。所以,闭包的构成调条件:

● 在函数嵌套(函数里面再定义函数)的前提下;
● 内部函数使用了外部函数的变量(还包括外部函数的参数);
● 外部函数返回了内部函数。

# 采用定义闭包函数的方式
def average_obj():
    list_obj = []
    def inner(money):
        list_obj.append(money)
        return sum(list_obj) / len(list_obj)
    return inner
average = average_obj()
print(average(6000))
print(average(7000))
print(average(8000))

执行以上代码,输出结果为:

6000.0
6500.0
7000.0

21.11 匿名函数

匿名函数,又称 lambda 函数,常用来表示内部仅包含 1 行表达式的函数。如果一个函数的函数体仅有 1 行表达式,则该函数就可以用 lambda 表达式来代替,语法格式为:name = lambda[参数1 [,参数2,…参数n]]:表达式,其中,lambda 后为可选参数,用于指定要传递的形参列表,多个参数使用逗号 , 分隔;name 为函数的名称。与匿名函数效果相同的普通函数的定义形式如下:

def name([参数1 [,参数2,…参数n]]):
表达式
return 表达式

比如,定义函数来计算两数之和:

# 普通函数
def addition1(a, b):
    c = a + b
    return c
print(addition1(1, 2))
# 匿名函数
f = lambda d, e: d + e
print(f(1, 2))

执行以上代码,输出结果为:

3
3

作业:

写一个打印一条横线的函数; 写一个函数,可以通过输入的参数,打印出自定义行数的横线(提示:调用前面的函数);

# 第一问
def print_xian():
    return "-" * 20
print(print_xian())
# 第二问
def print_number(number):
    i = 1
    while i <= number:
        print(print_xian())
        i += 1
print_number(3)

执行以上代码,输出结果为:

--------------------
--------------------
--------------------
--------------------

写一个函数求三个数的和; 写一个函数求三个数的平均值(提示:调用前面的函数) 。

# 第一问
def sum_obj(a, b, c):
    return a + b + c
print(sum_obj(1, 2, 3))
# 第二问
def average_obj(a, b, c):
    return sum_obj(a, b, c) / 3
print(average_obj(1, 2, 3))

执行以上代码,输出结果为:

6
2.0

22 文件操作

22.1 文件路径

(1)绝对路径和相对路径

文件有两个关键属性,分别是“文件名”和“路径”。其中,文件名指的是为每个文件设定的名称,而路径则用来指明文件在计算机上的位置。

在 Windows 上,路径书写使用反斜杠 “” 作为文件夹之间的分隔符,比如:D:\PyCharm\bin\icons;但在 OS X 和 Linux 上,使用正斜杠 “/” 作为它们的路径分隔符,比如:/home/gliu/sample.txt。

明确一个文件所在的路径,有两种种表示方式,分别是:

● 绝对路径:总是从根文件夹开始,Window 系统中以盘符(C:、D:)作为根文件夹,而 OS X 或者 Linux 系统中以 \ 作为根文件夹。
● 相对路径:指的是文件相对于当前工作目录所在的位置。例如,当前工作目录为 “C:\Windows\System32”,若文件 demo.txt 就位于这个 System32 文件夹下,则 demo.txt 的相对路径表示为 “.\demo.txt”(其中 .\ 就表示当前所在目录)。

(2)读取文件路径失败的原因

os.path.exists 函数用于判断一个指定路径的文件或者目录是否存在,语法格式为:

import os
os.path.exists(“path”)

比如,判断位于桌面的的文件 wow.txt 是否存在:

import os
print(os.path.exists("C:\Users\****\Desktop\wow.txt"))

执行以上代码,输出结果为:

SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape

输出时会报错,这是因为系统将 \ 解释为了转义字符串。

为了避免出现转义字符导致文件路径解析出错,可以使用 \ 或者 / 代替 \ ,或者使用 r"\path" 或 R"\path" :

import os
print(os.path.exists("C:\\Users\\****\\Desktop\\wow.txt"))
print(os.path.exists("C:/Users/****/Desktop/wow.txt"))
print(os.path.exists(r"C:\Users\****\Desktop\wow.txt"))

执行以上代码,输出结果为:

True
True 
True

(3)文件路径的实际运用

比如,在一个当前工作目录名为“xiaotudui”的 PyCharm 项目中,使用绝对路径打开某个文件,路径名称为:

C:\\Users\\**\\Desktop\\DeepLearning\\xiaotudui\\Dataset\\练手数据集\\val\\ants\\800px-Meat_eater_ant_qeen_excavating_hole.jpg

使用相对路径同样打开该文件,路径名称为:

.\\Dataset\\练手数据集\\val\\ants\\800px-Meat_eater_ant_qeen_excavating_hole.jpg

22.2 文件的操作步骤

文件的应用级操作可以分为 3 步,每一步都需要借助对应的函数实现;一个文件,必须在打开之后才能对其进行操作,并且在操作结束之后,还应该将其关闭,这 3 步的顺序不能打乱:

● 打开文件:使用 open 函数,该函数会返回一个文件对象;
● 读取/写入文件:读取文件内容可使用 read 函数、readline 函数以及 readlines 函数;向文件中写入内容,可以使用 write 函数。
● 关闭文件:完成对文件的读/写操作之后,最后需要关闭文件,可以使用 close 函数。

22.3 文件的打开

(1)语法格式

open 函数用于创建新文件或打开已经存在的文件,语法格式为:file_obj = open(“file_name”, mode=‘r’, buffering=-1, encoding=None)

● file_obj:表示要创建的文件对象;
● file_name:表示要创建或打开文件的文件名称,该名称要用引号括起来。需要注意的是,如果要打开的文件和当前执行的代码文件位于同一目录,则直接写文件名即可;否则,此参数需要指定打开文件所在的完整路径。
● mode:可选参数,用于指定文件的打开模式,如果不写,则默认以只读 r 模式打开文件;
● buffering:可选参数,用于指定对文件做读写操作时是否使用缓冲区;通常情况下使用 open 函数时打开缓冲区,即不需要修改 buffing 参数的值;
● encoding:可选参数,手动设定打开文件时所使用的编码格式,不同平台的 ecoding 参数值也不同,默认采用 GBK 编码。

(2)文件的打开模式

访问模式说明注意事项
r(*常用)只读模式打开文件,读文件内容的指针会放在文件的开头,如果文件不存在,则会发生错误。操作的文件必须存在
rb(*常用)以二进制格式、采用只读模式打开文件,读文件内容的指针位于文件的开头,一般用于非文本文件,如图片文件、音频文件等操作的文件必须存在
r+打开文件后,既可以从头读取文件内容,也可以从开头向文件中写入新的内容,写入的新内容会覆盖文件中等长度的原有内容操作的文件必须存在
rb+(*常用)以二进制格式、采用读写模式打开文件,读写文件的指针会放在文件的开头,通常针对非文本文件(如音频文件)操作的文件必须存在
w(*常用)打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。若文件存在,会清空其原有内容(覆盖文件);反之,则创建新文件
wb以二进制格式、只写模式打开文件,一般用于非文本文件(如音频文件)若文件存在,会清空其原有内容(覆盖文件);反之,则创建新文件
w+打开文件后,会对原有内容进行清空,并对该文件有读写权限若文件存在,会清空其原有内容(覆盖文件);反之,则创建新文件
wb+以二进制格式、读写模式打开文件,一般用于非文本文件若文件存在,会清空其原有内容(覆盖文件);反之,则创建新文件
a以追加模式打开一个文件,对文件只有写入权限,如果文件已经存在,文件指针将放在文件的末尾(即新写入内容会位于已有内容之后);反之,则会创建新文件\
ab以二进制格式打开文件,并采用追加模式,对文件只有写权限。如果该文件已存在,文件指针位于文件末尾(新写入文件会位于已有内容之后);反之,则创建新文件\
a+以读写模式打开文件;如果文件存在,文件指针放在文件的末尾(新写入文件会位于已有内容之后);反之,则创建新文件\
ab+以二进制模式打开文件,并采用追加模式,对文件具有读写权限,如果文件存在,则文件指针位于文件的末尾(新写入文件会位于已有内容之后);反之,则创建新文件\

比如,写入之前,当前代码文件所在的目录中不存在 .txt 类型的文件:
写入前代码文件所在目录

file_obj = open("hello.txt", "w")
file_obj.close()

执行以上代码,写入之后,在当前代码文件所在的目录中创建了新文件 hello.txt:
写入后代码文件所在目录

(3)文件对象的常用属性

成功打开文件之后,可以调用文件对象本身拥有的属性获取当前文件的部分信息,其常见的属性为:

● file_obj.name:返回文件的名称;
● file_obj.mode:返回打开文件时,采用的文件打开模式;
● file_obj.encoding:返回打开文件时使用的编码格式;
● file_obj.closed:判断文件是否己经关闭。
其中,file_obj 表示要查看属性的文件对象。

file_obj = open("hello.txt", "w")
file_obj.close()
print(file_obj.name)
print(file_obj.mode)
print(file_obj.encoding)
print(file_obj.closed)

执行以上代码,输出结果为:

hello.txt
w
cp936
True

22.4 文件的关闭

在 Python 中,打开文件操作完毕后,还需要关闭文件,如果只打开文件,不关闭文件,会造成系统资源的浪费,关闭文件使用 close 函数,语法格式为:file_obj.close(),其中,file_obj 表示需要关闭的文件对象。

file_obj = open("hello.txt", "w")
file_obj.close()
print(file_obj.closed)

执行以上代码,输出结果为:

True

22.5 文件的读取

Python 提供了 3 种函数实现读取文件中数据的操作:

● read 函数:逐个字节或者字符读取文件中的内容;
● readline 函数:逐行读取文件中的内容;
● readlines 函数:一次性读取文件中多行内容。
(1)read 函数

对于借助 open 函数并以可读模式(包括 r、r+、rb、rb+)打开的文件,可以调用 read 函数逐个字节(或者逐个字符)读取文件中的内容,语法格式为:file_obj.read(size),其中,file_obj 表示已打开的文件对象;size 为可选参数,用于指定一次最多可读取的字符(字节)个数,如果省略,则默认一次性读取所有内容。

● size 表示的是一次最多可读取的字符(或字节)数,因此,即便设置的 size 大于文件中存储的字符(字节)数,read 函数也不会报错,它只会读取文件中所有的数据;
● 想使用 read 函数成功读取文件内容,除了严格遵守 read 函数的语法外,其还要求 open 函数必须以可读默认(包括 r、r+、rb、rb+)打开文件;
● 在使用 read 函数时,如果 Python 解释器提示“UnicodeDecodeError”异常,其原因在于目标文件使用的编码格式和 open 函数打开该文件时使用的编码格式不匹配。

# hello.txt 中包含的内容为:Welcome to Wuhan!
# 读取所有内容
file_obj = open("hello.txt", "r", encoding="utf-8")
print(file_obj.read())
file_obj.close()
# 读取文件开头的前10个字符
file_obj = open("hello.txt", "r", encoding="utf-8")
print(file_obj.read(10))
file_obj.close()
# 读取文件开头的前20个字符
file_obj = open("hello.txt", "r", encoding="utf-8")
print(file_obj.read(20))
file_obj.close()

执行以上代码,输出结果为:

Welcome to Wuhan!
Welcome to
Welcome to Wuhan!

(2)readline 函数

readline 函数用于读取文件中的一行,包含最后的换行符“\n”,语法格式为:file_obj.readline(size),其中,file_obj 为打开的文件对象;size 为可选参数,用于指定读取每一行时,一次最多读取的字符(字节)数。

● 和 read 函数一样,此函数成功读取文件数据的前提是,使用 open 函数指定打开文件的模式必须为可读模式(包括 r、rb、r+、rb+);
● 由于 readline 函数在读取文件中一行的内容时,会读取最后的换行符“\n”,再加上 print 函数输出内容时默认会换行,所以输出结果中会看到多出了一个空行。

"""
hello.txt 文件包含的内容为:
Welcome to Wuhan!
Welcome to Wuhan!
"""
# 读取所有内容
file_obj = open("hello.txt", "r", encoding="utf-8")
print(file_obj.read())
file_obj.close()
# 读取文件第一行的字符
file_obj = open("hello.txt", "r", encoding="utf-8")
print(file_obj.readline())
file_obj.close()
# 读取文件第一行的前10个字符,这里没有完整读取一行的数据,因此不会读取到换行符
file_obj = open("hello.txt", "r", encoding="utf-8")
print(file_obj.readline(10))
file_obj.close()
# 读取文件第一行的前20个字符
file_obj = open("hello.txt", "r", encoding="utf-8")
print(file_obj.readline(20))
file_obj.close()

执行以上代码,输出结果为:

Welcome to Wuhan!
Welcome to Wuhan!
Welcome to Wuhan!

Welcome to
Welcome to Wuhan!

(3)readlines 函数

readlines 函数用于读取文件中的所有行,它和调用不指定 size 参数的 read 函数类似,只不过该函数返回是一个字符串列表,其中每个元素为文件中的一行内容,语法格式为:file_obj.readlines(),其中,其中,file_obj 为打开的文件对象。

● 和 read 函数、readline 函数一样,readlines 函数要求打开文件的模式必须为可读模式(包括 r、rb、r+、rb+);
● 和 readline 函数一样,readlines 函数在读取每一行时,会连同行尾的换行符一块读取。

"""
hello.txt 文件包含的内容为:
Welcome to Wuhan!
Welcome to Wuhan!
"""
# 读取所有内容
file_obj = open("hello.txt", "r", encoding="utf-8")
print(file_obj.read())
file_obj.close()
# 读取文件所有行的字符
file_obj = open("hello.txt", "r", encoding="utf-8")
print(file_obj.readlines())
file_obj.close()

执行以上代码,输出结果为:

Welcome to Wuhan!
Welcome to Wuhan!
['Welcome to Wuhan!\n', 'Welcome to Wuhan!']

22.6 文件的写入

Python 提供了 2 种函数实现在文件中写入数据:

● write 函数:写入字符串或字节串,只有以二进制模式(b 模式)打开的文件才能写入字节串;
● writelines 函数:写入多个字符串或多个字节串。
(1)write 函数

Python 中的文件对象提供了 write 函数,可以向文件中写入指定内容,语法格式为:file_obj.write(string),其中,file_obj 表示已经打开的文件对象;string 表示要写入文件的字符串。

● 在使用 write 函数向文件中写入数据,需保证使用 open 函数是以 r+、w、w+、a 或 a+ 的模式打开文件,否则执行 write 函数会抛出 io.UnsupportedOperation 错误;
● 采用不同的文件打开模式,会直接影响 write 函数向文件中写入数据的效果。

# 原文件包含内容:Welcome to Wuhan!
file_obj = open("hello.txt", "r+", encoding="utf-8")
file_obj.write("替换")
file_obj.close()                   

执行以上代码,文件包含内容为:替换e to Wuhan!

# 原文件包含内容:Welcome to Wuhan!
file_obj = open("hello.txt", "w", encoding="utf-8")
file_obj.write("替换")
file_obj.close()

执行以上代码,文件包含内容为:替换

# 原文件包含内容:Welcome to Wuhan!
file_obj = open("hello.txt", "w+", encoding="utf-8")
file_obj.write("替换")
file_obj.close()

执行以上代码,文件包含内容为:替换

# 原文件包含内容:Welcome to Wuhan!
file_obj = open("hello.txt", "a", encoding="utf-8")
file_obj.write("替换")
file_obj.close()

执行以上代码,文件包含内容为:Welcome to Wuhan!替换

# 原文件包含内容:Welcome to Wuhan!
file_obj = open("hello.txt", "a+", encoding="utf-8")
file_obj.write("替换")
file_obj.close()

执行以上代码,文件包含内容为:Welcome to Wuhan!替换

(2)writelines 函数

Python 的文件对象中不仅提供了 write 函数,还提供了 writelines 函数,可以实现将字符串列表写入文件中,语法格式为:file_obj.writelines(string),其中,file_obj 表示已经打开的文件对象;string 表示要写入文件的字符串(多个字符串需要用一个变量代表,否则会报错)。

● 写入函数只有 write 函数和 writelines 函数,而没有名为 writeline 的函数;
● 使用 writelines 函数向文件中写入多行数据时,不会自动给各行添加换行符,需要添加换行符 \n。

file_obj = open("hello.txt", "w", encoding="utf-8")
seq = ("测试 1\n", "测试 2")
file_obj.writelines(seq)
file_obj.close()

执行以上代码,文件包含内容为:

测试 1
测试 2

22.7 Seek函数和tell函数

(1)文件指针

文件指针用于标明文件读写的起始位置。假如把文件看成一个水流,文件中每个数据(以 b 模式打开,每个数据就是一个字节;以普通模式打开,每个数据就是一个字符)就相当于一个水滴,而文件指针就标明了文件将要从文件的哪个位置开始读起(原始文件的指针指向 0)。

Python 文件指针

(2)tell 函数

tell 函数用于判断文件指针当前所处的位置,语法格式为:file_obj.tell(),其中,file_obj 表示文件对象。

# 原文件包含内容:Welcome to Wuhan!
file_obj = open("hello.txt", "rb")
print(file_obj.tell())
print(file_obj.read(3))
print(file_obj.tell())

执行以上代码,输出结果为:

0
b'Wel'
3

(3)seek 函数

seek 函数用于将文件指针移动至指定位置,语法格式为:file_obj.seek(offset, whence)

● file_obj:表示文件对象;
● whence:作为可选参数,用于指定文件指针要放置的位置,该参数的参数值有 3 个选择:0 代表文件头(默认值)、1 代表当前位置、2 代表文件尾;
● offset:表示相对于 whence 位置文件指针的偏移量,正数表示向后偏移,负数表示向前偏移。

# 原文件包含内容:123456789123456789.
file_obj = open("hello.txt", "rb")
# 判断文件指针的位置
print(file_obj.tell())
# 读取一个字节,文件指针自动后移1个数据
print(file_obj.read(1))
print(file_obj.tell())
# 将文件指针从文件开头,向后移动到 5 个字符的位置
file_obj.seek(5)
print(file_obj.tell())
print(file_obj.read(1))
# 将文件指针从当前位置,向后移动到 5 个字符的位置
file_obj.seek(5, 1)
print(file_obj.tell())
print(file_obj.read(1))
# 将文件指针从文件结尾,向前移动到距离 2 个字符的位置
file_obj.seek(-1, 2)
print(file_obj.tell())
print(file_obj.read(1))

执行以上代码,输出结果为:

0
b'1'
1
5
b'6'
11
b'3'
18
b'.'

22.8 使用with语句打开文件

with 语句会在代码块执行之前自动调用一个指定对象的上下文管理器,执行完代码块后不用关闭文件,自动释放该上下文管理器所占用的资源,从而简化了资源管理的代码,并可以保证资源被正确释放,避免了因为忘记关闭资源而导致的一些问题,语法格式为:

with expression [as variable]:
statement

其中,expression 表示任意表达式,variable 表示可选的变量名,statement 表示需要执行的语句块。

(1)使用 with 语句打开文件后读取文件

# 原文件包含内容:123456789123456789.
with open("hello.txt", "r", encoding="utf-8") as file_obj:
    a = file_obj.read(5)
    print(a)

执行以上代码,输出结果为:

12345

(2)使用 with 语句打开文件后写入文件

# 原文件包含内容:123456789123456789.
with open("hello.txt", "w", encoding="utf-8") as file_obj:
    file_obj.write("**")

执行以上代码,文件包含内容为:**

22.9 os模块

os 模块是 Python 中整理文件和目录(文件夹)最为常用的模块,该模块提供了非常丰富的方法用来处理文件和目录(文件夹),在调用 os 模块中的函数之前需要先导入 os 模块,导入 os 模块的语法格式为:import os,常用的 os 模块函数有:(1)os.rename 函数、(2)os.remove 函数、(3)os.mkdir 函数、(4)os.makedirs 函数、(5)os.getcwd 函数、(6)os.chdir 函数、(7)listdir 函数、(8)os.rmdir 函数、(9)os.walk 函数、(10)os.sep 函数。

(1)os.rename 函数

os.rename 函数用于重命名文件和目录(文件夹),如果指定的路径是文件,则重命名文件;反之,如果执行的路径是目录(文件夹),则重命名目录(文件夹),语法格式为:os.rename(“path_before”, “path_after”),其中,path_before 参数用于指定要进行重命名的目录(文件夹)或文件;path_after 参数用于指定重命名后的目录(文件夹)或文件。

① 比如,重新命名位于桌面的文件 hello.txt:
修改前文件名称

import os
os.rename("C:\\Users\\****\\Desktop\\hello.txt", "C:\\Users\\****\\Desktop\\world.txt")

执行以上代码,文件名成功改为 world.txt:
修改后文件名称

② 比如,重新命名位于桌面的目录(文件夹) hello:

改前目录(文件夹)名称

import os
os.rename("C:\\Users\\****\\Desktop\\hello", "C:\\Users\\****\\Desktop\\world")

执行以上代码,文件名成功改为 world:

修改后目录(文件夹)名称

(2)os.remove 函数

os.remove 函数用于删除指定路径的文件,如果指定的路径是一个目录,将抛出 OSError,语法格式为:os.remove(“path”),其中,path 表示指定文件的路径。

比如,删除桌面的文件 hello.txt:

import os
os.remove("C:\\Users\\****\\Desktop\\hello.txt")

(3)os.mkdir 函数

os.mkdir 函数用于创建一级目录(文件夹),语法格式为:os.mkdir(“path_first”),其中,path_first 表示需要创建的一级目录(文件夹)的路径。

比如,在桌面创建一级目录(文件夹)hello:

import os
os.mkdir("C:\\Users\\****\\Desktop\\hello")

执行以上代码,成功在桌面创建一级目录(文件夹)hello:
在桌面创建一级目录(文件夹)hello

(4)os.makedirs 函数

os.makedirs 函数用于创建多级目录(文件夹),语法格式为:os.makedirs(“path_multiple”),其中,path_multiple 表示需要创建的多级目录(文件夹)的路径 。

比如,先在桌面创建一级目录(文件夹) books,继续在 books 目录(文件夹)下创建目录(文件夹) book:

import os
os.makedirs("C:\\Users\\****\\Desktop\\books\\book")

执行以上代码,成功在桌面创建一级目录(文件夹)books,在 books 目录(文件夹)下创建二级目录(文件夹)books:

在桌面创建一级目录(文件夹)books
在 books 目录(文件夹)下创建二级目录(文件夹)books

(5)os.getcwd 函数

os.getcwd 函数用于获得程序运行的当前目录(文件夹)所在位置,语法格式为:os.getcwd()

import os
print("当前目录:", os.getcwd())

执行以上代码,输出结果为:

当前目录: C:\Users\****\Desktop\Python学习\Demo1

(6)os.chdir 函数

os.chdir 函数用于将当前工作目录(文件夹)切换到指定的路径,语法格式为:os.chdir(“path_new”),其中,path_new 表示要切换到的新的指定路径。

import os
print("当前目录:", os.getcwd())
os.chdir("C:\\Users\\iliuwe\\Desktop")
print("修改后的目录:", os.getcwd())

执行以上代码,输出结果为:

当前目录: C:\Users\****\Desktop\Python学习\Demo1
修改后的目录: C:\Users\****\Desktop

(7)listdir 函数

listdir 函数用于打印指定路径中存在的所有文件的名称列表,语法格式为:os.listdir(“path”),其中,path 表示指定路径。

import os
a = os.listdir("C:\\Users\\****\\Desktop\\books")
print(a)

执行以上代码,输出结果为:

['book1', 'book2', 'book3', 'book4']

(8)os.rmdir 函数

os.rmdir 函数用于删除指定路径的空目录(文件夹),如果目录不存在或不为空,则会分别抛出 FileNotFoundError 或 OSError 异常,语法格式为:os.rmdir(“path”),其中,path 表示存在且为空的目录(文件夹)。

① 比如,删除位于桌面的非空目录(文件夹) books:

import os
os.rmdir("C:\\Users\\****\\Desktop\\books")

执行以上代码,输出结果为:

OSError: [WinError 145] 目录不是空的。

② 比如,删除位于桌面的空目录(文件夹) hello:

位于桌面的空目录(文件夹) hello

import os
os.rmdir("C:\\Users\\****\\Desktop\\hello")

执行以上代码,位于桌面的空目录(文件夹) hello 被成功删除。

(9)os.walk 函数

os.walk 函数用来扫描某个指定目录下所包含的子目录和文件,对于根于目录顶部(包括顶部本身)的树中的每个目录,它都会生成一个三元组(目录路径,目录名,文件名),即返回的是(root, dirs, files),语法格式为:

for (root, dirs, files) in os.walk("path_top", topdown=True, onerror=None, followlinks=False):

● path_top:表示所要遍历的根目录的地址;
● root :表示当前正在遍历的这个目录的本身的地址;
● dirs:表示一个 list,内容是该目录中包含的目录的名字(不包括下属目录或文件的子目录);
● files:表示一个 list,内容是该目录中包含的文件的名字(不包括下属目录或文件的子文件);
● topdown:表示可选参数,为 True,则优先遍历 top 目录,否则优先遍历 top 的子目录(默认为开启)。如果 topdown 参数为 True,walk 会遍历 top 文件夹,与 top 文件夹中每一个子目录;
● onerror:表示可选参数,需要一个 callable 对象,当 walk 需要异常时,会调用;
● followlinks:表示可选参数,如果为 True,则会遍历目录下的快捷方式(linux 下是软连接 symbolic link)实际所指的目录(默认关闭),如果为 False,则优先遍历 top 的子目录。

假设 A1 文件夹有如下的目录结构:
目录结构

① 用 os.walk 函数自顶向下扫描 A1 文件夹下所有的子目录和文件:

import os
for (root, dirs, files) in os.walk("C:\\Users\\****\\Desktop\\A1"):
    print("-"*80)
    print("当前目录:", root)
    print("该目录中包含的目录的名字(不包括下属目录或文件的子目录):", dirs)
    print("该目录中包含的文件的名字(不包括下属目录或文件的子文件):", files)

执行以上代码,输出结果为:


当前目录: C:\Users\****\Desktop\A1
该目录中包含的目录的名字(不包括下属目录或文件的子目录): ['B1', 'B2', 'B3']
该目录中包含的文件的名字(不包括下属目录或文件的子文件): []
--------------------------------------------------------------------------------
当前目录: C:\Users\****\Desktop\A1\B1
该目录中包含的目录的名字(不包括下属目录或文件的子目录): ['C1']
该目录中包含的文件的名字(不包括下属目录或文件的子文件): ['book2.txt', 'book3.txt']
--------------------------------------------------------------------------------
当前目录: C:\Users\****\Desktop\A1\B1\C1
该目录中包含的目录的名字(不包括下属目录或文件的子目录): []
该目录中包含的文件的名字(不包括下属目录或文件的子文件): ['book1.txt']
--------------------------------------------------------------------------------
当前目录: C:\Users\****Desktop\A1\B2
该目录中包含的目录的名字(不包括下属目录或文件的子目录): []
该目录中包含的文件的名字(不包括下属目录或文件的子文件): ['book4.txt', 'book5.txt', 'book6.txt']
--------------------------------------------------------------------------------
当前目录: C:\Users\****\Desktop\A1\B3
该目录中包含的目录的名字(不包括下属目录或文件的子目录): []
该目录中包含的文件的名字(不包括下属目录或文件的子文件): ['book7.txt', 'book8.txt']

② 用 os.walk 函数自底向上扫描 A1 文件夹下所有的子目录和文件,需要加上 topdown=False:

import os
for (root, dirs, files) in os.walk("C:\\Users\\****\\Desktop\\A1", topdown=False):
    print("-"*80)
    print("当前目录:", root)
    print("该目录中包含的目录的名字(不包括下属目录或文件的子目录):", dirs)
    print("该目录中包含的文件的名字(不包括下属目录或文件的子文件):", files)

执行以上代码,输出结果为:

--------------------------------------------------------------------------------
当前目录: C:\Users\****\Desktop\A1\B1\C1
该目录中包含的目录的名字(不包括下属目录或文件的子目录): []
该目录中包含的文件的名字(不包括下属目录或文件的子文件): ['book1.txt']
--------------------------------------------------------------------------------
当前目录: C:\Users\****\Desktop\A1\B1
该目录中包含的目录的名字(不包括下属目录或文件的子目录): ['C1']
该目录中包含的文件的名字(不包括下属目录或文件的子文件): ['book2.txt', 'book3.txt']
--------------------------------------------------------------------------------
当前目录: C:\Users\****\Desktop\A1\B2
该目录中包含的目录的名字(不包括下属目录或文件的子目录): []
该目录中包含的文件的名字(不包括下属目录或文件的子文件): ['book4.txt', 'book5.txt', 'book6.txt']
--------------------------------------------------------------------------------
当前目录: C:\Users\****\Desktop\A1\B3
该目录中包含的目录的名字(不包括下属目录或文件的子目录): []
该目录中包含的文件的名字(不包括下属目录或文件的子文件): ['book7.txt', 'book8.txt']
--------------------------------------------------------------------------------
当前目录: C:\Users\****\Desktop\A1
该目录中包含的目录的名字(不包括下属目录或文件的子目录): ['B1', 'B2', 'B3']
该目录中包含的文件的名字(不包括下属目录或文件的子文件): []

(10)os.sep 函数

os.sep 函数用于根据所处的平台,自动采用相应的分隔符号:在 Windows 中,文件路径的分隔符为 \,在Linux 中,文件路径的分隔符为 /,语法格式为:“component1” + os.sep + “component2” +…+ os.sep + “componentn”,其中,componentn 表示路径组件。

# 基于 Windows
import os
a = "D:" + os.sep + "Users" + os.sep + "Desktop" + os.sep + "book.txt"
print(a)

执行以上代码,输出结果为:

D:\Users\Desktop\book.txt

22.10 os.path模块

os.path 模块不仅提供了一些操作路径字符串的方法,还包含一些指定文件属性的方法,在调用 os.path 模块中的函数之前需要先导入 os 模块,导入 os 模块的语法格式为:import os,常用的 os.path 模块函数有:(1)os.path.exists 函数、(2)os.path.join 函数、(3)os.path.split 函数、(4)os.path.dirname 函数、(5)os.path.basename 函数、(6)os.path.isdir 函数、(7)os.path.isfile 函数、(8)os.path.getsize 函数、(9)os.path.abspath 函数、(10)os.path.getatime 函数、(11)os.path.getctime 函数。

(1)os.path.exists 函数

os.path.exists 函数用于判断给定的文件或目录是否存在,若存在返回 True,若不存在返回 False,语法格式为:os.path.exists(“path_obj”),其中,path_obj 表示给定的文件或目录的路径。

比如,判断位于桌面的目录 A1 是否存在:

import os
a = os.path.exists("C:\\Users\\****\\Desktop\\A1")
print(a)

执行以上代码,输出结果为:

True

(2)os.path.join 函数

os.path.join 函数用于实现文件路径组件的拼接,语法格式为:os.path.join(“component1”, …“componentn”),其中,componentn 表示路径组件。

① 若各个路径之间不存在 \ ,则其会自动为各个路径之间增加连接符 \;

import os
print(os.path.join("PyCharm", "Exercise", "Demo"))

执行以上代码,输出结果为:

PyCharm\Exercise\Demo

② 若存在以 \ 开始的子路径,则从最后一个以 \ 开头的子路径开始拼接,之前的子路径全部丢弃;

import os
print(os.path.join("PyCharm", "\Exercise", "Demo"))
print(os.path.join("PyCharm", "\Exercise", "\Demo"))

执行以上代码,输出结果为:

\Exercise\Demo
\Demo

③ 若存在以 . \ 开始的子路径,同时存在以 \ 开头的子路径,则还是以 \ 开头的子路径为依据,从最后一个以 \ 开头的子路径开始拼接,之前的子路径全部丢弃;

import os
print(os.path.join(".\PyCharm", "\Exercise", "Demo"))
print(os.path.join("PyCharm", "\Exercise", ".\Demo"))

执行以上代码,输出结果为:

\Exercise\Demo
\Exercise\.\Demo

④ 只存在以 . \ 开始的子路径,而不存在以 \ 开头的子路径,则会将 . \ 里面的 . 当作子路径的一部分,进行拼接;

import os
print(os.path.join(".\PyCharm", "Exercise", "Demo"))
print(os.path.join("PyCharm", "Exercise", ".\Demo"))

执行以上代码,输出结果为:

.\PyCharm\Exercise\Demo
PyCharm\Exercise\.\Demo

(3)os.path.split 函数

os.path.split 函数用于根据文件路径将文件名和目录分割开,语法格式为:os.path.split(“path_obj”),其中,path_obj 表示给定的文件路径,如果给出的路径包含目录和文件名,则返回目录和文件名;如果给出的路径只含目录名,则返回目录和空文件名。

import os
a = os.path.split("C:\\Users\\****\\Desktop\\books\\book.txt")
print(a)
b = os.path.split("C:\\Users\\****\\Desktop\\books\\")
print(b)

执行以上代码,输出结果为:

('C:\\Users\\****\\Desktop\\books', 'book.txt')
('C:\\Users\\****\\Desktop\\books', '')

(4)os.path.dirname 函数

os.path.dirname 函数用于根据文件路径去掉文件名,返回路径名,语法格式为:os.path.dirname(“path_obj”),其中,path_obj 表示给定的文件路径。

import os
a = os.path.dirname("C:\\Users\\****\\Desktop\\books\\book.txt")
print(a)
b = os.path.dirname("C:\\Users\\****\\Desktop\\books\\")
print(b)

执行以上代码,输出结果为:

C:\Users\iliuwe\Desktop\books
C:\Users\iliuwe\Desktop\books

(5)os.path.basename 函数

os.path.basename 函数用于根据文件路径去掉路径名,返回文件名,语法格式为:os.path.basename(“path_obj”),其中,path_obj 表示给定的文件路径。

import os
a = os.path.basename("C:\\Users\\****\\Desktop\\books\\book.txt")
print(a)

执行以上代码,输出结果为:

book.txt

(6)os.path.isdir 函数

os.path.isdir 函数用于检查指定的路径是否为目录,如果指定的路径是目录,则此方法返回 True,否则返回 False,语法格式为:os.path.isdir(“path_obj”),其中, path_obj 表示指定的路径。

比如,检查位于桌面的文件夹 books 以及其下属的文件 book.txt:

import os
a = os.path.isdir("C:\\Users\\****\\Desktop\\books")
print(a)
b = os.path.isdir("C:\\Users\\****\\Desktop\\books\\book.txt")
print(b)

执行以上代码,输出结果为:

True
False

(7)os.path.isfile 函数

os.path.isfile 函数用于检查指定的路径是否为文件,如果指定的路径是文件,则此方法返回 True,否则返回 False,语法格式为:os.path.isfile(“path_obj”),其中, path_obj 表示指定的路径。

比如,检查位于桌面的文件夹 books 以及其下属的文件 book.txt:

import os
a = os.path.isfile("C:\\Users\\****\\Desktop\\books")
print(a)
b = os.path.isfile("C:\\Users\\****\\Desktop\\books\\book.txt")
print(b)

执行以上代码,输出结果为:

False
True

(8)os.path.getsize 函数

os.path.getsize 函数用于返回文件或文件夹的大小,如果是文件,那么返回文件的大小;如果是文件夹,那么就返回文件夹的大小,文件夹大小是指文件夹这个数据结构在文件系统中占用的大小,语法格式为:os.path.getsize(“path_obj”),其中,path_obj 表示目标路径。

import os
a = os.path.getsize("C:\\Users\\****\\Desktop\\books")
print(a)
b = os.path.getsize("C:\\Users\\****\\Desktop\\books\\zhaopian.png")
print(b)

执行以上代码,语法格式为:

0
190181

(9)os.path.abspath 函数

os.path.abspath 函数用于返回绝对路径,语法格式为:os.path.abspath(“path_obj”),其中,path_obj 表示目标路径。

import os
a = os.path.abspath(".")                                                # 返回当前目录的绝对路径
print(a)
b = os.path.abspath("..")                                               # 返回上级目录的绝对路径
print(b)
c = os.path.abspath("C:\\Users\\****\\Desktop\\books\\zhaopian.png")
print(c)

执行以上代码,语法格式为:

C:\Users\****\Desktop\Python学习\Demo1
C:\Users\****\Desktop\Python学习
C:\Users\****\Desktop\books\zhaopian.png

(10)os.path.getatime 函数

os.path.getatime 函数用于返回对指定路径的最后访问时间,语法格式为:os.path.getatime(“path_obj”),其中,path_obj 表示指定路径。此方法返回一个浮点值,该值表示自纪元以来的秒数。如果文件不存在或无法访问,则此方法引发OSError。纪元代表时间开始的时间点,从1970年1月1日算起。

import os
a = os.path.getatime("C:\\Users\\****\\Desktop\\books\\zhaopian.png")
print(a)

执行以上代码,语法格式为:

1682523613.2431264

time.ctime 函数用于把一个时间戳(按秒计算的浮点数)转化为类似于"Tue Dec 11 18:07:14 2008"的24个字符的字符串的形式,语法格式为:time.ctime(“sec”),其中,sec 表示要转换为字符串时间的秒数。

import os
import time
a = os.path.getatime("C:\\Users\\****\\Desktop\\books\\zhaopian.png")
b = time.ctime(a)
print(b)

执行以上代码,输出结果为:

Wed Apr 26 23:40:13 2023

(11)os.path.getctime 函数

os.path.getctime 函数用于返回指定路径的最后创建时间,语法格式为:os.path.getctime(“path_obj”),其中,path_obj 表示指定路径。此方法返回一个浮点值,该值表示自纪元以来的秒数。如果文件不存在或无法访问,则此方法引发OSError。纪元代表时间开始的时间点,从1970年1月1日算起。

import os
a = os.path.getctime("C:\\Users\\****\\Desktop\\books\\zhaopian.png")
print(a)

执行以上代码,输出结果为:

1682002876.1564693

用 time.ctime 函数转换格式:

import os
import time
a = os.path.getctime("C:\\Users\\****\\Desktop\\books\\zhaopian.png")
b = time.ctime(a)
print(b)

执行以上代码,输出结果为:

Thu Apr 20 23:01:16 2023

23 错误和异常

Python 中编写程序时遇到的错误可大致分为两类,分别为语法错误和异常。

23.1 语法错误

语法错误,也就是解析代码时出现的错误。当代码不符合 Python 语法规则时,Python 解释器在解析时就会报出 SyntaxError 语法错误,与此同时还会明确指出最早探测到错误的语句。

while True
    print("Hello world")

执行以上代码,输出结果为 :

  File "C:\****", line 1
    while True
              ^
SyntaxError: expected ':'

这个例子中,函数 while True 被检查到有错误,是它后面缺少了一个冒号。

语法错误多是开发者疏忽导致的,属于真正意义上的错误,是解释器无法容忍的,因此,只有将程序中的所有语法错误全部纠正,程序才能执行。

23.2 异常

即便 Python 程序的语法是正确的,在运行它的时候,也有可能发生错误。运行时检测到的错误被称为异常。如果编写了处理异常的代码,程序将继续运行;如果未对异常做处理,程序将进行停止,并显示一个 traceback,其中包含有关异常的报告。Python 中常见的异常类型有 AssertionError, AttributeError, IndexError, KeyError, NameError, TypeError, ZeroDivisionError, NameError, IndentationError, ValueError 等。

(1)AssertionError

当 assert 关键字后的条件为假时,程序运行会停止并抛出 AssertionError 异常。

assert 1 > 2

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 1, in <module>
    assert 1 > 2
           ^^^^^
AssertionError

(2)AttributeError

属性错误,当试图访问的对象属性不存在时抛出的异常。

a = (1, 2, 3)
a.append(4)

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 2, in <module>
    a.append(4)
    ^^^^^^^^
AttributeError: 'tuple' object has no attribute 'append'

(3)IndexError

索引错误,使用的索引不存在或者索引超出序列范围时抛出的异常。

a = [1, 2, 3, 4]
a[10]

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 2, in <module>
    a[10]
    ~^^^^
IndexError: list index out of range

(4)KeyError

字典中查找一个不存在的关键字时抛出的异常。

a = {"name1": 1, "name2": 2}
print(a["name3"])

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 2, in <module>
    print(a["name3"])
          ~^^^^^^^^^
KeyError: 'name3'

(5)NameError

尝试访问一个未声明的变量时抛出的异常。

print(a)

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 1, in <module>
    print(a)
          ^
NameError: name 'a' is not defined

(6)TypeError

类型错误,不同类型数据之间无效操作时抛出的异常。

print(1 + "a")

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 1, in <module>
    print(1 + "a")
          ~~^~~~~
TypeError: unsupported operand type(s) for +: 'int' and 'str'

(7)ZeroDivisionError

除法运算中除数为 0 时抛出的异常。

print(1/0)

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 1, in <module>
    print(1/0)
          ~^~
ZeroDivisionError: division by zero

(8)NameError

访问的变量名不存在时抛出的异常。

print(a)

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 1, in <module>
    print(a)
          ^
NameError: name 'a' is not defined

(9)IndentationError

缩进错误,错误使用缩进量时抛出的异常。

for i in [1, 2, 3]:
print(i)

执行以上代码,输出结果为:

  File "C:\****", line 2
    print(i)
    ^
IndentationError: expected an indented block after 'for' statement on line 2

(10)ValueError

值错误,当传给对象的参数类型不正确时抛出的异常。

int("a")

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 1, in <module>
    int("a")
ValueError: invalid literal for int() with base 10: 'a'

(11)FileNotFoundError

当读取或写入文件的过程中,找不到文件或文件路径不正确时抛出的异常。

file_obj = open("hello.txt", "r")
file_obj.close()

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 1, in <module>
    file_obj = open("hello.txt", "r")
               ^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'hello.txt'

23.3 异常处理

(1)try-except 语句

try-except 语句用于检测 try 子句中的错误,从而令 except 语句捕获异常信息并作出应对和处理。

try-except 语句

● 首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句);
● 如果没有异常发生,忽略 except 子句,try 子句执行后结束;
● 如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行;
● 如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。

a = 5
b = 0
try:
    c = a / b
except:
    print("It is wrong.")

执行以上代码,输出结果为:

It is wrong.

① 捕获指定类型的异常

except 后指定具体的异常名称,表示捕获指定类型的异常。比如 AssertionError, AttributeError, IndexError, KeyError, NameError, TypeError, ZeroDivisionError, NameError, IndentationError, ValueError 等,分别表示各自的 except 代码块可以处理异常的具体类型。

a = 5
b = 0
try:
    c = a / b
except ZeroDivisionError:
    print("It is ZeroDivisionError.")

执行以上代码,输出结果为:

It is ZeroDivisionError

② 同时捕获多个异常

一个 except 子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组。

a = 5
b = 0
try:
    assert a < b
    c = a / b
except (ZeroDivisionError, AssertionError):
    print("It is ZeroDivisionError or AssertionError.")

执行以上代码,输出结果为:

It is ZeroDivisionError or AssertionError.

③ 捕获某种未指定的类型的异常

一个 try-except 语句可能包含多个 except 子句,分别来处理不同的特定的异常,最多只有一个分支会被执行。

a = 5
b = 0
try:
    c = a / b
except ZeroDivisionError:
    print("It is ZeroDivisionError.")
except AssertionError:
    print("It is AssertionError.")

执行以上代码,输出结果为:

It is ZeroDivisionError.

(2)获取异常的信息描述

① 获取所有异常的信息描述

因为 except 默认捕获的异常类型是 Exception(表示常规错误的基类),所以 except 子句总是捕获所有异常。获取所有异常的信息描述的语法格式为:except Exception as e :

其中,Exception 表示捕获的所有异常;e 表示存储异常的基本信息,在该 except 代码块下可以实现 e 的打印。

a = 5
b = 0
try:
    c = a / b
except Exception as e:
    print(e)

执行以上代码,输出结果为:

division by zero

② 获取指定类型的异常的信息描述

获取指定类型的异常的信息描述的语法格式为:except error as e :

其中,error 表示要捕获的指定类型的异常;e 表示存储异常的基本信息,在该 except 代码块下可以实现 e 的打印。

a = 5
b = 0
try:
    c = a / b
except ZeroDivisionError as e:
    print(e)

执行以上代码,输出结果为:

division by zero

③ 获取同时捕获的多个异常的信息描述

获取同时捕获的多个异常的信息描述的语法格式为:except (error1, error2 … errorn) as e :

其中,(error1, error2 … errorn) 表示同时捕获的多个异常;e 表示存储异常的基本信息,在该 except 代码块下可以实现 e 的打印。

a = 5
b = 0
try:
    c = a / b
except (AssertionError, ZeroDivisionError) as e:
    print(e)

执行以上代码,输出结果为:

division by zero

(3)try-except-else 语句

若使用 else 子句,则必须将其后接于 except 子句之后,且只能有一个 else 子句。else 子句将在 try 子句未发生任何异常时执行。通常,使用 else 子句比将所有语句都放在 try 语句中灵活性更强,效果更好,因为这样可避免一些难以预料且 except 无法捕获的异常。
try-except-else 语句
① 当 try 子句发生异常时:

a = 5
b = 0
try:
    c = a / b
except AssertionError as e1:
    print(e1)
except ZeroDivisionError as e2:
    print(e2)
else:
    print(c)

执行以上代码,输出结果为:

division by zero

② 当 try 子句未发生异常时:

a = 5
b = 5
try:
    c = a / b
except AssertionError as e1:
    print(e1)
except ZeroDivisionError as e2:
    print(e2)
else:
    print(c)

执行以上代码,输出结果为:

1.0

(4)try-except-else-finally 语句

若使用 finally 子句,则必须将其后接于最后,且只能有一个 finally 子句。无论异常有无发生,finally 子句都将执行。因此,finally 子句常用于存放一些必定要执行的内容或操作。

finally 子句常用于定义无论在任何情况下都会执行的清理行为。若一个异常在 try 子句里(或在 except 子句和 else 子句里)被抛出,而又没有任何的 except 子句将其捕获,那么该异常将会在 finally 子句执行后被抛出。
try-except-else-finally 语句

① 当 try 子句发生异常时:

a = 10
b = "1"
try:
    result = a / b
except ZeroDivisionError:
    print("division by zero!")
else:
    print("the result is", result)
finally:
    print("Program execution completed.")

执行以上代码,输出结果为:

Program execution completed.
Traceback (most recent call last):
  File "C:\****", line 4, in <module>
    result = a / b
             ~~^~~
TypeError: unsupported operand type(s) for /: 'int' and 'str'

② 当 try 子句未发生异常时:

a = 10
b = 1
try:
    result = a / b
except ZeroDivisionError:
    print("division by zero!")
else:
    print("the result is", result)
finally:
    print("Program execution completed.")

执行以上代码,输出结果为:

the result is 10.0
Program execution completed.

23.4 抛出异常

Python 中可以使用 raise 语句触发异常,基本格式如下:

raise 异常类 # 引发异常时会隐式地创建类对象
raise 异常类对象 # 引发异常类实例对象对应的异常
raise # 重新引发刚刚发生的异常

(1)使用异常类名引发异常

当 raise 语句指定异常的类名时,会创建该类的实例对象,然后引发异常,语法格式为:raise error,其中,error 表示异常类名。

raise ZeroDivisionError

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 1, in <module>
    raise ZeroDivisionError
ZeroDivisionError

(2)使用异常类对象引发异常

通过显式地创建异常类的实例,直接使用该实例对象来引发异常。

error = NameError
raise error

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 2, in <module>
    raise error
NameError

(3)传递异常

不带任何参数的 raise 语句,可以再次引发刚刚发生过的异常,作用就是向外传递异常。

try:
    raise NameError
except:
    print("It is wrong")
    raise

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 2, in <module>
    raise NameError
NameError

上述示例中,try 子句使用 raise 语句抛出了 NameError 异常,程序会跳转到 except 子句中执行输出打印语句,然后使用 raise 语句再次引发刚刚发生的异常,导致程序出现错误而终止运行。

(4)指定异常的描述信息

当使用 raise 语句抛出异常时,还能给异常类指定描述信息,语法格式为:raise error (“指定异常的描述信息”),error 表示异常类名。

raise IndexError("索引下标超出范围")

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 1, in <module>
    raise IndexError("索引下标超出范围")
IndexError: 索引下标超出范围

(5)异常引发异常

如果要在异常中抛出另外一个异常,可以使用 raise-from 语句实现。

try:
     number
except Exception as exception:
     raise IndexError("下标超出范围") from exception

执行以上代码,输出结果为:

Traceback (most recent call last):
  File "C:\****", line 2, in <module>
    number
NameError: name 'number' is not defined

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\****", line 4, in <module>
    raise IndexError("下标超出范围") from exception
IndexError: 下标超出范围

作业:

(1)通过 Python 新建一个文件 gushi.txt,定义一个函数,将古诗《静夜思》写入文件中;

(2)另外定义一个函数,读取指定文件 gushi.txt,将内容复制到 copy.txt 中;

(3)尽可能完善代码,添加异常处理。

def writefile(gushi):
    f = open("gushi.txt", "w", encoding='utf-8')
    for i in gushi:
        f.write(i)
        f.write('\n')
    f.close()


def readfile():
    f = open("gushi.txt", "r", encoding='utf-8')
    copy1 = f.readlines()
    f.close()
    f = open("copy.txt", "w", encoding='utf-8')
    for i in copy1:
        f.write(i)
    f.close()


str = ["床前明月光", "疑似地上霜", "举头望明月", "低头思故乡"]
try:
    writefile(str)
    readfile()
except Exception as result:
    print(result)
finally:
    print("结束运行")

执行以上代码,输出结果为:

结束运行
gushi.txt 中包含的内容为:

床前明月光
疑似地上霜
举头望明月
低头思故乡
copy.txt 中包含的内容为:

床前明月光
疑似地上霜
举头望明月
低头思故乡

24 pip

24.1 pip 定义

pip 是 Python 包的管理工具,提供了对 Python 包的查找、下载、安装、卸载的功能;pip 已内置于 Python 3.4 和 2.7 及以上版本。

24.2 pip 常用指令

(1)安装包

要安装一个 Python 包,可以使用以下命令:

pip install package_name

其中,package_name 表示要安装的 Python 包的名字,pip 会自动从 Python 包索引中下载并安装该包及其依赖项,比如安装 NumPy:

pip install numpy

(2)升级包

要升级已安装的包,可以使用以下命令:

pip install --upgrade package_name

其中,package_name 表示要升级的 Python 包的名字,pip 会下载最新版本的包并进行安装,比如升级 NumPy:

pip install --upgrade numpy

(3)卸载包

要卸载一个已安装的包,可以使用以下命令:

pip uninstall package_name

其中,package_name 表示要卸载的 Python 包的名字,比如卸载 NumPy 包:

pip uninstall numpy

(4)查看已安装的包

要查看已安装的包及其版本信息,可以使用以下命令:

pip list

(5)安装指定版本的包

要安装指定版本的包,可以使用以下命令:

pip install package_name==version_number

其中,package_name 表示要安装的 Python 包的名字,version_number 表示要安装的 Python 包的版本号,比如安装 1.19.3 版本的 NumPy:

pip install numpy==1.19.3

(6)安装包的特定部分

有些包可能包含多个部分,可以使用以下命令来安装特定的部分:

pip install package_name[part_name]

其中,package_name 表示要安装的 Python 包的名字,part_name 表示要安装的 Python 包的特定部分的名字;

(7)查看包的详细信息

要查看包的详细信息,可以使用以下命令:

pip show package_name

其中,package_name 表示要查看的 Python 包的名字,比如查看 NumPy 的详细信息:

pip show numpy

(8)清理缓存

pip 安装包的时候,会下载并缓存一些包以便后续使用,这可能会占用磁盘空间;要清理缓存,可以使用以下命令:

pip cache purge

(9)安装本地包

要安装本地的 Python 包,可以使用以下命令:

pip install /path/to/package

其中,/path/to/package 表示要安装的 Python 包在本地的路径,比如安装本地路径中的 /home/user/my_package,可以使用以下命令:

pip install /home/user/my_package
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值