Skip to content

python 基础语法笔记


数据类型

类型名关键字说明例子type() 返回值
整数int没有小数的数值42, -3<class 'int'>
浮点数float有小数的数值3.14, -0.5<class 'float'>
字符串str文本数据'hello'<class 'str'>
布尔值bool逻辑值 TrueFalseTrue, False<class 'bool'>
列表list有序的元素集合[1, 'a', 3.14]<class 'list'>
元组tuple有序的不可变元素集合(1, 'a', 3.14)<class 'tuple'>
字典dict键值对集合{'name': 'Alice', 'age': 25}<class 'dict'>
集合set无序的唯一元素集合{1, 2, 3}<class 'set'>
复数complex包含实部和虚部的数3 + 4j<class 'complex'>
二进制bytes二进制数据b'\x00\x01'<class 'bytes'>
内存视图memoryview字节对象的内存视图memoryview(b'ABC')<class 'memoryview'>
自定义对象使用类定义的对象实例MyClass()<class '__main__.MyClass'>

算术运算符

运算符说明例子结果
+加法5 + 38
-减法5 - 32
*乘法5 * 315
/除法5 / 31.6666666666666667
//整除(向下取整)5 // 31
%取余(模运算)5 % 32
**幂运算5 ** 3125
+x正号(不变)+55
-x负号(取反)-(-3)3

定义字符串的方式

定义方式例子特点
单引号'hello'适用于单行字符串。如果字符串中不包含单引号,可以使用单引号定义。
双引号"hello"适用于单行字符串。如果字符串中不包含双引号,可以使用双引号定义。
三引号'''hello'''"""hello"""适用于多行字符串。可以包含换行符、制表符等。常用于文档字符串、多行注释或诗歌等。三引号可以是三个单引号或三个双引号,也可以混合使用。
mermaid
graph TD 
A-->D

if while for range

类型关键字说明语法示例代码输出
判断语句if条件为真时执行的代码块if 条件表达式:
代码块
if x > 5:
print("x is greater than 5")
"x is greater than 5" (如果 x > 5)
elif如果之前的条件为假,当前条件为真时执行的代码块elif 条件表达式:
代码块
elif x == 5:
print("x is equal to 5")
"x is equal to 5" (如果 x == 5)
else所有条件为假时执行的代码块else:
代码块
else:
print("x is less than 5")
"x is less than 5" (如果 x < 5)
循环语句for遍历序列的元素for 变量 in 序列:
代码块
for fruit in ["apple", "banana", "cherry"]:
print(fruit)
apple
banana
cherry
while只要条件为真,就不断循环执行代码块while 条件表达式:
代码块
i = 1
while i <= 5:
print(i)
i += 1
1
2
3
4
5
range()生成一个整数序列,通常用于 for 循环range([start,] stop[, step])for i in range(1, 6):
print(i)
1
2
3
4
5

函数

特性说明语法/示例
定义创建一个新函数def function_name(parameters):
代码块
调用使用函数名和参数列表来执行函数function_name(arguments)
参数函数定义中用于接收输入值的变量def add(a, b):
return a + b
返回值函数执行完毕后返回的值def add(a, b):
return a + b
默认参数给参数指定默认值,调用时可以不传递该参数def greet(name="World"):
print("Hello, " + name)
可变参数允许函数接受任意数量的参数def add(*args):
return sum(args)
关键字参数允许函数接受任意数量的关键字参数def create_dict(**kwargs):
return kwargs
命名关键字参数允许函数接受一定数量的命名关键字参数def create_dict(name, age, **kwargs):
return {"name": name, "age": age, **kwargs}
函数说明函数的第一行文本,用来解释函数的作用def add(a, b):
"""Return the sum of a and b."""
return a + b
嵌套函数在一个函数内部定义另一个函数def outer():
def inner():
print("Inside inner function")
inner()
函数属性函数也是一个对象,可以有属性和方法def my_function():
pass
my_function.__name__
作用域变量在函数内定义则为局部变量,定义在函数外则为全局变量x = 5
def my_function():
y = 10
print(x) inside function prints 5, y inside function is local
global在函数内部声明变量为全局变量,允许修改全局变量的值x = 5
def my_function():
global x
x = 10
my_function()
print(x) prints 10
多返回值函数可以返回多个值def get_user_info():
return name, age, email
多传参方式函数支持多种参数传递方式def my_func(a, b, *args, **kwargs):
pass
函数作为参数函数本身可以作为另一个函数的参数def apply_func(func, a, b):
return func(a, b)
位置参数调用函数时根据函数定义的参数位置来传递参数def func(a, b):
print(a, b)
func(1, 2)
关键字参数函数调用时通过“键=值”形式传递参数def func(**args):
print(args)
func(b=2, a=1)
缺省参数
默认参数
定义函数时为参数提供默认值,
注意:所有位置参数必须出现在默认参数前,包括函数定义和调用)
def func(a, b=10):
print(a, b)
不定长参数位置不定长传递以*号标记一个形式参数,以元组的形式接受参数,形式参数一般命名为args
关键字不定长传递以**号标记一个形式参数,以字典的形式接受参数,形式参数一般命名为kwargs
函数作为参数传递就像上述代码那样,不仅仅是相加,相见、相除、等任何逻辑都可以自行定义并作为函数传入def test_func(compute):
result compute(1,2)
print(result)
def compute(x, y):
return X
test_func(compute) #结果:2
匿名函数使用 lambda 关键词创建的简洁函数
注意:函数体只能写一行,无法写多行代码
add = lambda a, b: a + b
print(add(2, 3))
示例:
def test_func(compute) :
return = compute(1, 2)

test_func(lambda x , y : x + y) # 结果为3

容器

容器总结

名称关键字特点容器示例常见方法遍历方法切片切片示例遍历示例注意事项
列表list有序、可变、允许重复、可通过下标访问[1, 'a', 3.14]append(x): 添加元素到列表末尾
extend( iterable ): 扩展列表
pop([index]): 删除并返回指定位置的元素
remove(x): 移除列表中第一个匹配的元素
index(x): 返回元素第一次出现的索引
count(x): 返回元素在列表中出现的次数
sort(key=None, reverse=False): 对列表进行排序
reverse(): 反转列表
直接遍历支持my_list[1:3]for item in my_list: print(item)可以修改,可以包含不同类型的元素,支持嵌套
元组tuple有序、不可变、允许重复、可通过下标访问(1, 'a', 3.14)index(x): 返回元素第一次出现的索引
count(x): 返回元素在元组中出现的次数
直接遍历支持my_tuple[1:3]for item in my_tuple: print(item)创建后不可修改,可以包含不同类型的元素,支持嵌套
字符串str有序、不可变、元素唯一、可通过下标访问"hello"index(x): 返回子字符串第一次出现的索引
count(x): 返回子字符串在字符串中出现的次数
strip(chars=None): 移除字符串首尾的空格或指定字符
split(sep=None, maxsplit=-1): 按照指定分隔符分割字符串
replace(old, new, count=-1): 替换字符串中的子字符串
直接遍历支持"hello"[1:3]for char in "hello": print(char)不可变,元素为字符,支持下标索引
集合set无序、可变、元素唯一、不可通过下标访问{1, 2, 3}add(x): 向集合添加元素
remove(x): 移除集合中指定元素
discard(x): 移除集合中指定元素,如果不存在不报错
clear(): 清空集合
union(*iterables): 返回两个集合的并集
intersection(*iterables): 返回两个集合的交集
difference(*iterables): 返回两个集合的差集
直接遍历不支持N/Afor item in my_set: print(item)元素唯一,无序,不可下标索引,可以进行集合运算
字典dict无序、可变、键值对、键唯一、不可通过下标访问{'name': 'Alice', 'age': 25}get(x, default=None): 通过键获取值,如果键不存在返回默认值
keys(): 获取字典中所有的键
values(): 获取字典中所有的值
items(): 获取字典中所有的键值对
update(*args, **kwargs): 更新字典
pop(k[, d]): 删除字典中指定键的键值对
clear(): 清空字典
遍历键或项不支持N/Afor key, value in my_dict.items(): print(key, value)键值对存储,键必须是不可变类型,如字符串或元组,不支持下标索引,但可以通过键访问

