Python 环境
anaconda 安装
是一个python解析器、大部分扩展包和依赖项,支持所有操作系统。
一直下一步,看到这里,不创建快捷方式,不加入环境,安装python3.13
将安装目录,加入到环境变量。就可以使用 python了。
还有以下目录,也要加入环境变量
虚拟环境
有时候版本之间会有冲突,可以使用虚拟环境安装不同版本的Python。
入门
基本
查看关键词
help('keywords')
数据类型
a = 10
i = type(a)
print(i) # <class 'int'>
# 元组类型,不可变
g = (20, 30, 40, 20)
print(type(g)) # <class 'tuple'>
# set 类型,自动去重
k = {10, 10, 20, 20, 30}
print(k) # {10, 20, 30}
print(type(k)) # <class 'set'>
运算符
print(100 / 4) # 25.0 非整除
print(100 // 4) # 25 整除
输入操作
password = input('请输入你的密码') # 返回 Str 类型
num = int(password) # 转换成 int
print(num) # 2323
转换函数
password = input('请输入你的密码') # 返回 Str 类型
int(password) # 转换成整数
float(password) # 转换成浮点数
str(password) # 转换成字符串
bool(password) # 转换成 bool
# 自动推断是什么类型,进行转换。不能转换成字符串
eval(password)
制表符与输出
# \n 换行符,\t 制表符
print('hello\nword') # hello
# word
print('1\t2\t3') # 1 2 3
# 分隔符和结束符
print(10, 20, 30, sep='-', end='2') # 10-20-302
格式化输出
使用 : 进一步对数据进行处理,.2 表示保留两位小数
# 我的名字叫做小明,今年23岁!
name = '小明'
age = 23
print(f'我的名字叫做{name},今年{age}岁!')
# 蔬菜只要3.50元/斤
price = 3.5
print(f'蔬菜只要{price:.2f}元/斤')
另一种写法,单个和多个,.2f 是保留几位小数,%5d是用空格填充,补齐5个,%05d是用0填充补齐5个。
# 占位符,%s 字符串,%d 整数,%f 浮点数
name = 'zhangsan'
print('我的姓名是:%s' % name)
# 占位符,%s 字符串,%d 整数,%f 浮点数
name = 'zhangsan'
age = 22
print('我的姓名是:%s,年龄:%05d' % (name, age))
条件判断
age = 15
if 0 <= age <= 24: # 这里是链式调用
print('2')
else:
print('1')
for 循环
for i in range(5):
print(i) # 输出 0 到 4
for i in range(1, 6):
print(i) # 输出 1 到 4
for i in range(1, 10, 2): # 随机9个,从1开始,步长 2
print(i) # 1,3,5,7,9
循环带 else
如果正常执行完毕,循环中的else会执行,如果非正常,比如break,else不会执行
str = 'abcde'
for i in str:
if i == 'c':
continue
print(i)
else:
print('for循环结束了')
# while 表达式:
# print()
# else:
# print('while循环正常结束')
模块化
import 全部导入
import random
print(random.randint(1, 3))
form 按需导入
from random import randint # 从 random 中导入 randint
print(randint(1, 3))
random 模块
# 随机生成数字
print(random.randint(1, 100))
# 随机在容器类型中选取一个
print(random.choice('abcdefghijklmnopqrstuvwxyz')) # t
# 随机在容器类型中选取指定数量的
print(random.choices('abcdefghijklmnopqrstuvwxyz', k=4)) # ['i', 'a', 'e', 'd']
# 等于上面
print(random.sample('abcdefghijklmnopqrstuvwxyz', k=4)) # ['i', 'a', 'e', 'd']
案例使用打散函数,生成验证码
import random
s = 'abcdefghijklmnopqresuvwxyz'
# 将字符串转换成列表
list = list(s)
# 随机打散该列表
random.shuffle(list)
# 取前四个,转换成字符串
print(''.join(list[:4]))
容器
列表
增删改查
list1 = ['刘备', '关羽']
# 添加
list1.append('张飞') # 尾部追加
# 插入到指定位置
# list1.insert(1, '吕布')
# 修改
list1[1] = '关平'
# 删除
list1.remove('关平')
# 根据索引删除
# list1.pop(1)
# del(list1[1])
print(list1)
# 查询
print(list1[0])
# 遍历
for e in list1:
print(e)
max 函数求最大值,min 求最小值
其他函数操作
# 列表长度
list = [1, 2, 3]
print(len(list)) # 3
# 列表复制
print(list * 3) # [1, 2, 3, 1, 2, 3, 1, 2, 3]
# 列表合并
list1 = [4, 5]
list2 = list1 + list
print(list2) # [4, 5, 1, 2, 3]
# 判断元素是否存在
if 11 in list2: # not in 是判断不在
print('存在')
else:
print('不存在') # 不存在
切片
list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
# 普通切片
print(list1[0:2]) # [a,b],参数3省略了,默认 1
# 3个参数
print(list1[1:3:1]) # [b,c]
# 步长不为 1
print(list1[0:6:2]) # [a,c,e]
# 没有开始和结束
print(list1[:]) # ['a', 'b', 'c', 'd', 'e', 'f', 'g']
# 有开始,没结尾,一直获取到结尾
print(list1[1:]) # ['b', 'c', 'd', 'e', 'f', 'g']
# 有结尾没开头,从头开始截取
print(list1[:5]) # ['a', 'b', 'c', 'd', 'e']
# 步长为负,最后一个为 -1,倒数第二个为 -2,以此类推
print(list1[-2:-5:-1]) # ['f', 'e', 'd']
元组
不支持修改和删除,会报错。
tup = (1)
print(type(tup)) # <class 'int'>
tup1 = (1,)
print(type(tup1)) # <class 'tuple'>
词典
dict = {'name': 'jack'}
# 修改
dict['age'] = '18'
# 删除
del dict['age']
del dict
# 清空
dict.clear()
# 根据 key 获取
print(dict.get('name'))
print(dict)
# 获取所有键
print(dict.keys())
# 获取所有值
print(dict.values())
# 获取键值对
print(dict.items())
拆包理念
key, value = ('age', 25)
print(key)
print(value)
集合
# 集合,自动去重,无序
set = {10, 20, 20, 30, 40}
print(set)
字符串相关函数
查找
从头开始查找
str = 'hello,python'
# 从头开始查询,查询到结尾
print(str.find('python')) # 6
# 从第 2 位开始查询
print(str.find('python', 2)) # 6
# 从第2位到第5位区间查询
print(str.find('python', 2, 5)) # -1
从结尾开始查找
str = 'hello,python,hi python!!!'
print(str.rfind('python', 2, 24)) # 16
拼接
list1 = ['aa', 'bb', 'cc']
str = '#'.join(list1) # 使用 # 号链接
print(str) # aa#bb#cc
判断是不是数字
print('33'.isdigit()) # True
print('33.2'.isdigit()) # False
删除前后空格
str = ' 222 '
print(str.strip())
函数
函数的定义
def greent():
print('green')
返回值
def greent():
return 1,2,3,4
k = greent()
print(k) # (1, 2, 3, 4),返回的是元组
关键值传参 + 默认参数
带默认值的参数需要在最后面
def greent(age, name, phone=10086):
print(age, phone, name)
greent(age=22, name='zhangsan')
不定长传参
元组,也叫不定长位置参数
def greent(*keys):
print(keys) # 这是一个元组
greent('zhangsan', 22)
字典,不定长关键词参数
def greent(**keys):
print(keys) # 这是一个字典,{'name': 'zhangsan', 'age': 22}
greent(name='zhangsan', age=22)
混合使用
需要按照顺序
def greent(*args, **keys):
print(keys) # {'name': 'zhangsan', 'age': 22}
print(args) # (10, 29)
greent(10, 29, name='zhangsan', age=22)
解包
注意解包的时候,位置参数必须靠前,字典参数必须靠后,否则会报错
那这里的,* 是啥意思呢,其实一个 * 是解包为单个元素,两个 * 为解包为键值对。
def greent(*args, **keys):
print(keys) # {'name': 'zhangsan', 'age': 22}
print(args) # (10, 29)
# 传递列表和字典
list = [1, 2, 3, 4, 5]
dict = {'name': 'zhangsan', 'age': 22}
# 注意解包的时候,位置参数必须靠前,字典参数必须靠后,否则会报错
greent(*list, **dict)
推导式写法
列表推导式
list2 = [i for i in range(10)]
print(list2) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list3 = [i for i in range(1, 10, 2)]
print(list3)
list4 = [i for i in range(10) if i % 2 != 0]
print(list4)
集合推导式
set1 = {i for i in range(1, 10, 2)}
print(set1)
词典推导式
list1 = ['name', 'age', 'gender']
list2 = ['zhangsan', 22, '男']
dict = {list1[i]: list2[i] for i in range(len(list1))}
print(dict) # {'name': 'zhangsan', 'age': 22, 'gender': '男'}
变量
- 全局变量,定义在函数外面。
- 局部变量,定义在代码块中,比如函数。
num = 10
def greent():
# 这里的num是定义了一个新的变量
num = 20
greent()
print(num) # 10
函数内引用全局变量
num = 10
def greent():
# 声明引用全局变量
global num
# 此时是重新赋值给全局变量的num
num = 20
greent()
print(num) # 20
三目表达式
a, b = 10, 20
max = a if a > b else b
print(max)
lambda 表达式
这就是定义了一个匿名函数,也就是lambda表达式,冒号左侧是参数,右侧是返回值。
f = lambda: 100
print(f()) # 100
面向对象
类的定义
class Student(object):
# 魔术方法,用于初始化成员属性,在类实例化的时候会触发
def __init__(self, name, age):
# 初始化两个成员属性
self.name = name
self.age = age
# self 就等于this,表示这个对象,指向自己
def eat(self):
print('eat')
# 实例化一个类
stu = Student('zhangsan', 16)
print(stu.name)
print(stu.age)
打印类的时候,是调用了类的 str函数。我们可以重写它
class Student(object):
# 魔术方法,用于初始化成员属性,在类实例化的时候会触发
def __init__(self, name, age):
# 初始化两个成员属性
self.name = name
self.age = age
def __str__(self):
return '这是str函数'
# 实例化一个类
stu = Student('zhangsan', 16)
print(stu)
析构函数
class Student(object):
def __init__(self, name):
self.name = name
# 析构函数
def __del__(self):
print('Student 被销毁了') # 删除对象时被触发了
# 实例化一个类
stu = Student('zhangsan')
# 删除对象
del stu
call 函数,对象被调用时触发
怎么调用
class Student(object):
def __init__(self):
pass
# 调用函数
def __call__(self, value = 0):
print(22, value)
# 实例化一个类
stu = Student()
# 这里就是调用
stu()
私有属性,公有属性
私有的是不能被继承的,公有的才可以被继承
- 带双下划线的变量是私有属性,不能在外面直接访问
- 不带双下划线的是共有属性,可以在任何地方访问
class Student(object):
def __init__(self, name, age):
self.name = name
self.__age = age
# 调用函数
def __call__(self, value = 0):
print(22, value)
# 规范函数名称小写
def get_age(self):
return self.__age
# 实例化一个类
stu = Student('zhangsadn', 22)
# 访问共有属性
print(stu.name)
# 访问私有属性
print(stu.get_age())
公共方法和私有方法
class Student(object):
def __init__(self, name, age):
self.name = name
self.__age = age
# 规范函数名称小写
def get_age(self):
return self.__age
def __func(self):
return '这是私有方法'
# 实例化一个类
stu = Student('zhangsadn', 22)
stu.get_age()
stu.__func() # 不能调用,会报错
继承
class Student(object):
def __init__(self, name, age):
self.name = name
self.__age = age
def eat(self):
print('吃东西')
def __set_name(self, name):
self.name = name
class Student2(Student):
pass
# 会继承 init 魔术方法
stu = Student2('name2', 22)
stu.eat()
# stu.__set_name('2') # 私有方法不继承
Super 方法
使用 super() 调用父类的方法和属性
class Student(object):
def __init__(self, name, age):
self.name = name
self.__age = age
class Student2(Student):
# 扩展,加一个学号
def __init__(self, name, age, number):
super().__init__(name, age)
self.number = number
# 会继承 init 魔术方法
stu = Student2('name2', 22, 11011)
print(stu.number) # 11011
print(stu.name) # name2
精准访问
这里调用父类的函数,构造函数等,如果想访问属性,需要通过self。
class master(Student, Teacher):
def show(self):
print(Teacher.show2(self))
多继承
python 是支持多继承的
class Student(object):
pass
class Student2(object):
pass
# 同时继承两个类
class Student3(Student, Student2):
pass
如果继承的两个类具有共同的属性和方法,那优先用谁的呢。
python 会优先选择距离自己更近的,比如上面的案例中,Student 更近,如果找到了方法,就不会再去 Student2中找。
查看类的继承关系
class Student(object):
def eat(self):
print('Student')
class Student2(object):
def eat(self):
print('Student2')
# 同时继承两个类
class Student3(Student, Student2):
pass
# 查看类的继承关系
#[<class '__main__.Student3'>, <class '__main__.Student'>, <class '__main__.Student2'>, <class 'object'>]
print(Student3.mro())
多态
同一个事物,在不同的环境,表现出的形态不同。 前提条件
- 要有继承关系
- 要有方法重写
- 要有父类引用指向子类对象
class Animal:
def speak(self):
print('不会说话')
class Dog(Animal):
def speak(self):
print('狗:汪汪汪')
class Cat(Animal):
def speak(self):
print('猫:喵喵喵')
def test(an: Animal): # 限制只能是父类传递
an.speak()
if __name__ == '__main__':
cat = Cat()
dog = Dog()
test(cat) # 猫:喵喵喵
test(dog) # 狗:汪汪汪
抽象类
class Animal:
def speak(self):
pass
包
首先每个文件,都有一个 __name__,打印显示 __main__。用于测试模块
创建包
会自己带一个 __init__.py 文件。
init 文件
在 init 文件中导入所需要的常用包
__all__ = ['a01', 'a33']
作用是,在以后开发的独立文件中,快速导入一些包。
from my_package import *
当运行完上面的代码后,会将init文件中all对应的数组包一一执行。这个时候该文件,可以直接引用,不需要导入了。
文件操作
基础
- AscII:其他都是占用一个字节
- GBK:一个中文两个字节
- utf-8:一个中文占三个字节
s1 = '你好'
print(s1.encode('utf-8'))
print(s1.encode('gbk'))
解码
s = b'\xe6\xb5\xb7\xe6\xb5\xb7cdode2'
print(s.decode('utf-8'))
打开文件
- name:文件路径
- mode:设置打开文件模式,只读,只写,追加
- encoding:编码格式,推荐utf-8
# open(name, mode, encoding)
scor = open('E:\\pycharm_work\\my_package\\a01.py', 'r', encoding='utf-8')
print(scor)
scor.close()
相关 API
# 读取所有数据
data = scor.read()
print(data)
# 读取指定字节数据
data = scor.read(10)
print(data)
# 读取一行
data = scor.readline()
print(data)
自动释放资源,with open
with open('E:\\pycharm_work\\my_package\\11.txt', 'r', encoding='utf-8') as scor:
# 读取一行
data = scor.readline()
print(data)
读取所有行
with open('E:\\pycharm_work\\my_package\\11.txt', 'r', encoding='utf-8') as scor:
# 读取所有行
data = scor.readlines()
print(data)
os 操作文件
import os
import shutil
# 改名
os.rename('源文件路径', '新文件路径')
# 删除文件
os.remove('文件路径')
# 获取当前工作路径
print(os.getcwd())
# 修改工作路径
os.chdir('d:/')
# 创建文件夹
os.mkdir('路径文件名')
# 删除文件夹,必须是空的文件夹
os.rmdir('路径文件名')
# 删除文件夹和文件
shutil.rmtree('文件夹')
# 查看文件夹下的目录和文件
os.listdir('d:/')
异常处理
try:
print('执行了')
except Exception as e:
print(f'程序出错了,{e}')
finally:
print('无论如何都执行')
Object 类
将对象转成字典
stu = Student('zhangsan', 'nan', 18, 136, '描述')
d = stu.__dict__ # 转成字典
print(d)
拷贝
浅拷贝
import copy
stu = Student('zhangsan', 'nan', 18, 136, '描述')
a = copy.copy(stu)
深拷贝
import copy
s1 = Student("张三", [90, 95])
s3 = copy.deepcopy(s1)
闭包
装饰器的底层就是闭包。
条件:嵌套、引用、返回。
- 概述:当外部函数执行结束后,其内部函数依旧可以访问外部函数的变量 -> 闭包
- 作用:
-
- 延长外部函数,局部变量的生命周期
-
- 闭包是装饰器的前提
-
存活周期:当方法返回的函数,被接收后,存活时间延长,直到变量释放才会销毁。
语法
def outer(a):
# 内部函数
def fun(b):
sum = a + b
print(sum)
return fun
# 调用外部函数
my_fun = outer(10)
# 调用内部函数,此时外部函数传递的10,没有被销毁。
my_fun(20)
my_fun(30)
内部函数如何修改外部函数的变量
直接修改会报错
def outer(a):
def fun(b):
# 提升 a 的权限
nonlocal a
a = a + b
print(a)
return fun
my_fun = outer(10)
my_fun(20)
my_fun(30)
装饰器
概念:闭包的三大概念 + 有额外功能
作用:不修改原函数基础上,增强该函数。
定义装饰器和使用
# 需要增强的函数
def comment():
print('发表评论')
# 装饰器
def check_login(func):
def fun_inner():
print('登录中...') # 额外增强
func()
return fun_inner
传统写法
fn = check_login(comment)
fn()
装饰器写法
# 需要增强的函数
@check_login
def comment():
print('发表评论')
comment()
多个装饰器装饰
实际使用时,我们看到的是从外向内的,实际上实现是从内向外的。
装饰器写法
# 装饰器
def check_login(func):
def fun_inner():
print('登录中...') # 额外增强
func()
return fun_inner
def check_code(func):
def fun_inner():
print('验证码验证中...') # 额外增强
func()
return fun_inner
# 需要增强的函数
@check_login
@check_code
def comment():
print('发表评论')
comment()
拆解写法
# 装饰器
def check_login(func):
def fun_inner():
print('登录中...') # 额外增强
func()
return fun_inner
def check_code(func):
def fun_inner():
print('验证码验证中...') # 额外增强
func()
return fun_inner
# 需要增强的函数
def comment():
print('发表评论')
code = check_code(comment)
login = check_login(code)
login()
类方法装饰器
如果在函数内部,需要用到类的引用,需要定义成类方法,否则就是静态的。
class Animal:
teacher = '22222'
# 用类方法,修饰
@classmethod
def method(cls):
print('我是类方法')
print(f'访问类属性,{cls.teacher}')
# 调用
Animal.method()
animal = Animal()
animal.method() # 可以,但是不被推荐
带有参数的装饰器
希望实现,传递+打印加法,传递 - 打印减法
- 实现思路:多层嵌套
def loadding(flag):
def decorator(func):
def inner(a, b):
if flag == '+':
print('努力计算加法中')
elif flag == '-':
print('努力计算减法中')
return func(a, b)
return inner
return decorator
@loadding('+')
def add(a, b):
return a + b
@loadding('-')
def sub(a, b):
return a - b
print(add(1, 2))
print(sub(1, 2))
- 通过 __name__ 实现,拿到的是函数名
def decorator(func):
def inner(a, b):
if func.__name__ == '+':
print('努力计算加法中')
elif func.__name__ == '-':
print('努力计算减法中')
return func(a, b)
return inner
@decorator
def add(a, b):
return a + b
@decorator
def sub(a, b):
return a - b
print(add(1, 2))
print(sub(1, 2))
静态方法
class Animal:
teacher = '22222'
# 用类方法,修饰
@staticmethod
def method():
print('我是静态方法')
print(f'访问类属性,{Animal.teacher}')
# 调用
Animal.method()
animal = Animal()
animal.method() # 可以,但是不被推荐
简化编程
可以简化函数,让函数当变量使用。
入门版本
class Student:
def __init__(self, name):
self.__name = name
# 使用 property 可以让,函数当变量使用
@property
def get_name(self):
return self.__name
# 使用 property 可以让,函数当变量使用
@get_name.setter
def set_name(self, name):
self.__name = name
student = Student('张三')
print(student.get_name)
student.set_name = '王五'
print(student.get_name)
最终版本
class Student:
def __init__(self, name):
self.__name = name
# 使用 property 可以让,函数当变量使用
@property
def name(self):
return self.__name
# 使用 property 可以让,函数当变量使用
@name.setter
def name(self, name):
self.__name = name
student = Student('张三')
print(student.name)
student.name = '王五'
print(student.name)
类变量写法
class Student:
def __init__(self, name):
self.__name = name
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
name = property(get_name, set_name)
if __name__ == '__main__':
s = Student('张三')
print(s.name)
s.name = '李四'
print(s.name)
网络编程
概述
分类
- IPv4:由4个字节组成,十进制表示IP,例如:
192.168.33.150 - IPv6:由8个字节组成,十六进制表示IP
| 类型 | 格式 | 说明 | 示例 |
|---|---|---|---|
| A类 / 城域网 | 1 + 3 | 第1段作为网络地址(网段地址),后三段作为主机地址 | 10.0.0.0 ~ 10.255.255.255 |
| B类 / 广域网 | 2 + 2 | 前两段是网络号 | 10.10.0.0 ~ 10.10.255.255 |
| C类 / 局域网 | 3 + 1 | 前3段是网络号,最后1段是主机地址 | 192.168.33.0 ~ 192.168.33.255 |
开发流程
创建服务端
import socket
# 参数一是表示 IPV4,参数二是表示 TCP
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定端口和ip,传递元组
server_socket.bind(('127.0.0.1', 10086))
# 设置最大连接数
server_socket.listen(5)
# 开启监听,等待客户端建立连接,返回元祖,一个是客户端交互的socket对象,一个是客户端的ip信息,会阻塞
client_socket, client_address = server_socket.accept()
# 给客户端发一句话
client_socket.send(b'hello, i am server')
# 接收客户端发来的数据,一次性接收1024字节
data = client_socket.recv(1024)
# 解码
print(data.decode())
# 释放资源
client_socket.close()
创建客户端
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 链接
client_socket.connect(('127.0.0.1', 10086))
recv = client_socket.recv(1024)
print(recv.decode())
client_socket.send(b'hello, i am client')
client_socket.close()
TCP 注意事项
-
TCP服务端程序必须绑定端口号,否则客户端找不到这个TCP服务端程序,为了更稳定,建议把IP也绑定
-
accept()前的套接字是被动套接字,只负责接收新的客户端的连接请求,不能收发消息
-
当 TCP客户端程序和 TCP服务端程序连接成功后, TCP服务器端程序会产生一个新的套接字,用于收发客户端消息
-
若关闭 accept()返回的被动连接套接字,则表示和这个客户端已经通信完毕
-
对于服务器端socket,关闭时需慎重
-
当客户端的套接字调用 close 后,服务器端的 recv 会解阻塞,返回的数据长度为0,用于判断客户端是否已经下线
服务端设置端口号释放(个别个机器需要设置)
因为主板和CPU的不同
要写在关闭之后。
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
...
# SOL_SOCKET 代表当前的Socket 对象,SO_REUSEADDR 代表重用,值为True
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
长连接,聊天
服务端
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('127.0.0.1', 10086))
server_socket.listen(5)
client_socket, client_address = server_socket.accept()
while True:
# 接受并且解码
data = client_socket.recv(1024).decode()
if data == '886':
break
print(data)
client_socket.close()
客户端
import socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('127.0.0.1', 10086))
while True:
data = input('请输入要发送的信息?')
client_socket.send(data.encode())
if data == '886':
break
client_socket.close()
多进程
创建多进程去执行任务
import multiprocessing
def func():
print('hello world')
if __name__ == '__main__':
# 创建进程,去执行任务
p = multiprocessing.Process(target=func)
p.start()
传参
args 传参
import multiprocessing
def func(name, age):
print(f'我叫{name},今年{age}岁')
if __name__ == '__main__':
p = multiprocessing.Process(target=func, args=('zhangsan', 22))
p.start()
keywords 传参
import multiprocessing
def func(name, age):
print(f'我叫{name},今年{age}岁')
if __name__ == '__main__':
p = multiprocessing.Process(target=func, kwargs={'name': '张三', 'age': 18})
p.start()
进程参数
import multiprocessing
# name 设置进程名称
p = multiprocessing.Process(target=func, name='天龙八部')
获取进程 ID
获取当前进程
import multiprocessing
import os
def func():
print(os.getpid()) # 6504
# print(multiprocessing.current_process().pid) # 6504
if __name__ == '__main__':
p = multiprocessing.Process(target=func)
p.start()
获取父进程
import multiprocessing
import os
def func():
print(os.getppid()) # 获取父进程:14260
if __name__ == '__main__':
p = multiprocessing.Process(target=func)
p.start()
进程特点
数据隔离
进程之间数据是隔离的,在main函数外面的数据,每个进程都会拷贝一份。main函数里面的是不会的。
生命周期
默认情况下主进程会等待,子进程结束再结束。
守护线程
随着主进程结束而结束
设置方式
if __name__ == '__main__':
p = multiprocessing.Process(target=func)
# 设置进程为守护进程
p.daemon = True
p.start()
子进程自己控制结束,可能会残留资源,变成僵尸线程。不推荐。
交给系统管理器进行管理。
if __name__ == '__main__':
p = multiprocessing.Process(target=func)
p.start()
p.terminate() # 强制结束子进程
线程
创建线程
- 线程的数据是共享的
- 线程是无序的,CPU调度那个线程,那个线程就执行
import threading
def func():
print('hello python')
if __name__ == '__main__':
# 创建线程,也具备name,args,kwargs参数
thread = threading.Thread(target=func)
thread.start()
基本信息
# 打印线程名字
print(threading.current_thread().name)
守护线程
默认主线程会等待子线程执行结束再结束,如果想实现主线程结束,子线程直接销毁,需要设置守护线程。
if __name__ == '__main__':
thread = threading.Thread(target=func)
# 设置线程为守护线程
thread.daemon = True
thread.start()
加锁
# 创建锁
lock = threading.Lock()
def func():
# 上锁
lock.acquire()
...
# 释放锁
lock.release()
进程和线程的区别
进程:
- 优点:可以用多核
- 缺点:资源开销大
线程:
- 优点:资源开销小
- 缺点:不能使用多核
上下文管理器
任何一个类,实现了 enter 和 exit 魔法方法,该类就是上下文管理器类。
上下文管理器对象需要结合 with 语句一起使用,格式如下
with 上下文管理器对象 as 别名:
代码块
特点:
- with执行前,自动调用 enter,一般用于初始化
- with执行后,调用exit方法,用于释放资源。
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
# 进入上下文时自动打开文件
def __enter__(self):
print("正在打开文件...")
self.file = open(self.filename, self.mode, encoding='utf-8')
return self.file # 这里返回的对象会赋值给 as 后的变量
# 退出上下文时自动关闭文件
def __exit__(self, exc_type, exc_value, traceback):
print("正在关闭文件...")
if self.file:
self.file.close()
# 如果返回 True,则异常不会继续抛出
return False
# 使用 with 自动调用 __enter__ 和 __exit__
with FileManager("test.txt", "w") as f:
f.write("Hello, Python 上下文管理器!")
print("文件写入完成。")
print("with 语句块已结束。")
推导式写法
元组内部是一个生成器,它有自己的计算规则
下面的写法 1
gen = (i for i in range(5))
print(next(gen)) # 内部有一个指针,每次调用next()函数,指针就会移动一位
查看内存
import sys
gen = (i for i in range(10))
# 获取占用空间大小
print(sys.getsizeof(gen))
写法2
def mya33():
for i in range(1, 10):
yield i
mya_ = mya33()
print(next(mya_)) # 1
正则表达式
match 全量匹配
import re
match = re.match('.it', 'ait') # 匹配成功,.是任意匹配一个
match = re.match('[ackil]it', '.it') # 失败,因为 .没有在中括号中
match = re.match('[a-e]it', 'cit') # 成功,因为 c 在中括号里,a-e,区间取
# 匹配开头数字,后面任意,不要换行符
# match = re.match('[0123456789].*', '2dadadkak') # 第一个必须是数字,后面除了换行符匹配任意,太繁琐
# match = re.match('[0-9].*', '2dadadkak') # 还是复杂
match = re.match('\\d.*', '2dadadkak')
match = re.match('.', '你好') # 成功 . 表示任意匹配一个,其他的会抛弃,.不包含\n
match = re.match('\\.', '.aq') # 成功 # 匹配 .
print(match) # 打印 None 表示匹配失败
search 查找
只要有一个区间,全额匹配就成立
import re
match = re.search('\\d.*', 'p2dadadkak') # 也成立,首字母不匹配,跳过后,继续检验,返回后面匹配
替换
方式一
import re
# 要替换的字符串
string = "哈|嘿|呵|嘻"
# 编译成正则表达式对象
pattern = re.compile(string)
# 执行替换
res = pattern.sub('♥️', '请把哈嘿呵嘻替换为红桃')
print(res)
方式二
import re
# 执行替换
res = re.sub("[哈嘿呵嘻]", '♥️', '请把哈嘿呵嘻替换为红桃')
print(res)
单量
| 代码 | 功能 |
|---|---|
| . | 匹配任意1个字符(除了\n) |
| [ ] | 匹配[ ]中列举的字符 |
| [^指定字符] | 匹配除了指定字符以外的所有字符 |
| \d | 匹配数字,即0-9 |
| \D | 匹配非数字,即不是数字 |
| \s | 匹配空白,即 空格,tab键 |
| \S | 匹配非空白 |
| \w | 匹配非特殊字符,即a-z、A-Z、0-9、_、汉字 |
| \W | 匹配特殊字符,即非字母、非数字、非汉字 |
数量
| 代码 | 功能 |
|---|---|
| * | 匹配前一个字符出现0次或者无限次,即可有可无 |
| + | 匹配前一个字符出现1次或者无限次,即至少有1次 |
| ? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
| {m} | 匹配前一个字符出现m次 |
| {m,n} | 匹配前一个字符出现从m到n次 |
限定开头和结尾
| 代码 | 功能 |
|---|---|
| ^ | 匹配字符串开头 |
| $ | 匹配字符串结尾 |
分组
| 代码 | 功能 |
|---|---|
| | | 匹配左右任意一个表达式 |
| (ab) | 将括号中字符作为一个分组 |
| \num | 引用分组num匹配到的字符串 |
import re
s = "3109405015@qq.com"
match = re.match('^[a-zA-Z_0-9]{4,20}@(163|126|qq)\\.com', s)
# 获取 0 组数据,默认也是0,表示所有数据,写1,表示找到第一组,也就是小括号
group = match.group(0)
print(group)
提取分组
import re
s = "qq:3109405015"
match = re.match('^(qq):(\\d{5,20})$', s)
# 获取 0 组数据,默认也是0,表示所有数据,写1,表示找到第一组,也就是小括号
group = match.group(0)
group1 = match.group(1)
group2 = match.group(2)
print(group)
print(group1)
print(group2)
迭代器
将容器转换为迭代器
if __name__ == '__main__':
# 列表
list = [i for i in range(1, 100000)]
# 获取列表所占内存大小
print(sys.getsizeof(list))
#将列表转换为迭代器
iterator = iter(list) # 800984
print(sys.getsizeof(iterator)) # 48
自定义迭代器
class MyIterator:
def __init__(self, value):
self.max_value = value
self.current_value = 0
# 迭代器方法
def __iter__(self):
return self
def __next__(self):
if self.current_value >= self.max_value:
raise StopIteration
self.current_value += 1
return self.current_value
if __name__ == '__main__':
iterator = MyIterator(200000)
for e in iterator:
print(e)
print(sys.getsizeof(iterator)) # 48
扩展题
需求:有一天你穿越到了古代,碰巧遇到古代的皇帝在杀人,有两个消息,1 个好消息,1 个坏消息。坏消息:你也在此列。好消息:你可以选择自己占的位置。规则:让所有的人从 1 开始不断数数,每次累加 1,只要数到 3 的倍数,就干掉这个人,直至剩下最后 1 个人,他站的位置就是幸运数字。
参考答案:10 个人玩游戏 → 幸运数字:4
常规思路
# 10 个人玩游戏
n = 10
list = [i for i in range(1, n + 1)]
i = 0
# 当前数到的数字
current_num = 0
while len(list) != 1:
current_num += 1
# 如果当前数是 3 的倍数,则删除该数字
if current_num % 3 == 0:
list.pop(i)
# 细节:删除数字后,索引 i 需要减 1
i -= 1
i += 1
# 细节二:索引 i 超过数组长度时,需要归零,说明开启了第二轮
if i == len(list):
i = 0
print(list[0])
数学思维
# 10 个人玩游戏
n = 10
list = [i for i in range(1, n + 1)]
i = 0
while len(list) != 1:
i = (i + 2) % len(list)
list.pop(i)
print(list[0])
完结撒花!!