Post

python基础

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

可以在一个线程内实现多个任务的切换和执行。协程的调度完全由用户程序控制,而不是操作系统。在一个线程中,可以有多个协程,这些协程可以在执行过程中暂停,将执行权交给其他协程,然后在合适的时候再恢复执行,从而实现多个任务之间的协作式多任务处理。

优势

  1. 极高的执行效率。
    • 在一个线程中,没有线程切换的开销
  2. 不需要多线程的锁机制。
    • 不存在同时写变量冲突
    • 控制共享资源不加锁,只需判断状态

await表示在这个地方等待子函数执行完成,再往下执行(在并发操作中,把程序控制权交给主程序,让它分配其他协程执行)

异步IO

异步IO模型需要一个消息循环,在消息循环中,主线程不断地重复“读取消息-处理消息”这一过程。这样,一个线程就可以同时处理多个IO请求,并且没有切换线程的操作。

  • 同步IO模型下,主线程只能挂起
  • 异步IO模型下,主线程并没有休息,而是在消息循环中继续处理其他消息

typing和collections

typing主要用于提供类型注解,比如 ListDictTupleSetOptionalUnionCallable 等

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 内置的数据类型(如 listdicttuple 等)

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)
This post is licensed under CC BY 4.0 by the author.