通用功能

功能描述
通用for循环遍历容器(字典是遍历key)
max容器内最大元素
min()容器内最小元素
len()容器元素个数
list()转换为列表
tuple()转换为元组
str()转换为字符串
set()转换为集合
sorted(序列, [reverse=True])排序,reverse=True表示降序 得到一个排好序的列表

切片

切片语法说明示例结果支持切片的容器
s[start:stop:step]从索引 start 开始到 stop 结束(不包括 stop),步长为 stepa = [0, 1, 2, 3, 4, 5]
a[1:5:2]
[1, 3]列表、元组、字符串
s[start:]从索引 start 到序列末尾a = [0, 1, 2, 3, 4, 5]
a[2:]
[2, 3, 4, 5]列表、元组、字符串
s[:stop]从序列开头到索引 stop(不包括 stopa = [0, 1, 2, 3, 4, 5]
a[:3]
[0, 1, 2]列表、元组、字符串
s[:]复制整个序列a = [0, 1, 2, 3, 4, 5]
a[:]
[0, 1, 2, 3, 4, 5]列表、元组、字符串
s[-step:]从序列末尾向前取 step 个元素a = [0, 1, 2, 3, 4, 5]
a[-3:]
[3, 4, 5]列表、元组、字符串
s[:-step]取序列开头到索引 -step(不包括 -step)的元素a = [0, 1, 2, 3, 4, 5]
a[:-3]
[0, 1]列表、元组、字符串
s[start:stop:-1]逆向切片,从索引 startstop 逆向取元素a = [0, 1, 2, 3, 4, 5]
a[3:0:-1]
[3, 2, 1]列表、元组、字符串
s[::-1]完全逆向切片,即反转序列a = [0, 1, 2, 3, 4, 5]
a[::-1]
[5, 4, 3, 2, 1, 0]列表、元组、字符串

列表推导式

[expression for item in iterable]

python
"""

"""
original_list = [1, 2, 3, 4, 5]
squared_list = [x**2 for x in original_list] # == [1, 4, 9, 16, 25]
"""
带条件的
"""
original_list = [1, 2, 3, 4, 5]
even_squared_list = [x**2 for x in original_list if x % 2 == 0] 
# even_squared_list == [4,16]

"""
嵌套使用for 
"""
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened_list = [num for row in matrix for num in row]

"""
带占位符`_`

dp == [
    [0, 0, 0, 0, 0],  # 第0行
    [0, 0, 0, 0, 0],  # 第1行
    [0, 0, 0, 0, 0],  # 第2行
    [0, 0, 0, 0, 0]   # 第3行
]

"""
a = [1, 2, 3]  # 假设a的长度为3
m = 4
dp = [[0] * (m + 1) for _ in range(len(a) + 1)]

文件处理

操作类型函数/方法描述模式示例代码
打开文件open()打开文件并返回文件对象可指定模式和编码f = open('example.txt', 'r', encoding='utf-8')
读取文件read(size)读取指定数量的字节读模式 'r'content = f.read(10)
readline()读取文件的一行读模式 'r'line = f.readline()
readlines()读取所有行,返回列表读模式 'r'lines = f.readlines()
写入文件write(string)将字符串写入文件写模式 'w'f.write('Hello, World!')
flush()强制将缓冲区内容写入文件写模式 'w'f.flush()
关闭文件close()关闭文件对象所有模式f.close()
自动管理with open() as f自动打开和关闭文件所有模式with open('example.txt', 'r') as f: content = f.read()
追加文件open('file', 'a')追加内容到文件末尾追加模式 'a'with open('example.txt', 'a') as f: f.write('New line')
文件编码encoding='utf-8'指定文件编码所有模式f = open('example.txt', 'r', encoding='utf-8')
错误处理try...except处理文件操作中的异常所有模式try: f = open('example.txt', 'r') except FileNotFoundError: print('File not found')

表格中的“模式”列指的是使用open()函数时可以指定的文件访问模式。每种模式适用于不同的文件操作场景。例如,如果你想要读取一个文件,应该使用读模式'r';如果你想要写入一个文件,并且如果文件已存在则覆盖它,应该使用写模式'w'。追加模式'a'允许你向现有文件的末尾添加内容,而不会删除现有内容。使用with open() as f语法可以确保文件在使用后正确关闭,即使在发生异常时也是如此。

异常

异常基础

  • 异常(Exception):程序错误时抛出的对象,通常是Exception类或其子类的实例。

异常处理结构

  • try:包含可能引发异常的代码。
  • except:捕获并处理特定类型的异常。
  • else:当try块没有引发异常时执行。
  • finally:无论是否发生异常,都会执行,常用于资源清理。

异常处理语法

python
try:
    # 可能引发异常的代码
except ExceptionType1:
    # 处理ExceptionType1异常
except (ExceptionType2, ExceptionType3) as e:
    # 同时处理ExceptionType2和ExceptionType3,e是异常实例
else:
    # 如果没有异常发生
finally:
    # 无论是否发生异常都会执行

自定义异常

  • 通过继承Exception类创建自己的异常类型。

抛出异常

  • 使用raise语句手动抛出异常。

异常链

  • 使用raise ... from ...保留原始异常的上下文。

内置异常

  • Python定义了许多内置异常,如ValueErrorTypeErrorIndexError等。

异常层次结构

  • BaseException:所有异常的基类。
  • Exception:大多数异常的基类。

异常传递关系

  • 异常可以被传递到调用栈的上一层,直到被捕获处理。
  • 未被捕获的异常会导致程序终止,并显示错误信息。

示例代码

python
try:
    result = 10 / 0
except ZeroDivisionError:
    print("不能除以零!")
except Exception as e:
    print(f"发生了一个错误:{e}")
else:
    print("一切正常!")
finally:
    print("这是 finally 块。")

模块 包

模块(Module)

定义与作用

  • 模块:Python 中的模块是一个包含 Python 代码的文件,通常以 .py 为文件扩展名。
  • 作用:模块提供了一种组织 Python 代码的方式,允许将相关的函数和类封装在一起,便于重用和维护。

导入模块

  • 导入整个模块

    python
    import math
    print(math.sqrt(16))
  • 从模块中导入特定内容

    python
    from math import sqrt
    print(sqrt(16))
  • 导入模块中的所有内容

    (不推荐):

    python
    from math import *
    print(sqrt(16))
  • 为模块或功能设置别名

    python
    import math as m
    print(m.sqrt(16))
    from math import sqrt as square_root
    print(square_root(16))

模块的导入机制

  • 当使用 import 语句时,Python 解释器会首先在 sys.path 列表中查找模块文件。
  • 如果模块不在 sys.path 中,会引发 ImportError

自定义模块

  • 创建:创建一个 .py 文件,文件名即为模块名。
  • 使用:在其他 Python 文件中通过 import 语句导入并使用。

__main__ 变量

  • 当一个模块被直接运行时,__name__ 变量被设置为 '__main__'
  • 这允许模块在被直接运行时执行一些代码,而在被导入时不执行这些代码。

包(Package)

什么是包

  • 定义:在 Python 中,包是组织模块的一种方式,它是一个包含一个 __init__.py 文件的目录,这个文件可以为空,也可以定义包的初始化代码。
  • 目的:包允许将相关的模块组织在一起,形成一个更大的结构,便于管理和重用。

包的结构

  • 目录:一个包通常是一个包含多个 .py 文件的目录,这些文件就是包内的模块。
  • __init__.py 文件:这个文件的存在使得 Python 将目录视为一个包。它可以包含 Python 代码,用于初始化包或者定义包级别的变量。

创建包

  • 步骤
    1. 创建一个目录。
    2. 在目录中创建一个 __init__.py 文件。
    3. 在目录中添加所需的 .py 文件作为模块。

包的导入

  • 导入包内的模块

    python
    import package_name.module_name
  • 从包中导入特定模块

    python
    from package_name import module_name
  • 从包中导入所有模块(使用 __all__ 控制):

    python
    from package_name import *

__init__.py 文件的作用

  • 初始化代码:可以包含包初始化时执行的代码。
  • 控制可导入内容:通过定义 __all__ 列表,控制使用 from package_name import * 时哪些模块或对象被导入。

__all__ 变量

  • 定义:在 __init__.py 文件中定义,用于指定当使用 from package_name import * 时应该导入哪些模块或对象。
  • 示例
    python
    __all__ = ['module1', 'module2']

包的安装和使用

  • 第三方包:非 Python 官方提供的包,通常通过 pip 命令安装。
  • 安装第三方包
    bash
    pip install package_name
  • 使用国内镜像加速
    bash
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple package_name

包的发布

  • 准备:确保包内有 setup.py 文件,这是一个用于安装、打包和分发包的脚本。
  • 发布:通过 python setup.py sdist upload 命令将包上传到 PyPI。

包的依赖管理

  • 虚拟环境:使用 venvconda 创建虚拟环境,以隔离不同项目的依赖。
  • 依赖文件:使用 requirements.txt 文件列出项目依赖,便于安装和管理。

包的命名空间

  • 作用:包可以有自己的命名空间,这允许在不同包中使用相同的模块名而不会发生冲突。

包的实际应用

  • 项目结构:在大型项目中,包可以用来组织不同的功能模块,如 Web 应用的路由、模型、视图等。
  • 重用:包可以被多个项目重用,提高了代码的可维护性和可重用性。

通过这些信息,你可以更好地理解 Python 包的概念、如何创建和使用它们,以及如何在项目中有效地组织代码。

可视化

折线图

  1. 概述
  • 目的:通过数据可视化案例,巩固 Python 基础语法,锻炼编程能力。
  • 工具:使用 pyecharts 模块进行数据可视化。
  1. 数据来源
  • 来源:数据来自百度疫情实时大数据报告和公开的全球各国 GDP 数据。
  1. 技术栈
  • Echarts:由百度开源的数据可视化工具,具有良好交互性和精巧图表设计。 画廊 https://gallery.pyecharts.org/#/README
  • Python:适合数据处理的编程语言。
  • pyecharts:Echarts 的 Python 版本,用于生成数据可视化图表。
  1. 安装 pyecharts
  • 命令:使用 pip install pyecharts 安装。
  1. JSON 数据格式
  • 定义:JSON 是一种轻量级的数据交换格式,本质上是一个特定格式的字符串。
  • 用途:作为不同编程语言间数据传递的通用格式。
  • 转换
    • Python 数据转 JSON:json.dumps(data)
    • JSON 转 Python 数据:json.loads(data)
  1. pyecharts 快速入门
  1. 折线图创建
  • 基础配置:使用 Line() 类创建折线图,并通过 .add_xaxis().add_yaxis() 方法添加数据。
  1. 折线图配置项
  • 初始化设置init_opts=opts.InitOpts(width="1600px", height="800px")
  • 添加 X 轴数据.add_xaxis(列表)
  • 添加 Y 轴数据.add_yaxis(series_name="图例名称", y_axis=[数据列表])
  • 点大小symbol_size=10
  • 标签设置label_opts=opts.LabelOpts(is_show=False)
  • 线条样式linestyle_opts=opts.LineStyleOpts(width=2)
  1. 全局配置选项
  • 图标题title_opts=opts.TitleOpts(title="标题", pos_left="center")
  • X 轴配置xaxis_opts=opts.AxisOpts(name="时间")
  • Y 轴配置yaxis_opts=opts.AxisOpts(name="累计确诊人数")
  • 图例配置legend_opts=opts.LegendOpts(pos_left='70%')
  1. 数据处理
  • 原始数据清洗:去除不符合 JSON 格式的字符,如 "jsonp_1629350871167_29498("");"
  • 数据转换:将清洗后的数据转换为 JSON 格式,然后提取所需数据。
函数/方法描述
json.dumps(data)将 Python 数据对象转换成 JSON 格式的字符串。
json.loads(data)将 JSON 格式的字符串解析成 Python 数据对象。
Line()pyecharts 模块中用于创建折线图的类。
.add_xaxis(列表)向折线图中添加 X 轴数据。
.add_yaxis(series_name, y_axis)向折线图中添加 Y 轴数据,series_name 设置图例名称,y_axis 输入 Y 轴数据。
set_global_opts()设置全局配置项,如图表标题、图例、X/Y 轴配置等。
init_opts=opts.InitOpts(width, height)设置图表初始化时的宽度和高度。
title_opts=opts.TitleOpts(title, pos_left)设置图表标题和标题位置。
xaxis_opts=opts.AxisOpts(name)配置 X 轴的名称。
yaxis_opts=opts.AxisOpts(name)配置 Y 轴的名称。
legend_opts=opts.LegendOpts(pos_left)设置图例的位置。
symbol_size设置折线图上数据点的大小。
label_opts=opts.LabelOpts(is_show)设置是否显示数据点的标签。
linestyle_opts=opts.LineStyleOpts(width)设置折线图线条的宽度和样式。
python
import json
from pyecharts.charts import Line
from pyecharts.options import TitleOpts, LabelOpts


f_us = open("./data_sources/美国.txt", "r", encoding="utf-8")
f_jp = open("./data_sources/日本.txt", "r", encoding="utf-8")
f_in = open("./data_sources/印度.txt", "r", encoding="utf-8")

us_dict = json.load(f_us)
jp_dict = json.load(f_jp)
in_data = json.load(f_in)

f_us.close()
f_jp.close()
f_in.close()

# 获取trend key
us_trend_data = us_dict['data'][0]['trend']
jp_trend_data = jp_dict['data'][0]['trend']
in_trend_data = in_data['data'][0]['trend']

us_x_data = us_trend_data['updateDate'][:314]
jp_x_data = jp_trend_data['updateDate'][:314]
in_x_data = in_trend_data['updateDate'][:314]

us_y_data = us_trend_data['list'][0]['data'][:314]
jp_y_data = jp_trend_data['list'][0]['data'][:314]
in_y_data = in_trend_data['list'][0]['data'][:314]

line = Line()
line.set_global_opts(
    # title
    title_opts=TitleOpts(title="2020年美日印三国确诊人数折线图", pos_top="center", pos_bottom="1%")
)
line.add_xaxis(us_x_data)
line.add_yaxis("美国确诊人数", us_y_data, label_opts=LabelOpts(is_show=False))
line.add_yaxis("日本确诊人数", jp_y_data, label_opts=LabelOpts(is_show=False))
line.add_yaxis("印度确诊人数", in_y_data, label_opts=LabelOpts(is_show=False))

line.render()

地图

本案例介绍了如何使用Python的pyecharts库来构建地图可视化图表,特别是针对疫情数据的可视化。

  1. 基础地图使用
  • 疫情地图:包括国内疫情地图和省级疫情地图。
  1. 核心技能
  • 掌握使用pyecharts构建基础的全国地图可视化图表。
  1. 案例效果
  • 展示疫情数据的分布情况,通过颜色深浅表示疫情的严重程度。
  1. 数据整理
  • 获取数据:首先需要获取全国及各省的疫情数据。
  • 数据结构:了解数据的整体结构,包括全国和省数据结构。
  • 确诊数据:获取每个省份的确诊数据。
  1. 国内疫情地图
  • 创建地图:使用pyecharts创建地图。
  • 添加数据:将疫情数据添加到地图中。
  • 设置全局配置选项:包括是否为分段型,自定义每一段的范围、文字和样式。
  1. 省疫情地图
  • 效果展示:展示省级疫情地图的效果。
  • 数据获取:以河南省为例,获取各市数据。
  • 数据汇总:将各市数据汇总到一个列表中。
  • 生成地图:参考国内疫情地图生成河南省疫情地图。
  1. 函数总结
函数描述
set_global_opts设置全局配置选项,如视觉映射器的配置。
VisualMapOpts视觉映射器选项,用于定义颜色和数据范围的映射。
is_piecewise定义是否为分段型视觉映射。
pieces自定义分段的范围、标签和颜色。
  1. 代码示例
python
"""
演示河南省疫情地图开发
"""
import json
from pyecharts.charts import Map
from pyecharts.options import *

# 读取文件
f = open("D:/疫情.txt", "r", encoding="UTF-8")
data = f.read()
# 关闭文件
f.close()
# 获取河南省数据
# json数据转换为python字典
data_dict = json.loads(data)
# 取到河南省数据
cities_data = data_dict["areaTree"][0]["children"][3]["children"]

# 准备数据为元组并放入list
data_list = []
for city_data in cities_data:
    city_name = city_data["name"] + "市"
    city_confirm = city_data["total"]["confirm"]
    data_list.append((city_name, city_confirm))

# 手动添加济源市的数据
data_list.append(("济源市", 5))

# 构建地图
map = Map()
map.add("河南省疫情分布", data_list, "河南")
# 设置全局选项
map.set_global_opts(
    title_opts=TitleOpts(title="河南省疫情地图"),
    visualmap_opts=VisualMapOpts(
        is_show=True,           # 是否显示
        is_piecewise=True,      # 是否分段
        pieces=[
            {"min": 1, "max": 99, "lable": "1~99人", "color": "#CCFFFF"},
            {"min": 100, "max": 999, "lable": "100~9999人", "color": "#FFFF99"},
            {"min": 1000, "max": 4999, "lable": "1000~4999人", "color": "#FF9966"},
            {"min": 5000, "max": 9999, "lable": "5000~99999人", "color": "#FF6666"},
            {"min": 10000, "max": 99999, "lable": "10000~99999人", "color": "#CC3333"},
            {"min": 100000, "lable": "100000+", "color": "#990033"},
        ]
    )
)

# 绘图
map.render("河南省疫情地图.html")

动态柱状图

  1. 案例效果
  • 目标:通过pyecharts实现1960~2019年全世界各国GDP变化趋势的动态显示。
  1. 基础柱状图
  • 目标:构建基础柱状图,反转x和y轴,设置数值标签在右侧。
  1. 时间线配置
  • 目标:掌握基础的时间线配置动态图表,设置主题更改颜色样式。
  1. 需求分析
  • GDP数据处理:处理为亿级。
  • 时间轴:按年份设置。
  • 数据筛选:每年数据只显示前8名国家。
  • 标题动态:标题年份动态更改。
  • 主题设置:设置为LIGHT。
  1. 列表的sort方法
  • 功能:对列表进行排序,指定排序规则。
python
"""
扩展列表的sort方法
在学习了将函数作为参数传递后,我们可以学习列表的sort方法来对列表进行自定义排序
"""

# 准备列表
my_list = [["a", 33], ["b", 55], ["c", 11]]

# 排序,基于带名函数
# def choose_sort_key(element):
#     return element[1]
#
# my_list.sort(key=choose_sort_key, reverse=True)
# 排序,基于lambda匿名函数
my_list.sort(key=lambda element: element[1], reverse=True)

print(my_list)
  1. 数据处理
  • 读取数据:删除第一条数据。
  • 数据转换:转换为字典格式,方便处理。
  1. 准备时间线
  • 功能:准备时间线以支持动态图表。
  1. 自动播放和绘图
  • 功能:设置自动播放,绘制最终图表。

相关函数表格

函数/方法描述用途
Bar()构建柱状图对象用于创建基础柱状图
add_xaxis()添加x轴数据配置柱状图的x轴数据
add_yaxis()添加y轴数据配置柱状图的y轴数据
reversal_axis()反转x和y轴用于调整图表的显示方向
LabelOpts(position="right")设置标签位置将数值标签显示在柱状图的右侧
Timeline()创建时间线对象用于创建动态图表的时间线
sort(key=函数, reverse=True/False)列表排序对列表进行排序,指定排序规则
ThemeType.LIGHT设置主题将图表主题设置为LIGHT

这些函数和方法是实现动态柱状图的关键,通过这些方法可以有效地构建和配置图表,实现数据的动态可视化。

python
"""
演示第三个图表:GDP动态柱状图开发
"""
from pyecharts.charts import Bar, Timeline
from pyecharts.options import *
from pyecharts.globals import ThemeType

# 读取数据
f = open("D:/1960-2019全球GDP数据.csv", "r", encoding="GB2312")
data_lines = f.readlines()
# 关闭文件
f.close()
# 删除第一条数据
data_lines.pop(0)
# 将数据转换为字典存储,格式为:
# { 年份: [ [国家, gdp], [国家,gdp], ......  ], 年份: [ [国家, gdp], [国家,gdp], ......  ], ...... }
# { 1960: [ [美国, 123], [中国,321], ......  ], 1961: [ [美国, 123], [中国,321], ......  ], ...... }
# 先定义一个字典对象
data_dict = {}
for line in data_lines:
    year = int(line.split(",")[0])      # 年份
    country = line.split(",")[1]        # 国家
    gdp = float(line.split(",")[2])     # gdp数据
    # 如何判断字典里面有没有指定的key呢?
    try:
        data_dict[year].append([country, gdp])
    except KeyError:
        data_dict[year] = []
        data_dict[year].append([country, gdp])

# print(data_dict[1960])
# 创建时间线对象
timeline = Timeline({"theme": ThemeType.LIGHT})
# 排序年份
sorted_year_list = sorted(data_dict.keys())
for year in sorted_year_list:
    data_dict[year].sort(key=lambda element: element[1], reverse=True)
    # 取出本年份前8名的国家
    year_data = data_dict[year][0:8]
    x_data = []
    y_data = []
    for country_gdp in year_data:
        x_data.append(country_gdp[0])   # x轴添加国家
        y_data.append(country_gdp[1] / 100000000)   # y轴添加gdp数据

    # 构建柱状图
    bar = Bar()
    x_data.reverse()
    y_data.reverse()
    bar.add_xaxis(x_data)
    bar.add_yaxis("GDP(亿)", y_data, label_opts=LabelOpts(position="right"))
    # 反转x轴和y轴
    bar.reversal_axis()
    # 设置每一年的图表的标题
    bar.set_global_opts(
        title_opts=TitleOpts(title=f"{year}年全球前8GDP数据")
    )
    timeline.add(bar, str(year))


# for循环每一年的数据,基于每一年的数据,创建每一年的bar对象
# 在for中,将每一年的bar对象添加到时间线中

# 设置时间线自动播放
timeline.add_schema(
    play_interval=1000,
    is_timeline_show=True,
    is_auto_play=True,
    is_loop_play=False
)
# 绘图
timeline.render("1960-2019全球GDP前8国家.html")

面向对象

重要概念

  • 封装:隐藏对象的内部状态和复杂性,仅暴露有限的接口。
  • 继承:允许新创建的类从现有的类中获取属性和方法。
  • 多态:不同对象可以共用一个接口,但各自的实现可以不同。

类和对象

类的定义

python
class Person:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address
  • class 关键字定义类。
  • __init__ 是类的构造方法,用于初始化对象。

创建和使用对象

python
# 创建对象
person1 = Person("Alice", 30, "123 Wonderland")

# 使用对象的属性和方法
print(f"Name: {person1.name}, Age: {person1.age}, Address: {person1.address}")

封装

  • 使用私有变量(以双下划线开头)隐藏类的内部状态。
    python
    class Person:
        def __init__(self, name, age, __address):
            self.name = name
            self.age = age
            self.__address = __address

继承

单继承

python
class Employee(Person):
    def __init__(self, name, age, address, job_title):
        super().__init__(name, age, address)
        self.job_title = job_title
  • super() 用于调用父类的构造方法。

多继承

python
class Engineer(Employee, Person):
    def __init__(self, name, age, address, job_title, skills):
        super().__init__(name, age, address, job_title)
        self.skills = skills

多态

多态的使用

python
class Animal:
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        print("Woof!")

class Cat(Animal):
    def speak(self):
        print("Meow!")

def animal_sound(animal):
    animal.speak()

# 多态性示例
dog = Dog()
cat = Cat()

animal_sound(dog)  # Woof!
animal_sound(cat)  # Meow!

pass关键字

pass 是 Python 中的一个关键字,它在语法上用作占位符,表示“什么都不做”。它的主要作用是作为最小化的、空的语句,使得 Python 程序在语法上正确,但在逻辑上什么都不执行。

使用场景

  1. 作为占位符:当你编写了一个函数或类,但暂时还不想实现它的内容时,可以使用 pass 作为占位符。
  2. 满足语法要求:在某些情况下,Python 语法要求必须有语句,即使没有实际的操作,这时候也需要使用 pass

示例

空的类定义

python
class EmptyClass:
    pass

上面的代码定义了一个空的类,其中不包含任何属性和方法,pass 表示这个类不执行任何操作。

空的方法定义

python
class MyClass:
    def do_nothing(self):
        pass

在这个例子中,do_nothing 方法是一个空方法,调用它不会有任何效果。

在条件语句中使用

python
if some_condition:
    pass
else:
    print("This will be printed.")

如果 some_condition 为真,程序将执行 pass,即什么都不做。如果为假,则执行 else 块中的代码。

抽象类和抽象方法

在面向对象编程中,pass 常用于定义抽象类和抽象方法。抽象方法是一种没有具体实现的方法,通常用于定义接口或规范,要求子类必须实现这些方法。

python
from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def make_sound(self):
        pass

class Dog(Animal):
    def make_sound(self):
        print("Woof!")

# 尝试创建抽象类的实例将会引发错误
# animal = Animal()

在这个例子中,Animal 是一个抽象类,它有一个抽象方法 make_sound,这个方法通过 pass 表示没有具体实现。任何继承自 Animal 的子类都必须实现 make_sound 方法,否则将不能实例化。

pass 是 Python 中一个简单但非常有用的关键字,它允许程序员在代码中预留位置,以便将来添加功能,或者在满足语法要求的同时不执行任何操作。在设计抽象类和方法时,pass 也扮演着重要的角色。

类内置方法和魔术方法

魔术方法

  • __init__:构造方法。
  • __str__:定义对象的字符串表示。
  • __lt____le____eq__:比较运算符方法。

示例

python
class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def __str__(self):
        return f"Student Name: {self.name}, Grade: {self.grade}"

    def __lt__(self, other):
        return self.grade < other.grade

# 使用魔术方法
student1 = Student("Alice", 90)
student2 = Student("Bob", 85)
print(student1)  # Student Name: Alice, Grade: 90
print(student1 < student2)  # True

类型注解

类型注解的使用

  • Python 3.5+ 引入类型注解,用于提高代码可读性和IDE支持。
  • 基本语法:变量名: 类型

示例

python
def greet(name: str) -> str:
    return f"Hello, {name}"

var: int = 10

数据组织

对象的数据组织

  • 使用对象代替简单变量,可以组织更加复杂的数据结构。

示例

python
class School:
    def __init__(self):
        self.students = []

    def add_student(self, student: Student):
        self.students.append(student)

    def display_students(self):
        for student in self.students:
            print(student)

school = School()
school.add_student(Student("Alice", 90))
school.add_student(Student("Bob", 85))
school.display_students()

综合案例:数据分析

示例代码

python
class FileReader:
    def read_data(self, file_path):
        with open(file_path, 'r') as file:
            return file.readlines()

class DataAnalyzer:
    def __init__(self, data):
        self.data = data

    def calculate_daily_sales(self):
        # 计算每日销售额的逻辑
        pass

    def generate_chart(self):
        # 使用pyecharts生成柱状图
        pass

# 使用示例
file_reader = FileReader()
data = file_reader.read_data("sales_data.txt")
analyzer = DataAnalyzer(data)
analyzer.calculate_daily_sales()
analyzer.generate_chart()

总结

  • 面向对象提供了一种组织代码的强大方式,能够提高代码的可维护性和可扩展性。
  • 类型注解和魔术方法是Python中增强代码表达能力的工具。
  • 面向对象的核心在于将数据和行为封装在对象中,并通过继承和多态提高代码的复用性。
mermaid
graph TD 
A

Python & MySQL

概述

  • pymysql:Python中用于操作MySQL的第三方库。
  • 安装pip install pymysql

连接配置

创建到MySQL的数据库连接

  • 导入库:from pymysql import Connection
  • 创建连接:
    python
    conn = Connection(host='localhost', port=3306, user='root', password='yourpassword', db='yourdatabase')

执行SQL语句

非查询性质的SQL语句

  • 执行语句:cursor.execute(sql)
  • 提交更改:conn.commit()
  • 关闭连接:conn.close()

查询性质的SQL语句

  • 获取游标对象:cursor = conn.cursor()
  • 执行查询:cursor.execute(sql)
  • 获取结果:results = cursor.fetchall()
  • 关闭游标:cursor.close()

示例代码

插入数据

python
import pymysql

# 创建数据库连接
conn = pymysql.Connection(host='localhost', port=3306, user='root', password='yourpassword', db='yourdatabase', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor)

try:
    with conn.cursor() as cursor:
        # SQL 插入语句
        sql = "INSERT INTO tablename (column1, column2) VALUES (%s, %s)"
        cursor.execute(sql, ('value1', 'value2'))
    # 提交到数据库执行
    conn.commit()
finally:
    conn.close()

查询数据

python
import pymysql

# 创建数据库连接
conn = pymysql.Connection(host='localhost', port=3306, user='root', password='yourpassword', db='yourdatabase', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor)

try:
    with conn.cursor() as cursor:
        # SQL 查询语句
        sql = "SELECT * FROM tablename"
        cursor.execute(sql)
        results = cursor.fetchall()
        for row in results:
            print(row)
finally:
    conn.close()

自动提交

  • 设置自动提交:autocommit=True 在创建连接时

示例:自动提交

python
import pymysql

# 创建数据库连接(自动提交)
conn = pymysql.Connection(host='localhost', port=3306, user='root', password='yourpassword', db='yourdatabase', autocommit=True, charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor)

try:
    with conn.cursor() as cursor:
        # SQL 插入语句
        sql = "INSERT INTO tablename (column1, column2) VALUES (%s, %s)"
        cursor.execute(sql, ('value1', 'value2'))
finally:
    conn.close()

PySpark

  1. PySpark简介
  • 定义: Apache Spark是一个用于大规模数据处理的统一分析引擎,而PySpark是Spark的Python API。
  • 特点: 支持分布式计算,能够处理TB甚至PB级别的数据。
  • 应用场景: 大数据开发、人工智能。
  1. 安装PySpark
bash
pip install pyspark
# 或者使用国内源
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark
  1. PySpark编程模型

  2. 数据输入: 通过SparkContext读取数据,生成RDD。

  3. 数据处理: 使用RDD的各种transformation和action操作进行数据处理。

  4. 数据输出: 将处理结果输出到文件、数据库等。

spark对象获取

python
# 导包
from pyspark import SparkConf, SparkContext
# 创建SparkConf类对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")
# 基于SparkConf类对象创建SparkContext对象
sc = SparkContext(conf=conf)
# 打印PySpark的运行版本
print(sc.version)
# 停止SparkContext对象的运行(停止PySpark程序)
sc.stop()
  1. SparkContext与RDD
  • SparkContext: 作为与Spark集群交互的主要入口,管理任务调度。
  • RDD (Resilient Distributed Dataset): 基本的数据结构,支持并行操作。

PySpark算子函数

以下是PySpark中常用的算子函数的总结:

算子描述示例代码
map(func)对RDD中的每个元素应用func函数,返回一个新的RDD。rdd.map(lambda x: x * 2)
flatMap(func)类似map,但每个输入元素可以映射到0或多个输出元素(因此可以"展平")。rdd.flatMap(lambda x: [x, x*2])
filter(func)返回一个新的RDD,其中包含满足func条件的所有元素。rdd.filter(lambda x: x > 10)
reduceByKey(func)对RDD中的元素进行归约操作,通常用于键值对RDD。rdd.reduceByKey(lambda a, b: a + b)
groupByKey()将具有相同键的值分组在一起。rdd.groupByKey()
sortBy(func)根据func函数返回的值对RDD元素进行排序。rdd.sortBy(lambda x: x[1], ascending=False)
distinct()返回一个新的RDD,其中所有元素都是唯一的。rdd.distinct()
union(other)返回一个包含所有原始RDD和other RDD元素的新RDD。rdd.union(other_rdd)
  1. 数据输入与输出

数据输入

  • 从Python容器转换:

    python
    sc = SparkContext()
    rdd = sc.parallelize([1, 2, 3, 4, 5])
  • 从文件读取:

    python
    rdd = sc.textFile("path/to/file.txt")

数据输出

  • 输出到文件:
    python
    rdd.saveAsTextFile("path/to/output")
  • 转换为Python列表:
    python
    result = rdd.collect()
  1. 综合案例:WordCount
python
from pyspark import SparkContext

sc = SparkContext()

# 读取文件
text_rdd = sc.textFile("path/to/file.txt")

# 切分单词
words = text_rdd.flatMap(lambda line: line.split())

# 计数
word_counts = words.map(lambda word: (word, 1)).reduceByKey(lambda a, b: a + b)

# 收集结果
print(word_counts.collect())
  1. 分布式集群运行
  • 提交命令:

    bash
    bin/spark-submit --master yarn --num-executors 3 --queue root.teach --executor-cores 4 --executor-memory 4g /home/hadoop/demo.py

闭包

  1. 闭包的定义

闭包是在函数嵌套的情况下,内部函数引用了外部函数的变量,并且外部函数返回了内部函数,这样内部函数就被称为闭包。

  1. 闭包的作用
  • 数据封装:闭包允许我们封装数据和相关操作,使得数据在函数外部不可见。
  • 数据持久化:闭包可以持续访问外部函数的变量,即使外部函数已经执行完毕。
  1. 闭包的实现
  • 简单闭包:返回内部函数,该内部函数引用了外部函数的变量。
  • 修改外部变量:使用nonlocal关键字,允许内部函数修改外部函数的局部变量。
  1. 闭包的优点
  • 避免全局变量:通过闭包可以避免使用全局变量,使得代码更加模块化。
  • 保护变量:闭包中的变量不直接暴露在外部,避免了被错误地修改。
  1. 闭包的缺点
  • 内存占用:由于闭包持续引用外部变量,可能会增加内存的占用。
  1. 闭包的代码示例
python
def create_atm():
    account_amount = 0  # 外部变量
    
    def withdraw(amount):
        nonlocal account_amount  # 使用nonlocal声明
        account_amount -= amount
        return account_amount
    
    def deposit(amount):
        nonlocal account_amount
        account_amount += amount
        return account_amount
    
    return withdraw, deposit

# 使用闭包
withdraw, deposit = create_atm()
print(withdraw(100))  # 取钱
print(deposit(200))   # 存钱
  1. 注意事项
  • 内存管理:注意闭包引用的变量可能会导致内存泄漏,尤其是当闭包作为回调函数时。
  • 作用域链:理解闭包如何通过作用域链访问外部变量是非常重要的。

优点

  • 数据封装:闭包提供了一种数据封装的手段,使得函数的状态(即变量)可以被保持。
  • 模块化:闭包使得代码更加模块化,通过函数封装实现细节,外部只能通过闭包提供的接口与之交互。
  • 避免全局变量:闭包提供了一种避免使用全局变量的方法,有助于减少全局命名空间的污染。
  • 灵活性:闭包允许创建可以接受参数并返回新函数的函数,这增加了代码的灵活性。

缺点

  • 内存消耗:由于闭包持续引用外部函数的局部变量,这可能会导致额外的内存消耗,因为这些变量不能被垃圾回收。
  • 复杂性:过度使用闭包可能会使得代码难以理解和维护,尤其是对于初学者来说,闭包的概念可能比较抽象。
  • 调试难度:闭包可能会使得调试变得更加困难,因为变量的作用域和生命周期与传统的函数调用模型不同。

装饰器

什么是装饰器

装饰器是 Python 中一个非常强大的功能,本质上是一种闭包,它允许用户在不修改原有函数代码的情况下,给函数增加新的功能。

装饰器的作用

  • 增强函数的功能,如:日志记录、性能测试、事务处理、缓存、权限校验等。
  • 装饰器可以带参数,使得装饰器更加灵活。

装饰器的基本写法

  1. 定义一个闭包函数,在这个闭包函数内部调用目标函数。
  2. 在闭包函数内部,执行目标函数之前和之后添加额外的代码,以实现额外的功能。

装饰器的语法糖

使用 @ 符号,将装饰器应用到目标函数上,简化了装饰器的使用。

装饰器的写法示例

python
def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Something before the function is executed")
        result = func(*args, **kwargs)
        print("Something after the function is executed")
        return result
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

带参数的装饰器

装饰器本身也可以接收参数,使得装饰器更加通用和灵活。

装饰器带参数的写法

python
def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def greet(name):
    print(f"Hello {name}")

greet("World")

装饰器的应用场景

  • 缓存:通过装饰器实现函数结果的缓存,避免重复计算。
  • 日志:通过装饰器记录函数的调用信息,如时间、参数等。
  • 权限控制:通过装饰器实现函数的访问权限控制。
  • 事务处理:在数据库操作中,通过装饰器实现事务的提交和回滚。

注意事项

  • 装饰器会改变原函数的一些属性,如 __name____doc__ 等,可以通过 functools.wraps 来保留这些属性。
  • 装饰器可能会使代码的阅读和调试变得复杂,应谨慎使用。

通过学习装饰器,我们可以更加灵活和高效地扩展函数的功能,是 Python 编程中不可或缺的一部分。

设计模式

  1. 什么是设计模式

设计模式是一套被反复使用的、多数人知晓的、经过分类编目的代码设计经验的总结。它描述了软件设计过程中的通用问题以及解决这些问题的方案。

  1. 常见的设计模式

单例模式(Singleton Pattern)

  • 定义:确保一个类只有一个实例,并提供一个全局访问点。
  • 适用场景:当一个类只能有一个实例,且客户可以从一个众所周知的访问点访问它时。
  • 特点
    • 节省内存。
    • 节省创建对象的开销。
  • 实现方式
    • 私有化构造函数。
    • 提供一个静态方法来获取唯一的实例。

单例模式示例

python
class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

# 使用
singleton1 = Singleton()
singleton2 = Singleton()
print(singleton1 is singleton2)  # 输出 True

工厂模式(Factory Pattern)

  • 定义:定义一个创建对象的接口,让子类决定实例化哪一个类。工厂模式使一个类的实例化延迟到其子类。
  • 适用场景:当需要大量创建一个类的实例时。
  • 特点
    • 易于代码维护。
    • 符合现实世界的模式。
  • 实现方式
    • 工厂类负责创建对象。
    • 通过工厂方法来创建具体类的对象。

工厂模式示例

python
class Person:
    def __init__(self, name):
        self.name = name

class Factory:
    @staticmethod
    def get_person(name):
        return Person(name)

# 使用
person1 = Factory.get_person("Alice")
print(person1.name)  # 输出 Alice
  1. 设计模式的好处
  • 提高代码的可重用性:设计模式提供了经过验证的解决方案,可以被重复应用到相似的问题上。
  • 提高代码的可维护性:设计模式提供了清晰的结构,使得代码更易于理解和修改。
  • 促进协作:设计模式是通用的,团队成员可以更容易地理解彼此的代码。

设计模式的注意事项

  • 不要过度使用:设计模式应该在合适的场景下使用,过度使用可能会使系统变得复杂。
  • 理解模式的本质:在使用设计模式之前,应该深入理解其背后的原理和适用场景。

设计模式是软件工程中的重要概念,它们提供了解决常见问题的通用方法。理解和掌握设计模式,可以提高编程的效率和质量。

多线程

  1. 什么是多线程

多线程是指在单个程序中可以同时运行多个线程执行不同的任务。线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

  1. 进程与线程的区别
  • 进程:操作系统进行资源分配和调度的基本单位。进程之间是内存隔离的,每个进程拥有独立的内存空间。
  • 线程:是进程内部的一个实体,是被系统独立调度和分派的基本单位。线程自身基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如执行栈),但它可以与同属一个进程的其他线程共享进程所拥有的全部资源。
  1. 并行执行
  • 多任务并行执行:操作系统可以同时运行多个进程,这些进程都在并行执行。
  • 多线程并行执行:一个进程内的多个线程可以同时运行,实现在同一个进程内部的并行处理。
  1. Python多线程编程

