函数
1. 函数声明
使用 def 关键字进行函数声明:
# 函数声明
def pokers():
lst1 = ["🍬", "💕", "😀", "🍭"]
lst2 = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]
for i in lst1:
for j in lst2:
print(f"{i}{j}", end=" ")
print()
注:只声明而不调用,函数里面的内容不会执行。
2. 函数调用
特点:一次声明,多次调用
3. 函数参数
3.1 实参和形参
形式参数:函数声明时,使用的参数
实际参数:调用函数时,传入的参数
# 计算 1 到 n 的累加和
def get_sum(n):
num = 0
for i in range(1, n + 1):
num = num + i
print(num)
get_sum(100)
get_sum(1000)
# 输出:
5050
500500
其中,n 为形参,100 和 1000 为实参。
3.2 位置参数
- 实参和形参按照位置,一一对应的进行传递参数;
- 也叫做必传参数,如果不传则会报错;
def add(x, y):
print(x + y)
add(1, 2)
add(10, 20)
# 输出:
3
30
3.3 默认参数
- 在函数声明时,给形参赋值一个默认参数。如果函数调用时,如果实际参数没有给这一项,就使用默认参数;
- 也叫做非必传参数,不传的时候取默认值,传了则取实际值;
def add1(x, y=100):
print(x + y)
add1(1, 2)
add1(1)
# 输出:
3
101
3.4 关键字参数
以 **形参=实参 **的形式去传递参数,因此可以不受传递参数的位置、顺序的影响。
def get_sum(m, n):
num = 0
for i in range(m, n + 1):
num = num + i
print(num)
get_sum(1, 1000)
get_sum(n=1000, m=1)
# 输出:
500500
500500
3.5 可变参数
当我们传递参数的值不确定时,可以使用可变参数;
可变参数 args 把参数组装成元组*,可变参数 kwargs 把参数组装成字典*;
def fun1(*args):
print(args)
print(type(args))
def fun2(**args):
print(args)
print(type(args))
fun1(1, 2, 3, 4, 5, "six")
fun2(a=1, b=2, c=3)
# 输出:
(1, 2, 3, 4, 5, 'six')
<class 'tuple'>
{'a': 1, 'b': 2, 'c': 3}
<class 'dict'>
3.6 拆包和装包
拆包:
- 形参中的 *args 其实真正接收数据的是 args,它是一个元组,把传进来的数据放在了 args 这个元组中
- 函数体里的 args 依然是那个元组,但是 *args 的含义就是把元组中的数据进行拆包,也就是把元组中的数据拆成单个数据
- 对于 args 这个元组,如果不对其进行解包,就将其作为实参传给其它以 *args 作为形参的函数时。args 这个元组会看看作一个整体,作为一个类型为元组的数据传入
示例一:
def run(a, *args):
print(a) # 第一个参数传给了a
print(args) # args是一个元组,里面是2和3两个参数
# *args是将这个元组中的元素依次取出来
print("对 args 拆包: ", end="")
print(*args) # *args 相当于 a,b = args
print("将未拆包的数据传给run1: ", end="")
run1(args)
print("将拆包后的数据传给run1: ", end="")
run1(*args)
def run1(*args):
print("输出元组: ", end="")
print(args)
print("对元组进行拆包: ", end="")
print(*args)
run(1, 2, 3) # 后面的2和3
# 输出:
1
(2, 3)
对 args 拆包: 2 3
将未拆包的数据传给run1: 输出元组: ((2, 3),)
对元组进行拆包: (2, 3)
将拆包后的数据传给run1: 输出元组: (2, 3)
对元组进行拆包: 2 3
示例二:
def run(**kwargs):
# kwargs是一个字典
print(kwargs)
print("对 kwargs 拆包: ", end="")
# 此处可以把 **kwargs 理解成对字典进行了拆包
# {"a":1,"b":2} 的 kwargs 字典又被拆成了 a=1, b=2 传递给 run2
# 但是 **kwargs 不能像之前 *args 那样被打印出来看的
run2(**kwargs)
# print(**kwargs)
def run2(a, b): # 此处的参数名一定要和字典的键的名称一致 因为相当于是关键字传参 a=1, b=2
print(a, b)
run(a=1, b=2)
# 输出:
{'a': 1, 'b': 2}
对 kwargs 拆包: 1 2
装包:
- 装包的含义就是把未命名参数和命名参数分别放在元组或者字典中
4. 函数返回值
4.1 默认返回值
如果没有写 return,默认返回空值 None
result1 = print("Hello Python")
print(result1)
# 输出:
None
4.2 多个返回值
如果以逗号分隔返回多个值,则实际返回的是一个元组
def send():
name = "Jarvis"
age = 21
email = "123@qq.com"
return name, age, email
print(send())
# 输出:
('Jarvis', 21, '123@qq.com')
4.3 自定义多个返回值
如果有多个返回值,也可以装在一个列表返回
def send1():
name = "Jarvis"
age = 21
email = "123@qq.com"
return [name, age, email]
print(send1())
# 输出:
['Jarvis', 21, '123@qq.com']
当然也可以返回一个字典
def send2():
name = "Jarvis"
age = 21
email = "123@qq.com"
return {
"name": name,
"age": age,
"email": email
}
print(send2())
# 输出:
{'name': 'Jarvis', 'age': 21, 'email': '123@qq.com'}
文件操作
1. 字符编码
计算机存储信息的基本单元为字节,即 Byte,一个字节等于八个比特位,即 1 Byte = 8 bit
- ASCII
- GBK
- Unicode
- UTF-8
2. 编码与解码
- 当我们创建一个文件时,实际上相当于在磁盘中开辟一块空间,然后按照指定的编码方式存放一堆二进制数据;
- 当我们打开某个文件时,实际上相当于从磁盘的空间中,以指定的解码方式,将二进制数据翻译为对应的信息;
- 这些事情都是系统默默去完成的,我们看不见这个编码和解码的过程;
- 但是如果编码和解码的方式不一致,就会导致翻译过后的数据不是我们真正想要的,可能会出现乱码;
- 由于 ASCII 字符集被其他字符集所兼容,所以即使编码解码方法不一致,英文、数字等字符也不会变成乱码;
3. Python 的编码与解码
3.1 编码
在 Python 中对字符串进行编码,会得到一个新的数据类型:字节 Bytes
str1 = "Hello Python"
str2 = "你好"
result = str1.encode() # 默认 utf-8
result1 = str2.encode() # 默认 utf-8
result2 = str2.encode("gbk")
# utf-8 中一个汉字占 3 个字节
# gbk 中一个汉字占 2 个字节
print(result, type(result), sep='\t')
print(result1, type(result1), sep='\t')
print(result2, type(result2), sep='\t')
# 输出:
b'Hello Python' <class 'bytes'>
b'\xe4\xbd\xa0\xe5\xa5\xbd' <class 'bytes'>
b'\xc4\xe3\xba\xc3' <class 'bytes'>
3.2 解码
用到字节类型的内置方法:decode()
data1 = b'Hello Python'
data2 = b'\xe4\xbd\xa0\xe5\xa5\xbd'
data3 = b'\xc4\xe3\xba\xc3'
print(type(data1), type(data2), type(data3), sep='\t')
print(data1.decode())
print(data2.decode())
print(data3.decode("gbk"))
# 输出:
<class 'bytes'> <class 'bytes'> <class 'bytes'>
Hello Python
你好
你好
4. 文件操作
4.1 r 模式基本流程
首先创建一个 .txt 文件,内容如下:
Jarvis
好累啊呜呜呜呜呜
12345
Csgo Apex GTA5
再写一个 Python 程序如下:
# 该函数返回一个操作句柄
f = open("a.txt", "r", encoding="utf-8")
# 读所有字符
data = f.read()
print(data)
# 输出:
Jarvis
好累啊呜呜呜呜呜
12345
Csgo Apex GTA5
解释:
- 首先,这个 .txt 文件是以 utf-8 的形式编码为字节数据存储在电脑的磁盘中,而 Python 程序不能直接读取磁盘中的内容,他会先向操作系统发出一个请求,然后由操作系统读取磁盘内容,交给程序;
- 操作系统读取到的是字节数据,然后交给程序,但是 mode=“r” 表示程序需要的是字符;
- 于是操作系统会傻乎乎的按照自己默认的编解码方式,对字节数据进行解码,然后交给程序(Windows 操作系统默认为 gbk,Mac 系统默认为 utf-8);
- 但是我们的 .txt 文件是以 utf-8 的形式编码为字节数据,然后存储在磁盘中的,所以操作系统默认以 gbk 进行解码读取,中文就必然会出现乱码;
- 要解决这个问题有两个办法,第一,可以将 .txt 文件更改为 gbk 编码;第二,可以在 open 函数中指定一个编解码方式,例如 encoding=“utf-8” 表示 Python 程序告诉操作系统别用自己的默认,用我指定的规则来解码,这样就可以正确解码;
4.2 rb 模式基本流程
# 字节操作
with open("c.txt", "rb") as f:
data = f.read().decode("utf-8")
print(data)
解释:
类似的,rb 模式表示 Python 程序向操作系统申请,读取磁盘空间的字节数据,不过这次不用麻烦操作系统来解码,这次是操作系统把拿到的数据交给 Python 程序,程序自己来解码。
4.3 wb 模式基本流程
with open("c.txt", "wb") as f:
f.write("你看内容被覆盖了吧".encode("utf-8"))
解释:
类似的,wb 模式表示 Python 程序申请向磁盘中写入字节数据,但是这次不用麻烦操作系统编码,Python 程序自己使用 encode() 编码为字节数据,然后交给操作系统,存入内存即可。
4.4 文件读写操作
方法 | 作用 | 示例 | 结果 |
---|---|---|---|
open() | 返回一个操作句柄 | f = open(“a.txt”, “r”, encoding=“utf-8”) | 可以用 f 来完成各种文件操作 |
f.read() | 读取并返回所有字符,光标会随着读取而移动 | data = f.read() | data 即文件中的所有字符,不建议用来读很大的文件,如视频文件 |
f.tell() | 返回光标位置 | print(f.tell()) | 2998 |
f.seek() | 将光标移动到指定位置 | f.seek(0) | 光标位置回到文件内容最开始处 |
f.read(17) | 读取指定数量的字符,光标也会随着字符的读取而移动 | data1 = f.read(17) | |
f.readline() | 读取一行的内容并返回,光标会随着字符的读取而移动 | line = f.readline() | 注意每一行的末尾自带一个换行 ‘\n’ |
f.readlines() | 读取所有行,将每一行的内容作为一个元素放入列表中,返回一个列表 | lines = f.readlines() | [‘汉皇重色思倾国,御宇多年求不得。\n’, ‘杨家有女初长成,养在深闺人未识。\n’] |
遍历文件 | 相当于每次 line 指向文件的某一行,读取之后释放资源,再指向下一行 | for line in f: | 建议使用这种方式遍历文件,对内存友好,不会一下子读取所有内容 |
f.write() | 写字符到文件,默认不换行,想要换行手动添加 ‘\n’,光标随着写入而移动 | f.write(“Hello Jarvis 哈哈”) | |
f.writelines() | 需要传入一个列表作为参数,将列表中的每一个元素写入文件,换行需要自己添加,光标随着写入移动 | f.writelines(lst) | |
f.close() | 关闭文件,释放资源 | f.close() | |
with open … as | 自动关闭文件,释放资源,更加安全 | with open(“c.txt”, “r”, encoding=“utf-8”) as f: |
补充:
f.read() 时,光标会随着读取字符而移动,因此执行一次 f.read() 后,光标到达文件内容的末尾,第二次 f.read() 实际上什么也读不到,所以只会打印出一次文件的内容。
# 该函数返回一个操作句柄
f = open("长恨歌.txt", "r", encoding="utf-8")
# 读所有字符
data = f.read()
print(data)
data = f.read()
print(data)
如果想到将文件内容 f.read() 两次,需要将光标移动到文件最开始处,如下所示:
# 该函数返回一个操作句柄
f = open("长恨歌.txt", "r", encoding="utf-8")
# 读所有字符
data = f.read()
print(data)
f.seek(0)
data1 = f.read()
print(data1)
不一定每次打开一个文件对象时,都要创建一个文件句柄对象:
f = open("c.txt", "w", encoding="utf-8")
lines = open("a.txt", "r", encoding="utf-8").readlines()
f.writelines(lines)
# 追加模式
open("c.txt", "a", encoding="utf-8").write("Hello, apple\n")
open("c.txt", "a", encoding="utf-8").write("Hello, banana")
一种比 f.close() 更加安全的资源释放 with open
5. 图片拷贝案例
拷贝图片的实质,就是先读取图片的二进制字节数据,然后写到指定的位置:
with open(r"F:\Code\Java\background.jpg", "rb") as fr:
data = fr.read()
with open("copy.jpg", "wb") as fw:
fw.write(data)
6. 爬虫下载高清图片
首先要安装 requests 模块,在控制台输入:pip install requests 即可下载安装
如何删除 requests 模块,在控制台输入:pip uninstall requests 即可卸载
使用 requests.get() 获取网页资源,然后 res.content 即可得到二进制字节数据,写入指定的位置即可
# 爬虫下载高清图片
import requests
# 得到章若楠图片的二进制数据
res = requests.get("https://img-baofun.zhhainiao.com/fs/af24bceea223a09c25194ae06f629daf.jpg")
with open("章若楠.jpg", "wb") as f:
f.write(res.content)