python基础
- 解释型语言
- 动态类型语言
- 区分大小写、不区别单引号和双引号
内存管理
有专用堆空间管理,所有python对象和数据结构都位于私有堆中,程序员无权访问此私有堆
- 堆空间的分配由内存管理器完成
- 有内置的垃圾收集器
类型转换
函数 | 作用 |
---|---|
int() | 将任何数据类型转成整数类型 |
float() | 将任何数据类型转换成float类型 |
ord() | 将字符转换为整数 |
hex() | 将整数转换为十六进制 |
oct() | 将整数转换为八进制 |
tuple() | 转换为元组 |
set() | 转换为set后分会类型 |
list() | 将任何数据类型转换为列表类型 |
dict() | 将顺序(键,值)的元组转换为字典 |
str() | 将整数转换为字符串 |
list和tuple
list
- 切片操作
- 列表生成式
1
2
3
4
L = [1, 2, 3, 4]
a = L[:2] # 取前2个元素
b = L[-2:] # 取后2个元素
c = L[::-1] # 逆置
1
2
3
4
5
a = [x * x for x in range(1, 11)]
b = [x * x for x in range(1, 11) if x % 2 == 0]
c = [m + n for m in 'ABC' for n in 'XYZ']
path = [d for d in os.listdir('.')]
tuple
一旦初始化就不能修改,代码更加安全(tuple
的每个元素,指向永远不变)
1
2
3
# 初始化一个元素
t = (3,)
dict和set
dict
查找和插入的速度极快,不会随着key
的增加而变慢;list
查找和插入的时间随着元素的增加而增加
set
中的key
不能重复
函数的参数
- 位置参数
- 默认参数:必须指向不变对象
- 可变参数:传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个
tuple
- 关键字参数:传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个
dict
- 命名关键字参数:限制关键字参数的名字。传入的时候必须加上参数名
1
2
3
4
5
6
7
8
9
10
# 可变参数
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
nums = [1, 2, 3]
a = calc(1, 2, 3)
b = calc(*nums) # 把list或tuple的所有元素作为可变参数传进去
1
2
3
4
5
6
7
# 关键字参数
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
extra = {'city': 'hangzhou', 'job': 'Engineer'}
a = person('jack', 23, city='hangzhou', job='Engineer')
b = person('ross', 22, **extra)
1
2
3
4
5
6
# 命名关键字参数
def person(name, age, *, city, job):
print(name, age, city, job)
def person(name, age, *args, city, job): # 已经有了一个可变参数
print(name, age, args, city, job)
列表项随机化
1
2
3
4
5
from random import shuffle
a = ["red", "blue", "green", "yellow"]
shuffle(a)
print(a)
生成随机数
1
2
3
4
5
6
7
8
import random
random.random() # 生成0到1之间的随机浮点数
random.randint() # 生成a到b之间的随机整数
random.uniform() # 生成a到b之间的随机浮点数
random.choice(sequence) # 从sequence中随机选取一个元素,sequence可以是列表、元素、字符串等
random.choices(sequence, k=n) # 从sequence中随机选取n个元素,元素可以重复,结果是一个列表
random.sample(sequence, k=n) # 从sequence中随机选取n个元素,元素不重复,结果是一个列表
生成器(generator)
生成器中的列表元素可以按照某种算法推算出来,不必创建完整的list,节省了大量的空间
1
2
3
4
5
6
g = (x * x for x in range(10)) # 使用圆括号而不是方括号
# 打印元素
next(g) # 方法一,直到没有数据时抛出StopIteration错误
for n in g: # 方法二
print(n)
对于复杂的算法,可以使用函数来实现生成器。需要使用到yield
关键字。每次调用next()
时,遇到yield
语句就返回。再次执行时从上次返回的yield
语句处继续执行。
生成函数不会一次性返回所有的结果,而是每调用时生成一个值,并在生成一个值后在暂停执行,知道下一次调用
1
2
3
4
5
6
7
8
9
10
11
12
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
f = fib(6) # 生成一个迭代器对象f
next(f)
for n in fib(6):
print(n)
生成器是一种特殊的迭代器
可迭代对象
实现了__iter__
方法或__getitem__
方法的对象。可迭代对象可以被迭代,意味着可以使用for
循环遍历其中的元素
- 例如:列表、元素、字符串、集合、字典都是可迭代对象
1
2
3
4
5
6
my_list = [1, 2, 3]
iter_obj = iter(my_list) # 或者 my_list.__iter__()
print(next(iter_obj)) # 输出1
print(next(iter_obj)) # 输出2
print(next(iter_obj)) # 输出3
print(next(iter_obj)) # 抛出 StopIteration 异常
1
2
3
4
5
from collections.abc import Iterable
# 判断对象是否为可迭代对象
isinstance('abc', Iterable) # True
isinstance([1, 2, 3], Iterable) # True
1
2
3
4
5
6
7
# 在循环中同时迭代索引和元素本身
for i, value in enumerate(['a', 'b', 'c']):
print(i, value)
# 在循环中引用两个变量
for x, y in [(1, 1), (2, 4), (3, 9)]:
print(x, y)
迭代器(Iterator)
可以被next()
函数调用并不断返回下一个值的对象称为迭代器
1
2
3
4
from collections.abc import Iterator
isinstance((x for x in range(10)), Iterator) # True
isinstance('abc', Iterator) # False
生成器都是迭代器
序列化和反序列化
pickling
是python中一种将对象结构转换为字节流的过程,成为序列化。与之相反为unpickling
,也就是将字节流转换为对象结构,称为反序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pickle
data = {
'name': 'jack',
'age': 21,
'offer': ["huawei", "tencent", "bytedance"]
}
# 文件
with open('data.pkl', 'wb') as file:
pickle.dump(data, file)
with open('data.pkl', 'rb') as file:
loaded_data = pickle.load(file)
# 内存
byte_stream = pickle.dumps(data)
loaded_data = pickle.loads(byte_stream)
pickle
序列化后只能用于python,而且不同版本的python可能会不兼容。所以,要在不同的编程语言之间传递对象,就必须把对象序列化成标准格式,因此可以使用json
格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import json
data = {
'name': 'jack',
'age': 21,
'offer': ["huawei", "tencent", "bytedance"]
}
# 文件
with open('data.json', 'wb') as file:
json.dump(data, file)
with open('data.json', 'rb') as file:
load_data = json.load(file)
# 内存
byte_stream = json.dumps(data)
loaded_data = json.loads(byte_stream)
使用json
序列化类对象。对于类,需要提供一个把类实例转换成字典对象的方法;对于反序列化也是同理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import json
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
s = Student('alice', 22, 92)
data = json.dumps(s, default=lambda obj: obj.__dict__)
# 通常class的实例都有一个__dict__属性,它就是一个dict,用来存储实例变量
print(data)
rebuild = json.loads(data, object_hook=lambda d : Student(d["name"], d["age"], d["score"]))
print(rebuild)
map和reduce
map
:将可迭代对象作用在一个函数上,并将结果以迭代器的形式返回
1
2
3
4
def f(x):
return x * x
r = list(map(f, [1, 2, 3, 4, 5]))
reduce
:把一个函数作用在一个序列上(这个函数必须接受两个参数),然后把结果继续和序列下一个元素做累计计算
1
2
3
4
5
6
from functools import reduce
def add(x, y):
return x + y
sum = reduce(add, [1, 3, 5, 7, 9])
装饰器
在不修改原函数代码的情况下,为原函数添加额外的功能。装饰器本质上就是一个函数,它接收一个函数作为参数,并返回一个新的函数
应用:日志记录
1
2
3
4
5
6
7
8
9
10
11
def log_decorator(func):
def wrapper(*args, **kw):
print(f"Calling function {func.__name__}")
result = func(*args, **kw)
print(f"Function {func.__name__} completed")
return result
return wrapper
@log_decorator
def add(a, b):
return a + b
IO编程
- 同步IO
- 发起IO请求的进程会被阻塞,直到IO操作完成
- 异步IO
- 进程发起IO操作后,不会被阻塞,可以继续执行其他任务
- 性能高于同步IO,但是编程模型复杂
文件读写
现代OS不允许普通的程序直接操作磁盘,都需要使用OS提供的接口去读写磁盘
1
2
3
f = open('d:/code/test.txt', 'r') # 文件不存在会抛出一个IOError的错误
print(f.read())
f.close()
1
2
3
with open('d:/code/test.txt', 'r') as f:
print(f.read())
# 如果无法确定大小,则反复调用f.read(size)进行读取,避免内存爆炸
这样的写法和try...finally
是一样的,但是代码更加简洁,不需要调用f.close()
的方法,with
语句会自动调用
1
2
for line in f.readlines():
print(line.strip())
读取二进制文件
1
2
with open('d:/code/test.jpg', 'rb') as f:
print(f.read())
读取特定字符编码的文件,并将处理编码错误的字符
1
2
with open('d:/code/test.txt', 'r', encoding='gbk', errors='ignore'):
print(f.read())
写文件
写文件的时候,操作系统不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入,只有关闭文件才会写入
1
2
3
4
5
6
7
# 直接覆盖
with open('d:/code/test.txt', 'w') as f:
f.write('hello wolrd')
# 追加到文件末尾
with open('d:/code/test.txt', 'a') as f:
f.write('hello wolrd')
StringIO和BytesIO
在内存中读写
StringIO
只能操作str
1
2
3
4
5
6
7
8
9
10
11
12
13
from io import StringIO
f = StringIO()
f.write('hello')
f.write(' world')
print(f.getvalue())
f = StringIO('Hello\nGoodbye')
while True:
s = f.readline()
if s == '':
break
print(s.strip())
BytesIO
可以操作二进制数据
1
2
3
4
5
6
7
8
from io import BytesIO
f = BytesIO()
f.write('工作'.encode('utf-8'))
print(f.getvalue())
f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
print(f.read())
操作文件和目录
1
2
3
4
5
import os
print(os.name)
if os.name == 'posix':
print(os.uname())
获取环境变量
1
2
3
4
5
import os
print(os.environ)
print(os.environ.get('PATH'))
文件和目录操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 查看当前目录的绝对路径
os.path.abspath('.')
# 创建一个目录
os.mkdir('d:/code/testdir')
# 删除一个目录
os.rmdir('d:/code/testdir')
# 拼接路径
os.path.join('d:/code', 'testdir')
# 拆分路径
os.path.split('d:/code/testdir/file.txt')
# 拆分文件扩展名
os.path.splitext('d:/code/testdir/file.txt')
# 文件重命名
os.rename('./test.txt', 'test.py')
# 删除文件
os.remove('test.py')
# 复制文件
import shutil
shutil.copyfile(source_file, destination_file) # 如果目标文件已存在,它将被覆盖
1
2
3
4
5
# 列出当前目录下的所有目录
[x for x in os.listdir('.') if os.path.isdir(x)]
# 列出当下目录所有python文件
[x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext[1] == '.py']
协程
微线程,coroutine
可以在一个线程内实现多个任务的切换和执行。协程的调度完全由用户程序控制,而不是操作系统。在一个线程中,可以有多个协程,这些协程可以在执行过程中暂停,将执行权交给其他协程,然后在合适的时候再恢复执行,从而实现多个任务之间的协作式多任务处理。
优势
- 极高的执行效率。
- 在一个线程中,没有线程切换的开销
- 不需要多线程的锁机制。
- 不存在同时写变量冲突
- 控制共享资源不加锁,只需判断状态
await
表示在这个地方等待子函数执行完成,再往下执行(在并发操作中,把程序控制权交给主程序,让它分配其他协程执行)
异步IO
异步IO模型需要一个消息循环,在消息循环中,主线程不断地重复“读取消息-处理消息”这一过程。这样,一个线程就可以同时处理多个IO请求,并且没有切换线程的操作。
- 同步IO模型下,主线程只能挂起
- 异步IO模型下,主线程并没有休息,而是在消息循环中继续处理其他消息
typing和collections
typing
主要用于提供类型注解,比如 List
, Dict
, Tuple
, Set
, Optional
, Union
, Callable
等
1
2
3
4
5
6
7
8
9
from typing import List, Dict, Optional
def process_data(data: List[int]) -> Dict[str, int]:
return {"length": len(data), "sum": sum(data)}
def find_user(user_id: int) -> Optional[str]:
if user_id == 1:
return "Alice"
return None
collections
是python内建的一个集合模块,提供了许多高效的数据结构,用于替代 Python 内置的数据类型(如 list
, dict
, tuple
等)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from collections import defaultdict, Counter, deque
# defaultdict 带默认值的字典
word_count = defaultdict(int)
for word in ["apple", "banana", "apple"]:
word_count[word] += 1
# Counter 计数器,用于统计元素出现次数
count = Counter(["apple", "banana", "apple"])
print(count) # 输出: Counter({'apple': 2, 'banana': 1})
# deque 双端队列,支持高效的头尾操作
queue = deque([1, 2, 3])
queue.append(4) # 添加到尾部
queue.popleft() # 从头部移除
海象运算符
:=
- python3.8引入
- 该运算符允许在表达式内部进行变量赋值
- 旨在简化代码提升效率
海象运算符:=
可以在如if语句或while循环中使用,以避免重复计算表达式的值
1
2
if a := 15 > 10:
print('hello world')
1
2
3
fp = open("test.txt", "r")
while line := fp.readline():
print(line.strip())
f-string变量名
获取值的同时获取变量名
1
2
number = 29
print(f"{number=}") # 输出:number=29
双星号 (**)
在函数调用时,使用**
可以将字典的键值对解包为关键字参数(key=value 形式),传递给函数。
1
2
3
4
5
def func(a, b, c):
print(a, b, c)
params = {'a': 1, 'b': 2, 'c': 3}
func(**params) # 等价于 func(a=1, b=2, c=3)