Python的多线程可以通过threading模块来实现。threading模块提供了更多易用的高级API,用于多线程编程。

4.1 threading模块的使用

  • 创建线程对象

    python
    import threading
    thread_obj = threading.Thread(target=func)

    其中func是线程要执行的函数。

  • 启动线程

    python
    thread_obj.start()

4.2 线程传参

  • 通过args传参:使用元组的形式,按照参数顺序传给线程函数。
  • 通过kwargs传参:使用字典的形式,传给线程函数。

4.3 线程同步

在多线程编程中,线程同步是一个重要的问题,需要防止多个线程同时访问共享资源造成数据错乱。可以使用锁(Lock)等机制来实现线程同步。

  1. 多线程编程实例

5.1 创建并启动线程

python
import threading

def print_numbers():
    for i in range(1, 6):
        print(i)

# 创建线程
thread1 = threading.Thread(target=print_numbers)
# 启动线程
thread1.start()

5.2 传参给线程

python
def print_numbers_with_args(a, b):
    print(f"Argument a: {a}, Argument b: {b}")

# 创建线程并传参
thread2 = threading.Thread(target=print_numbers_with_args, args=(10, 20))
# 启动线程
thread2.start()
  1. python
    """
    演示多线程编程的使用
    """
    import time
    import threading
    
    def sing(msg):
        print(msg)
        time.sleep(1)
    
    
    
    def dance(msg):
        print(msg)
        time.sleep(1)
    
    
    if __name__ == '__main__':
        # 创建一个唱歌的线程
        sing_thread = threading.Thread(target=sing, args=("我要唱歌 哈哈哈", ))
        # 创建一个跳舞的线程
        dance_thread = threading.Thread(target=dance, kwargs={"msg": "我在跳舞哦 啦啦啦"})
    
        # 让线程去干活吧
        sing_thread.start()
        dance_thread.start()
  2. 注意事项

  • 全局解释器锁(GIL):由于Python的GIL问题,CPython解释器下的多线程可能不会带来计算密集型任务的性能提升。GIL确保同一时间只有一个线程执行Python字节码,因此多线程主要用于I/O密集型任务。
  • 线程安全:当多个线程访问共享数据时,需要确保线程安全,避免数据竞争和不一致性。
  1. 多线程与多进程
  • 多线程:适用于I/O密集型任务,资源消耗小,但受GIL影响,不适合计算密集型任务。
  • 多进程:适用于计算密集型任务,不受GIL影响,但资源消耗大,进程间通信复杂。
  1. 结论

