Python 学习记录

106 阅读23分钟

Python 环境

anaconda 安装

是一个python解析器、大部分扩展包和依赖项,支持所有操作系统。

一直下一步,看到这里,不创建快捷方式,不加入环境,安装python3.13

image.png

将安装目录,加入到环境变量。就可以使用 python了。

还有以下目录,也要加入环境变量

image.png

虚拟环境

有时候版本之间会有冲突,可以使用虚拟环境安装不同版本的Python。

入门

基本

查看关键词

help('keywords')

image.png

数据类型

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)  # 输出 04

for i in range(1, 6):
    print(i) # 输出 14

for i in range(1, 10, 2): # 随机9个,从1开始,步长 2
    print(i) # 13579

循环带 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())

多态

同一个事物,在不同的环境,表现出的形态不同。 前提条件

  1. 要有继承关系
  2. 要有方法重写
  3. 要有父类引用指向子类对象
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 文件。 image.png

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()

image.png

相关 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)

闭包

装饰器的底层就是闭包。

条件:嵌套、引用、返回。

  • 概述:当外部函数执行结束后,其内部函数依旧可以访问外部函数的变量 -> 闭包
  • 作用:
      1. 延长外部函数,局部变量的生命周期
      1. 闭包是装饰器的前提

存活周期:当方法返回的函数,被接收后,存活时间延长,直到变量释放才会销毁。

语法

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() # 可以,但是不被推荐

带有参数的装饰器

希望实现,传递+打印加法,传递 - 打印减法

  1. 实现思路:多层嵌套
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))
  1. 通过 __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
开发流程

image.png

创建服务端

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 注意事项

  1. TCP服务端程序必须绑定端口号,否则客户端找不到这个TCP服务端程序,为了更稳定,建议把IP也绑定

  2. accept()前的套接字是被动套接字,只负责接收新的客户端的连接请求,不能收发消息

  3. 当 TCP客户端程序和 TCP服务端程序连接成功后, TCP服务器端程序会产生一个新的套接字,用于收发客户端消息

  4. 若关闭 accept()返回的被动连接套接字,则表示和这个客户端已经通信完毕

  5. 对于服务器端socket,关闭时需慎重

  6. 当客户端的套接字调用 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 别名:
    代码块

特点:

  1. with执行前,自动调用 enter,一般用于初始化
  2. 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])

完结撒花!!