多线程编程是提高程序性能和响应能力的有效手段,特别是在I/O密集型任务中。通过threading模块,Python提供了丰富的接口来支持多线程编程。然而,开发者需要注意线程安全和GIL的影响,合理选择多线程和多进程的策略。

网络编程Socket

  1. 什么是Socket

Socket,简称“套接字”,是进程间通信的端点,可以看作是各个运行在不同主机上的进程之间进行双向通信的通道。Socket是网络编程的核心,负责进程间的网络数据传输。

  1. Socket的基本操作

服务端

  • 创建Socket对象:首先需要创建一个Socket实例。
  • 绑定地址:将Socket与特定的IP地址和端口号绑定。
  • 监听连接:使Socket进入监听状态,等待客户端的连接请求。
  • 接受连接:接受客户端的连接请求,建立连接。
  • 接收和发送数据:通过Socket接收客户端发送的数据,或向客户端发送数据。
  • 关闭连接:完成数据传输后,关闭Socket连接。

客户端

  • 创建Socket对象:与服务端类似,首先创建一个Socket实例。
  • 连接服务端:通过指定服务端的IP地址和端口号,发起连接请求。
  • 发送和接收数据:连接成功后,可以向服务端发送数据,或接收服务端发送的数据。
  • 关闭连接:数据传输完成后,关闭Socket连接。
  1. Socket编程示例

服务端示例

python
"""
演示Socket服务端开发
"""
import socket
# 创建Socket对象
socket_server = socket.socket()
# 绑定ip地址和端口
socket_server.bind(("localhost", 8888))
# 监听端口
socket_server.listen(1)
# listen方法内接受一个整数传参数,表示接受的链接数量
# 等待客户端链接
# result: tuple = socket_server.accept()
# conn = result[0]        # 客户端和服务端的链接对象
# address = result[1]     # 客户端的地址信息
conn, address = socket_server.accept()
# accept方法返回的是二元元组(链接对象, 客户端地址信息)
# 可以通过 变量1, 变量2 = socket_server.accept()的形式,直接接受二元元组内的两个元素
# accept()方法,是阻塞的方法,等待客户端的链接,如果没有链接,就卡在这一行不向下执行了

print(f"接收到了客户端的链接,客户端的信息是:{address}")

while True:
    # 接受客户端信息,要使用客户端和服务端的本次链接对象,而非socket_server对象
    data: str = conn.recv(1024).decode("UTF-8")
    # recv接受的参数是缓冲区大小,一般给1024即可
    # recv方法的返回值是一个字节数组也就是bytes对象,不是字符串,可以通过decode方法通过UTF-8编码,将字节数组转换为字符串对象
    print(f"客户端发来的消息是:{data}")
    # 发送回复消息
    msg = input("请输入你要和客户端回复的消息:")
    if msg == 'exit':
        break
    conn.send(msg.encode("UTF-8"))
# 关闭链接
conn.close()
socket_server.close()

客户端示例

python
"""
演示Socket客户端开发
"""
import socket
# 创建socket对象
socket_client = socket.socket()
# 连接到服务端
socket_client.connect(("localhost", 8888))

while True:
    # 发送消息
    msg = input("请输入要给服务端发送的消息:")
    if msg == 'exit':
        break
    socket_client.send(msg.encode("UTF-8"))
    # 接收返回消息
    recv_data = socket_client.recv(1024)        # 1024是缓冲区的大小,一般1024即可。 同样recv方法是阻塞的
    print(f"服务端回复的消息是:{recv_data.decode('UTF-8')}")
# 关闭链接
socket_client.close()
  1. 注意事项
  • 异常处理:网络编程中,需要对可能发生的异常进行处理,如连接失败、数据传输错误等。
  • 资源清理:确保在数据传输完成后,及时关闭Socket连接,释放资源。
  • 安全性:在网络通信中,要注意数据的安全性,如使用加密传输、验证通信双方的身份等。
  1. 测试工具

可以使用网络调试助手等工具模拟客户端与服务端进行通信,以测试Socket程序的正确性。

正则表达式

正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,字母a到z)和特殊字符(称为"元字符")。它用于检索、替换符合某个模式(规则)的文本。

  • 文本搜索:在大量文本中搜索符合某个模式的字符串。
  • 文本替换:在文本中替换符合某个模式的字符串。
  • 验证输入:验证用户的输入是否符合特定格式(如邮箱、电话号码等)。

在Python中,正则表达式通过re模块实现。

3.1 基础方法

  • re.match():从字符串的起始位置匹配模式,如果匹配成功,返回一个匹配对象;否则返回None
  • re.search():扫描整个字符串并返回第一个成功的匹配。
  • re.findall():找出字符串中所有匹配的子串,并返回一个列表。

3.2 高级方法

  • re.sub():替换文本中符合某个模式的字符串。
  • re.compile():编译正则表达式,返回正则表达式对象,供后续使用。

匹配规则

单字符匹配

字符功能
.匹配任意1个字符(除了\n),\.匹配点本身
[]匹配[]中列举的字符
\d匹配数字,即θ-9
D匹配非数字
s匹配空白,即空格、 tab键
\S匹配非空白
\w匹配单词字符,即a-Z、A-Z、0-9、_
\W匹配非单词字符

数量匹配

字符功能
*匹配前一个规则的字符出现0至无数次
+匹配前一个规则的字符出现1至无数次
?匹配前一个规则的字符出现0次或1次
{m}匹配前一个规则的字符出现m次
{m,}匹配前一个规则的字符出现最少m次
{m,n}匹配前一个规则的字符出现m到n次

边界匹配

字符功能
^匹配字符串开头
$匹配字符串结尾
\b匹配一个单词的边界
\B匹配非单词边界

分组匹配

字符功能
``
()将括号中字符作为一个分组
  1. 字符串的原始标记

在Python字符串前加上rR,表示原始字符串,忽略字符串中的转义字符。

例如:

python
pattern = re.compile(r"\d+")
  1. 正则表达式案例

6.1 匹配账号

账号由字母和数字组成,长度限制6到10位:

python
import re
pattern = re.compile(r"^[0-9a-zA-Z]{6,10}$")
result = pattern.match("user123")
print(result)  # None or <re.Match object>

6.2 匹配QQ号

QQ号要求纯数字,长度5-11,第一位不为0:

python
pattern = re.compile(r"^[1-9][0-9]{4,10}$")
result = pattern.match("10000")
print(result)  # None or <re.Match object>

6.3 匹配邮箱地址

只允许qq、163、gmail这三种邮箱地址:

python
pattern = re.compile(r"^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$")
result = pattern.match("user@gmail.com")
print(result)  # None or <re.Match object>
  1. 注意事项
  • 性能问题:复杂的正则表达式可能会降低程序性能。
  • 可读性:过于复杂的正则表达式可能会降低代码的可读性。
  • 特殊字符:在正则表达式中,某些字符具有特殊含义,如.*等,需要使用\进行转义。
  1. 结论

正则表达式是一种强大的文本处理工具,通过掌握其基本语法和元字符规则,可以有效地进行文本搜索、替换和验证等操作。在实际应用中,合理使用正则表达式能够提高开发效率和处理文本数据的能力。