2024年8月

前言

本文介绍卷积神经网络的上半部分。
其实,学习还是需要老师的,因为我自己写文章的时候,就会想当然,比如下面的滑动窗口,我就会想当然的认为所有人都能理解,而实际上,我们在学习的过程中之所以卡顿的点多,就是因为学习资源中想当然的地方太多了。

概念

卷积神经网络,简称CNN, 即Convolutional Neural Network的缩写。

滤波器/卷积核(Filter/Kernels)

卷积核是一个小矩阵(通常是3x3、5x5等),它在输入图像上滑动(即移动),并与图像的局部区域进行矩阵乘法(点积)操作。结果是一个单值,这个值代表了该局部区域的某种特征。
点积就是内积,就是np.dot函数,内积是个值,就是两个矩阵对应项相乘,在相加
例如。a=[2,3] 和 b=[4,5],它们的点积是a⋅b=(2×4)+(3×5)=8+15=23
点积的意义是a⋅b=∥a∥∥b∥cosθ,意思是说,点积等于a向量的模乘以b向量的模乘以ab的夹角θ的cos的值
向量的模就是向量的长度,v=[3,4],因为勾股定理,c²=a²+b²,所以∥v∥=c=根号下a²+b²=根号下9+16=根号下25=5
例。rgb图,是3通道,卷积核会在3个通道上都进行卷积操作,最后形成一个特征图。

卷积核的尺寸

如果尺寸是 5×5,那么滑动窗口的大小也是 5×5。
image

特征图(Feature Map)

当一个卷积核(或滤波器)滑动在输入图像上时,它会在每一个位置计算卷积核与输入图像区域的点积,结果是一个标量。通过滑动整个图像,得到一组标量值,这些值构成了一个新的二维矩阵,这个矩阵就是特征图。
在CNN中,使用越多的卷积核,意味着提取的特征图越多,因此卷积核越大就可以得到的越丰富的特征。
更多的卷积核意味着更多的计算和内存消耗。因此,在选择卷积核数量时,也要考虑硬件资源的限制。

最大池化层(Max Pooling Layer)

是卷积神经网络(CNN)中常用的下采样(或降采样)技术。它用于减小特征图的尺寸,从而减少计算量,并有助于控制模型的复杂度(防止过拟合)。
最大池化操作使用一个固定大小的窗口(通常是2x2或3x3),在特征图上滑动。
在窗口覆盖的区域内,最大池化层会选择该区域的最大值作为输出。
步幅决定了池化窗口在特征图上滑动的步长。步幅为2意味着窗口每次移动2个像素。
每次池化操作生成的特征图尺寸会比输入特征图小。池化操作会减少特征图的宽度和高度,但保持深度(通道数)不变。
例,4x4 的输入特征图如下

1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

使用 2x2 的最大池化窗口和步幅为 2,池化过程如下:
池化窗口覆盖 1 2 5 6,最大值为 6
池化窗口覆盖 3 4 7 8,最大值为 8
池化窗口覆盖 9 10 13 14,最大值为 14
池化窗口覆盖 11 12 15 16,最大值为 16
得到的输出特征图为:

6 8
14 16

结合代码理解

结合下面的代码理解上面的概念。

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
# device config
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# hyper parameters
batch_size = 4
learning_rate = 0.001
num_epochs = 0

# dataset has PILImage images of range [0, 1].# We transform them to Tensors of normalized range [-1, 1]
# transforms.ToTensor():将PIL图像或numpy数组转换为PyTorch张量,并将值范围从[0,1]变为[0,255]。
# transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)):对图像进行归一化处理,将图像的像素值调整到[-1,1]范围。
transform = transforms.Compose(
    [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])


train_dataset = torchvision.datasets.CIFAR10(
    root='./data', train=True, download=True, transform=transform)

test_dataset = torchvision.datasets.CIFAR10(
    root='./data', train=False, download=True, transform=transform)

train_loader = torch.utils. data.DataLoader(
    dataset=train_dataset, batch_size=batch_size, shuffle=True)

test_loader = torch.utils.data.DataLoader(
    dataset=test_dataset, batch_size=batch_size, shuffle=False)


print('每份100个,被分成多了份', len(train_loader))

def imshow(img):
    img = img / 2 + 0.5  # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()


# get some random training images
dataiter = iter(train_loader)
images, labels = dataiter.__next__()
# show images
imshow(torchvision.utils.make_grid(images))

# nn.Conv2d 是 PyTorch 用于定义二维卷积层的类
# 三个参数分别为 in_channels、out_channels 和 kernel_size
# in_channels (输入通道数):
# 值=3 这是输入图像的通道数。对于彩色图像,通常有3个通道(对应于RGB),因此这里的值为3。如果输入的是灰度图像,通常只有1个通道。
# 对于彩色图像(RGB),输入图像有3个通道。卷积核对每个通道独立进行操作,然后将这些结果相加,得到输出特征图。
# 输出特征图的数量由卷积核的数量决定。如果你有多个卷积核,它们会捕捉输入图像中的不同特征,每个卷积核生成一个特征图
# out_channels (输出通道数):
# 值=6 这是卷积层输出的通道数,也称为卷积核的数量。这个参数决定了卷积操作后生成多少个不同的特征图。在本例中,卷积层会生成6个特征图,也就是说会有6个不同的卷积核应用于输入图像。
# kernel_size (卷积核大小):
# 值=5 这是卷积核的尺寸,表示卷积核的宽度和高度。这里使用的是 5x5 的卷积核。这意味着每个卷积核会查看输入图像的 5x5 像素区域,并通过滑动窗口方式在整个图像上进行卷积操作。
# conv1:第一个卷积层,将输入的3通道图像(RGB)通过一个5x5的卷积核,生成6个输出通道。这里,卷积层使用 6 个卷积核,每个卷积核会生成一个特征图。因此,该卷积层的输出是 6 个特征图,特征图的深度(通道数)为 6。
conv1 = nn.Conv2d(3, 6, 5)
# pool:最大池化层,将特征图的尺寸缩小一半。
# 第一个参数 (2): 池化窗口的大小。这表示池化操作将应用于一个 2x2 的窗口上。池化窗口决定了在特征图上进行池化操作的区域大小。 
# 第二个参数 (2): 池化的步幅(stride)。步幅决定了池化窗口在特征图上滑动的步长。步幅为 2 意味着池化窗口每次移动 2 个像素。
pool = nn.MaxPool2d(2, 2)
# conv2:第二个卷积层,将6个通道的输入特征图通过一个5x5的卷积核,生成16个输出通道。
conv2 = nn.Conv2d(6, 16, 5)
print(images.shape)


x = conv1(images)
# print(x.shape)
x = pool(x)
# print(x.shape)
x = conv2(x)
# print(x.shape)
x = pool(x)  # print(x.shape)


传送门:
零基础学习人工智能—Python—Pytorch学习—全集


注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!



若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

https://www.cnblogs.com/kiba/p/18375380

概述


数制与编码

  • 进位计数制及其相互转换;定点数的编码表示

运算方法和运算电路

  • 基本运算部件:加法器;算术逻辑单元(ALU)
  • 加减运算:补码加/减运算器;标志位的生成
  • 乘/除运算:乘/除法运算的基本原理;乘法电路和除法电路的基本结构

数制与编码

进位计数制及其相互转换


采用二进制编码的原因

  1. 二进制只有两种状态,使用有两个稳定状态的物理器件就可以表示二进制数的每一位,制造成本比较低。
  2. 二进制位1和0正好与逻辑值“真”和“假”相对应,为计算机实现逻辑运算和程序中的逻辑判断提供了便利条件。
  3. 二进制编码和运算规则都很简单,通过逻辑门电路能方便地实现算术运算。

进位计数法

  1. 计算机通常使用
    二进制数

    八进制数

    十六进制数

  2. 进位计数法中,每个数位所用到的不同数码的个数称为
    基数
    。每个数码所表示的数值等于该数码本身乘以一个与它所在数位有关的常数,这个常数称为
    位权

  3. 一个r进制数$ K_nK_{n-1}\dots K_0K_{-1}\dots K_{-m}$的数值可以表示为:

\[K_nr^n+K_{n-1}r^{n-1}+\cdots+K_0r^0+K_{-1}r^{-1}+\cdots+K_{im}r^{-m}=\sum^{-m}_{i=n}K_ir^i
\]

  1. 可以用后缀字母标识一个数的进位计数制,用B表示二进制,用O表示八进制,用D表示十进制(通常省略),用H表示十六进制,有时也用前缀0x表示十六进制数。
109=109D=1101101B=155O=5CH=0x5C

进制转换
将十进制数123.6875转换成二进制数

  • 整数部分采用
    除基取余法

    description

  • 小数部分采用
    乘基取整法

    description

定点数的编码表示


真值和机器数

  1. 真值
    :数据在逻辑上或数学上的实际值。
  2. 机器数
    :计算机内部存储和处理的数值。

机器数的定点表示

  1. 根据小数点的位置是否固定,将数据格式区分为:
    定点表示

    浮点表示
  2. 浮点表示
    :用补码整数表示整数,用原码小数表示浮点数的尾数部分,用移码表示浮点数的阶码部分。
  3. 定点小数
    :定点小数是纯小数,约定小数点位置在符号位之后、有效数值部分最高位之前。
  4. 浮点小数
    :定点整数是纯整数,约定小数点位置在有效数值部分最低位之后。
  5. 定点数的编码表示法有四种:原码、补码、反码和移码。
    description

原码
用机器数的最高位表示数的符号,其余各位表示数的绝对值。

+0111->0 0000111
-1011->1 0001011

反码
正数的反码与原码相同,对于负数的反码,符号位保持不变,而数值位则按位取反。

+1011->0 0001011
-1011->1 1110100

补码
正数的补码和原码相同,负数的补码等于模
\(2^{n+1}\)
与绝对值之差,其中n为尾数位数。
description

也可以采用“各位取反,末位+1”的方法得到
description

机器数BAH为补码,其表示的数值为多少?
BAH = 1011 1010B
符号:-
数值:011 1010-1=011 1001--取反--> 100 0110 = 70
故表示-70

移码
移码场用来表示浮点数的阶码。移码是在真值
\(X\)
上加上一个常数(偏置值),通常这个常数取
\(2^n\)

+1011 -> 10001011
-1011 -> 01110101

同一个数的反码、移码的数值位相同,符号位相反

无符号整数
当一个编码的全部二进制位均为数值位,而没有符号位时,该编码表示就是
无符号整数
,简称无符号数

C语言中的整数类型及类型转换


C语言规定了如下类型:

  • 字符型char
    :8位/1字节
  • 短整型short
    : 16位/2字节
  • 整型in
    t:32位/4字节
  • 长整型long
    :在32位机器中为32位,64位机器中为64位

char默认是无符号整数,short/int/double默认是有符合整数

强制类型转换

在有符号整型和无符号整型的强制类型转换时,位值保持不变,仅改变了解释值的方式。
description

不同字长整数之间的转换

长->短
:当长字长变量向短字长变量强制类型转换时,数值位的高位部分直接截断。
短->长
:当短字长变量向长字长变量强制类型转换时,数值位的高位部分补0。

运算方法和运算电路

基本运算部件


基本运算部件

  1. 运算器组成:运算器由
    算术逻辑单元(ALU)

    移位器

    状态寄存器(PSW)

    通用寄存器
    等组成。
  2. 运算器的基本功能包括四则运算、逻辑运算、移位、求补等。
  3. ALU的核心部件是加法器。

全加器及其工作原理

全加器
:全加器是一种用于二进制加法的基本电路元件。它的主要功能是对两个二进制数位以及一个进位输入进行加法运算。
全加器有三个输入:A加数、B加数、C来自前一位的进位输入。
全加器有两个输出:F和,加法结果的当前位;Cout进位输出,向下一位进位的值。

全加器输入和输出逻辑关系如下:

  • F和

    \(F=A\oplus B \oplus C_{in}\)

简单理解:保留计算结果的最低位。
1+1+0=10 -> F=0

  • Cout进位输出

    \(C_{out}=(A\land B)\lor(A\land C_{in})\lor(B\land C_{in})\)

简单理解:A、B、Cin只要有任意两个是1,则Cout为1。
A=1, B=1, Cin=1 -> Cout=1

description

带标志加法器

为了能进行有符号整数的加/减运算,需要在无符号加法器的基础上增加相应的逻辑门电路,使得加法器不仅能计算和/差,还要能生成相应的标志信息。

description

图:带标志加法器符号

标志信息的逻辑表达式如下:

  • 溢出标志OF:
    \(OF=C_n \oplus C_{n-1}\)
  • 符号标志SF:
    \(SF=F_{n-1}\)
  • 零标志ZF:
    \(ZF=0\)
    当且仅当
    \(F=0\)
  • 进位/借位标志CF:
    \(CF=C_{out}\oplus C_{in}\)

description

图:带标志加法器的逻辑电路

算术逻辑单元(ALU)

ALU是一种功能较强的组合逻辑电路,它能进行多种算术运算和逻辑运算。由于加、减、乘、除运算都能归结为加法运算,因此ALU的核心是带标志加法器。

ALUop是操作控制端,用来决定ALU所执行的处理功能。ALUop的位数决定了操作的种类。

description

在ALUop的控制下,由一个多路选择器(MUX)选择输出三种操作结果之一。

description

图:一位ALU的结构

定点数的移位运算


根据操作数的类型不同,移位运算可以分为逻辑移位和算术移位

逻辑移位

  • 逻辑移位将操作数视为无符号整数。
  • 规则:
    左移
    时,高位移出,低位补0;
    右移
    时,低位移出,高位补0。
左移:1011->0110
右移:1011->0101

位移如何判断是否有溢出?
左移:左移前后符号位相同则没有溢出,反之有溢出;
右移:右移不会发生溢出。

算术移位

计算机中的有符号整数都是用补码表示的,因此对于有符号整数的移位操作应采用补码算术移位方式。
左移
:数值位最高位移至符号位,其余为左移,最低位补零。
右移
:数值位右移,最高位补符号位。
description

定点数的加减运算


补码的加减法运算
补码运算规则如下:

  1. 按二进制运算规则运算。
  2. 若做
    加法
    ,两个数的补码直接相加;若做
    减法
    ,则将被减数与减数的负数补码相加。
  3. 符号位与数值位一起参与运算,加、减运算结果的符号位也在运算结果中直接得出
  4. 运算结果亦为补码。
    description
    图:补码加减法运算示意

溢出判别方式

\(A\)
的符号是
\(A_S\)
,B的符号是
\(B_S\)
,运算结果的符号是
\(S_S\)
,则溢出逻辑表达式是:

\[V=A_SB_S\overline{S_S}+\overline{A_S}\overline{B_S}S_S
\]

若V=0,表示无溢出;若V=1表示有溢出。

有两种溢出情况:输入均正输出为负;输入均负输出正。

加减运算电路设计

分析下图的运算组件。
当Sub为0时,加法器计算
\(X+Y\)

当Sub为1时,加法器计算
\(X+\overline{Y}+1\)
,相当于计算
\(X-Y\)

description

定点数的乘除运算


原码乘法
原码乘法运算分为两步:

  1. 符号
    :乘积的符号位由两个乘数的符号位“异或”得到。
  2. 数值
    :乘积的数值位是两个乘数的绝对值之积。

description

乘法运算电路

这里讨论32位无符号数乘法,其流程如下:

  1. 乘积寄存器初始置0。
  2. 将被乘数放在乘数寄存器X中,乘数存放在乘积寄存器Y中。
  3. 计数器
    \(C_n\)
    赋初值32,每循环一次减1。
  4. 对进位位C、寄存器P和寄存器Y实现同步“逻辑右移”,此时进位位C移入寄存器P的最高位,寄存器Y的最低位移出。
  5. 将乘积寄存器Y移出的最低位送至控制逻辑,以决定被乘数是否“加”到部分积上。
  6. 对乘积寄存器P和被乘数寄存器X的内容做无符号加法运算,结果送回寄存器P,进位存放在C中。
    7.. 跳转至3.

description

图:32位无符号数乘法的运算的逻辑结构图

为便于理解,以8位无符号数为例:

计算 1101 0111 * 0010 1101

1 置零Cn=0,C=0,P|Y = 0000 0000 0000 0000,X = 0000 0000
2 输入Cn=0,C=0,P|Y = 0000 0000 0010 1101,X = 1101 0111
3 初始化Cn=8,C=0,P|Y = 0000 0000 0010 1101,X = 1101 0111
4 右移P|Y = 0000 0000 0001 0110,移出1
5 移出为1,故做加法
6 P=0000 0000 X =1101 0111,结果F=1101 0111,C=0
  P|Y = 1101 0111 0001 0110
3 Cn=7
4 右移P|Y = 0110 1011 1000 1011,移出0
5 移出为0,不做加法,跳过⑥
3 Cn = 6
4 右移P|Y = 0011 0101 1100 0101,移出1
5 移出为1,做加法
6 P=0011 0101 X=1101 0111,结果F=0000 1100,C=1
  P|Y  = 0000 1100 0001 0110
3 Cn = 5
4 右移 P|Y = 1000 0110 0000 1011,移出0
...

除法运算

除法运算的过程可归纳为:

  1. 被除数与除数相减,够减则上商1,不够减则上商0。
  2. 差为中间余数,将除数右移继续比较。

description

除法运算电路

  1. 将除数存在寄存器Y中
  2. 将被除数的高32位存在寄存器R中;将被除数的低32位存在寄存器Q中
  3. 计数器
    \(C_n\)
    赋初值32,每循环一次减1
  4. ALU对寄存器R和寄存器Y内容做加/减法运算,运算结果送回寄存器R
  5. 对寄存器R、Q同步左移,Q最高位移入R的最低位,Q空出的最低位被上商
  6. 控制逻辑根据ALU运算结果的符号来决定上商是0还是1

32位int整数相除只有
\(-2^{31}/-1\)
会溢出

description

下篇预告

下篇我们将分享
整数的表示和运算

浮点数的表示和运算
两个板块的知识点。

制作不意,请帮我点一个免费的赞,谢谢!

Pinia 是 Vue 的状态管理库,它提供了一种更简单、更不规范的 API 来管理应用的状态。Pinia 的设计哲学是简单性和易用性,它避免了 Vuex 中的许多复杂概念,如 mutations 和模块的嵌套结构,提供了一种更现代、更符合 Vue 3 Composition API 风格的状态管理方式。

先来瞅一眼 Pinia 的核心组件主要包括以下几个方面:

  1. Store
    :Pinia 中的 Store 是一个保存状态和业务逻辑的实体,它不与组件树绑定,可以在整个应用中访问。Store 包含三个核心概念:state(状态)、getters(获取器)、actions(动作)。这些概念类似于组件中的 data、computed 和 methods。

  2. defineStore()
    :这是定义 Store 的函数,需要传入一个独一无二的名字作为 ID,用于连接 Store 和 devtools。Store 可以通过 Setup 函数或 Option 对象来定义。

  3. reactive
    :虽然 Pinia 本身不要求使用 Composition API,但它与 Vue 的 reactive 系统紧密集成,允许开发者以声明式的方式管理状态。

  4. Devtools 支持
    :Pinia 支持 Vue Devtools,提供了追踪 actions 和 mutations 的时间线、时间旅行和热更新等功能,方便开发者调试。

  5. 插件系统
    :Pinia 允许通过插件来扩展其功能,例如支持服务器端渲染或添加额外的中间件。

  6. TypeScript 支持
    :Pinia 提供了对 TypeScript 的良好支持,包括类型推断和自动补全功能,使得在 TypeScript 项目中使用 Pinia 更加方便 。

  7. SSR 支持
    :Pinia 支持服务端渲染,这对于构建需要 SSR 的应用是一个重要的特性。

  8. 映射辅助函数
    :Pinia 提供了如 mapStores、mapState 和 mapActions 等辅助函数,使得在组件中使用 Store 更加方便。

下面是每个部分的详细介绍。

1. Store

在 Pinia 中,
Store
是用来封装应用的状态和逻辑的核心概念。它允许你将状态和行为集中管理,而不是分散在各个组件中。
Store
可以包含以下几部分:

  • state
    :状态数据,通常是响应式的,可以在组件中被读取和修改。
  • getters
    :计算属性,用于派生状态,它们是响应式的,并且可以被缓存。
  • actions
    :可以包含任意的异步操作或同步操作,用于修改状态或执行复杂的业务逻辑。

案例实现

下面是一个创建
Store
的步骤解析,包括代码示例:

步骤 1: 定义 Store

首先,你需要从 Pinia 导入
defineStore
函数,并使用它来定义一个新的
Store

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    users: [] // 初始状态是一个空数组
  }),
  getters: {
    // 一个 getter 函数,返回数组中的用户数量
    count: (state) => state.users.length
  },
  actions: {
    // 一个 action 函数,用于添加用户
    addUser(user) {
      this.users.push(user)
    }
  }
})

步骤 2: 在组件中使用 Store

在 Vue 组件中,你可以通过调用你定义的
useUserStore
函数来使用这个
Store

<template>
  <div>
    <button @click="addNewUser">添加用户</button>
    <p>用户总数: {{ userCount }}</p>
    <ul>
      <li v-for="user in users" :key="user.id">{{ user.name }}</li>
    </ul>
  </div>
</template>

<script setup>
import { computed } from 'vue'
import useUserStore from '@/stores/user'

const store = useUserStore()
const users = computed(() => store.users)
const userCount = computed(() => store.count)

function addNewUser() {
  store.addUser({ id: Date.now(), name: '新用户' })
}
</script>

代码解析

  • 导入
    defineStore

    :从 Pinia 导入
    defineStore
    函数来定义新的
    Store
  • 定义
    useUserStore

    :创建一个名为
    useUserStore
    的函数,它返回一个
    Store
    对象。
  • state
    :在
    Store
    中定义了一个状态
    users
    ,初始为空数组。
  • getters
    :定义了一个
    count
    getter,它返回
    users
    数组的长度。
  • actions
    :定义了一个
    addUser
    action,它接受一个用户对象并将其添加到
    users
    数组中。
  • 在组件中使用
    :在组件的
    <script setup>
    块中,通过调用
    useUserStore
    来获取
    Store
    实例,并使用
    computed
    来创建响应式的
    users

    userCount
  • 添加用户
    :定义了一个
    addNewUser
    函数,当按钮被点击时,调用
    store.addUser
    来添加新用户。

以上案例展示了如何在 Pinia 中创建和管理状态,以及如何在 Vue 组件中使用
Store
。通过这种方式,你可以集中管理状态,使得状态逻辑更加清晰和可维护。

2. defineStore()

defineStore()
是 Pinia 中用于定义 Store 的函数。它允许你以声明式的方式创建一个状态管理单元,这个单元可以包含状态(state)、获取器(getters)、动作(actions)等。
defineStore()
函数接受一个唯一的 ID 和一个配置对象,配置对象中可以定义 state、getters、actions 等属性。

案例实现

下面是一个使用
defineStore()
创建 Store 的步骤解析,包括代码示例:

步骤 1: 导入 defineStore

首先,需要从 Pinia 导入
defineStore
函数。

import { defineStore } from 'pinia'

步骤 2: 定义 Store

使用
defineStore()
定义一个新的 Store,传入一个唯一的 ID 和一个配置对象。

export const useCartStore = defineStore('cart', {
  state: () => ({
    items: [] // 购物车初始状态为空数组
  }),
  getters: {
    // 计算属性,返回购物车中商品的总数量
    itemCount(state) {
      return state.items.reduce((total, item) => total + item.quantity, 0)
    }
  },
  actions: {
    // 添加商品到购物车的动作
    addItem(item) {
      const index = this.items.findIndex(i => i.id === item.id)
      if (index > -1) {
        // 如果商品已存在,则增加数量
        this.items[index].quantity += item.quantity
      } else {
        // 否则,添加新商品
        this.items.push(item)
      }
    },
    // 清空购物车的动作
    clearCart() {
      this.items = []
    }
  }
})

步骤 3: 在组件中使用 Store

在 Vue 组件中,通过调用
useCartStore
来使用这个 Store。

<template>
  <div>
    <button @click="addItem">添加商品</button>
    <button @click="clearCart">清空购物车</button>
    <p>商品总数: {{ itemCount }}</p>
    <ul>
      <li v-for="item in cartItems" :key="item.id">
        {{ item.name }} - 数量: {{ item.quantity }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { computed } from 'vue'
import useCartStore from '@/stores/cart'

const store = useCartStore()
const cartItems = computed(() => store.items)
const itemCount = computed(() => store.itemCount)

function addItem() {
  store.addItem({ id: 1, name: '商品A', quantity: 1 })
}

function clearCart() {
  store.clearCart()
}
</script>

代码解析

  • 导入 defineStore
    :从 Pinia 导入
    defineStore
    函数。
  • 定义 useCartStore
    :创建一个名为
    useCartStore
    的函数,它返回一个配置好的
    Store
    对象。
  • state
    :在
    Store
    中定义了一个状态
    items
    ,初始为空数组,用于存储购物车中的商品。
  • getters
    :定义了一个
    itemCount
    getter,它通过遍历
    items
    数组并累加每个商品的
    quantity
    来计算总数量。
  • actions
    :定义了
    addItem

    clearCart
    两个 actions。
    addItem
    用于向购物车添加商品,如果商品已存在则增加其数量;
    clearCart
    用于清空购物车。
  • 在组件中使用
    :在组件的
    <script setup>
    块中,通过调用
    useCartStore
    来获取
    Store
    实例,并使用
    computed
    来创建响应式的
    cartItems

    itemCount
  • 添加和清空商品
    :定义了
    addItem

    clearCart
    函数,分别用于添加商品和清空购物车。

使用
defineStore()
创建一个包含状态、获取器和动作的 Store,并在 Vue 组件中使用这个 Store 来管理购物车的状态。通过这种方式,你可以将状态逻辑封装在 Store 中,使得组件更加简洁和易于管理。

3. reactive

reactive()
是 Vue 3 的 Composition API 中的一个函数,它用于创建响应式的状态对象。当使用
reactive()
创建一个对象后,Vue 会追踪这个对象中属性的读取和修改,并且在数据变化时通知依赖于这些数据的组件重新渲染。

Pinia 与 Vue 的响应式系统紧密集成,
reactive()
通常在定义 Store 的状态时使用。在 Pinia 中,状态(state)是一个通过
reactive()
创建的响应式对象,因此任何对状态的修改都会自动触发与该状态相关的组件更新。

案例实现

下面是一个使用
reactive()
来创建响应式状态的步骤解析:

步骤 1: 导入 reactive

首先,需要从 Vue 导入
reactive
函数。

import { reactive } from 'vue'

步骤 2: 使用 reactive 创建状态

使用
reactive()
函数来创建一个响应式的状态对象。

const state = reactive({
  count: 0, // 初始状态
  message: 'Hello, Pinia!' // 初始消息
})

步骤 3: 在 Pinia Store 中使用 reactive 状态

在 Pinia 的
defineStore()
中,可以直接使用
reactive()
来定义状态。

import { defineStore } from 'pinia'
import { reactive } from 'vue'

export const useMyStore = defineStore('myStore', {
  state: () => reactive({
    count: 0,
    message: 'Hello, Pinia!'
  }),
  // 其他 getters 和 actions 可以在这里定义
})

步骤 4: 在组件中使用 Store

在 Vue 组件中,通过调用
useMyStore
来使用这个 Store,并访问响应式状态。

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Message: {{ message }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup>
import { computed } from 'vue'
import useMyStore from '@/stores/myStore'

const store = useMyStore()
const count = computed(() => store.state.count)
const message = computed(() => store.state.message)

function increment() {
  store.state.count++
}
</script>

代码解析

  • 导入 reactive
    :从 Vue 导入
    reactive
    函数。
  • 创建响应式状态
    :使用
    reactive()
    创建一个包含
    count

    message
    的响应式状态对象。
  • 在 Pinia Store 中使用 reactive
    :在
    defineStore()

    state
    函数中返回一个
    reactive()
    对象,这样 Pinia 就可以管理这个状态的响应性。
  • 在组件中使用
    :在组件的
    <script setup>
    块中,通过调用
    useMyStore
    来获取
    Store
    实例。使用
    computed
    来确保访问状态时保持响应性。
  • 修改状态
    :定义了一个
    increment
    函数,当按钮被点击时,直接修改
    store.state.count
    ,这会触发组件的更新。

通过这种方式,你可以确保状态的任何变化都会自动传播到使用这些状态的组件中,实现响应式的数据流,你get到了吗。

4. Devtools 支持

Vue Devtools 是一个浏览器扩展,它为开发 Vue 应用提供了强大的调试支持。对于 Pinia 来说,Devtools 支持意味着你可以在开发过程中更直观地查看和操作应用的状态。

Pinia 与 Vue Devtools 集成,提供了以下功能:

  • 状态查看
    :可以在 Devtools 中查看所有 Pinia Store 的状态。
  • 时间旅行
    :回溯状态的历史,查看状态的变化过程。
  • 动作追踪
    :记录和展示 Store 中 actions 的调用历史,包括参数和执行时间。
  • 状态修改
    :允许你在 Devtools 中直接修改状态,这些修改会实时反映到应用中。
  • 持久化状态
    :在开发过程中保持状态不变,即使页面刷新或组件重新渲染。

案例实现

要充分利用 Pinia 的 Devtools 支持,你需要确保正确安装和配置了 Vue Devtools,并且正确地在你的 Pinia Store 中编写代码。来吧,一步一步跟着做就行:

步骤 1: 安装 Vue Devtools

首先,确保你已经安装了 Vue Devtools 浏览器扩展。你可以从 Chrome Web Store 或 Firefox Add-ons 等地方安装。

步骤 2: 创建 Pinia Store

创建一个 Pinia Store,并定义 state、getters 和 actions。

import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    increment() {
      this.count++
    }
  }
})

步骤 3: 在 Vue 应用中使用 Pinia

在你的 Vue 应用中创建 Pinia 实例,并在应用启动时使用它。

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const pinia = createPinia()
const app = createApp(App)

app.use(pinia)
app.mount('#app')

步骤 4: 使用 Store

在组件中使用你的 Store,并执行一些状态更改的动作。

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup>
import { computed } from 'vue'
import useCounterStore from '@/stores/counter'

const store = useCounterStore()
const count = computed(() => store.count)

function increment() {
  store.increment()
}
</script>

步骤 5: 使用 Vue Devtools 调试

  • 打开浏览器的开发者工具。
  • 切换到 Vue Devtools 面板。
  • 在“Components”或“Store”标签页中,你应该能看到你的 Pinia Store。
  • 展开 Store,查看 state、getters 和 actions。
  • 点击 actions,你可以看到它们的调用历史和参数。
  • 直接在 Devtools 中修改 state 的值,看应用如何响应这些变化。

代码过程步骤解析

  • 安装 Devtools
    :确保浏览器扩展已安装。
  • 创建 Store
    :使用
    defineStore
    创建一个包含 state、getters 和 actions 的 Pinia Store。
  • 应用配置
    :在应用启动时创建 Pinia 实例并使用它。
  • 组件使用
    :在组件中通过调用 Store 函数来获取 Store 实例,并使用 computed 属性来保持响应性。
  • 调试
    :使用 Vue Devtools 查看和修改状态,追踪 actions。

利用 Vue Devtools 提供的功能来调试使用 Pinia 管理状态的 Vue 应用,感觉是不是挺爽。

5. 插件系统

Pinia 的插件系统允许开发者扩展 Pinia 的功能。插件可以访问 Pinia 的 Store 创建过程,可以执行以下操作:

  • 在 Store 创建之前或之后执行自定义逻辑。
  • 拦截 Store 的
    state

    getters

    actions
    等属性。
  • 向 Store 添加自定义属性或方法。
  • 处理 Store 的销毁逻辑。

Pinia 插件通常在创建 Pinia 实例时注册,然后 Pinia 会将插件应用到每个创建的 Store 上。

案例实现

下面是一个创建和使用 Pinia 插件的步骤解析,包括代码示例:

步骤 1: 定义插件

首先,定义一个插件函数,该函数接收 Pinia 的实例作为参数。

function myPiniaPlugin(pinia) {
  // 插件逻辑
}

步骤 2: 插件逻辑

在插件函数内部,可以访问 Pinia 的
store
对象,并对其进行操作。

function myPiniaPlugin(pinia) {
  pinia.use((store) => {
    // 可以在此处访问 store.state, store.getters, store.actions 等
    // 例如,向 store 添加一个新属性
    store.myCustomProperty = 'Hello from plugin!'
  })
}

步骤 3: 创建 Pinia 实例并使用插件

创建 Pinia 实例时,使用
use
方法注册插件。

import { createPinia } from 'pinia'

const pinia = createPinia().use(myPiniaPlugin)

步骤 4: 创建 Store

定义一个 Store,使用
defineStore
函数。

import { defineStore } from 'pinia'

export const useMyStore = defineStore('myStore', {
  // state, getters, actions 定义
  state: () => ({
    value: 0
  }),
  // 其他选项...
})

步骤 5: 在组件中使用 Store

在组件中使用 Store,并访问插件添加的属性。

<template>
  <div>
    <p>Value: {{ value }}</p>
    <p>Plugin Property: {{ store.myCustomProperty }}</p>
  </div>
</template>

<script setup>
import { computed } from 'vue'
import useMyStore from '@/stores/myStore'

const store = useMyStore()
const value = computed(() => store.value)
</script>

步骤 6: 组件中使用插件属性

在组件的模板或脚本中,使用插件添加到 Store 的自定义属性。

// 在模板中
<p>Plugin Property: {{ store.myCustomProperty }}</p>

// 在脚本中
console.log(store.myCustomProperty) // 输出: Hello from plugin!

代码过程步骤解析

  • 定义插件
    :创建一个函数,该函数接收 Pinia 实例并定义插件逻辑。
  • 插件逻辑
    :在插件函数内部,使用
    pinia.use
    方法注册一个回调,该回调接收每个 Store 并可以对其进行操作。
  • 注册插件
    :在创建 Pinia 实例时,通过
    .use()
    方法注册插件。
  • 创建 Store
    :使用
    defineStore
    定义 Store,包括 state、getters、actions。
  • 使用 Store
    :在组件中使用 Store,并利用 computed 属性保持响应性。
  • 访问插件属性
    :在组件的模板或脚本中,访问插件添加到 Store 的自定义属性。

记住这一点,开发者需要添加自定义逻辑和属性,通过Pinia 插件系统就 OK。

6. TypeScript 支持

Pinia 为 TypeScript 用户提供了一流的支持,确保类型安全和开发体验。Pinia 的 TypeScript 支持主要体现在以下几个方面:

  1. 类型推断
    :Pinia 利用 TypeScript 的高级类型系统来推断 Store 中的状态、getters 和 actions 的类型。
  2. 类型声明
    :Pinia 提供了类型声明文件(
    .d.ts
    ),确保 Pinia API 在 TypeScript 项目中的类型正确性。
  3. 自动补全
    :在 TypeScript 环境中,编辑器可以提供自动补全功能,帮助开发者快速编写代码。
  4. 类型守卫
    :Pinia 与 TypeScript 配合使用时,可以利用类型守卫来确保对 Store 属性的访问是安全的。

案例实现

下面是一个使用 TypeScript 与 Pinia 结合使用的步骤解析,包括代码示例:

步骤 1: 设置 TypeScript 环境

确保你的项目已经配置了 TypeScript,并且安装了必要的类型声明文件。

npm install typescript @vue/compiler-sfc

步骤 2: 定义 Store 使用 TypeScript

使用 TypeScript 的类型定义来创建 Pinia Store。

import { defineStore } from 'pinia'

interface State {
  count: number
  message: string
}

export const useMyStore = defineStore('myStore', {
  state: (): State => ({
    count: 0,
    message: 'Hello, Pinia with TypeScript!'
  }),
  getters: {
    // 使用 TypeScript 来声明 getter 的返回类型
    doubleCount: (state): number => state.count * 2
  },
  actions: {
    increment(): void {
      this.count++
    }
  }
})

步骤 3: 在组件中使用 Store

在 Vue 组件中使用 Store,并利用 TypeScript 提供类型安全。

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Message: {{ message }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import useMyStore from '@/stores/myStore'

const store = useMyStore()
const count = computed(() => store.count)
const message = computed(() => store.message)
const doubleCount = computed(() => store.doubleCount)

function increment() {
  store.increment()
}
</script>

步骤 4: 利用类型守卫

使用类型守卫来确保对 Store 属性的访问是安全的。

if (store.hasOwnProperty('count')) {
  // TypeScript 知道 'count' 存在且为 number 类型
  console.log(store.count)
}

代码过程步骤解析

  • 配置 TypeScript
    :确保项目中安装了 TypeScript 及其类型声明文件。
  • 定义 Store 类型
    :使用 TypeScript 的接口(
    interface
    )来定义 Store 的状态类型。
  • 创建 Store
    :使用
    defineStore
    并传入类型化的 state 函数,以及声明了返回类型的 getters 和 actions。
  • 组件中使用 Store
    :在组件中使用 TypeScript 来确保访问 Store 属性的类型安全。
  • 类型守卫
    :使用 TypeScript 的
    hasOwnProperty
    方法和类型守卫来安全地访问 Store 的属性。

小结一下,Pinia 与 TypeScript 结合使用可以提供类型安全的状态管理,同时编辑器的自动补全和类型检查功能可以提高开发效率和减少错误。

7. SSR 支持

服务器端渲染(SSR)是一种将网站页面在服务器上生成并发送给客户端的技术。对于状态管理库来说,SSR 支持意味着可以在服务器上初始化和操作状态,然后将状态序列化后发送到客户端,客户端再将这些状态恢复以保持与服务器端相同的状态。

Pinia 对 SSR 的支持主要体现在以下几个方面:

  1. 状态序列化
    :Pinia 允许将状态(state)序列化成 JSON 字符串,以便在服务器和客户端之间传输。
  2. 状态恢复
    :客户端可以接收服务器端的状态,并将其恢复到对应的 Store 中。
  3. 无状态的 Store 定义
    :Pinia 的 Store 定义是无状态的,这意味着在服务器端和客户端端都可以重新创建 Store。
  4. 与 Vue SSR 的集成
    :Pinia 可以与 Vue 的 SSR 系统集成,确保在服务器端预渲染组件时使用正确的状态。

案例实现

下面是一个使用 Pinia 进行 SSR 的步骤解析,包括代码示例:

步骤 1: 定义 Store

首先,定义一个 Pinia Store。

import { defineStore } from 'pinia'

export const useMyStore = defineStore('myStore', {
  state: () => ({
    count: 0
  }),
  // 其他选项...
})

步骤 2: 在服务器端创建和序列化状态

在服务器端,创建 Store 实例并初始化状态,然后将状态序列化。

// server.js
import { createPinia } from 'pinia'
import useMyStore from '@/stores/myStore'

const pinia = createPinia()

// 模拟从数据库获取初始状态
const initialState = { count: 10 }

// 创建 Store 实例并设置初始状态
const store = useMyStore(pinia)
store.$state.count = initialState.count

// 序列化状态
const stateToTransfer = JSON.stringify(store.$state)

步骤 3: 在客户端恢复状态

在客户端,接收服务器端发送的状态,然后恢复到对应的 Store 中。

// client.js
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue' // Vue 应用的根组件
import useMyStore from '@/stores/myStore'

const pinia = createPinia()

// 假设从服务器接收的状态如下
const stateFromServer = JSON.parse(/* state serialized from server */)

// 创建 Store 实例并恢复状态
const store = useMyStore(pinia)
store.$state.count = stateFromServer.count

const app = createApp(App)
app.use(pinia)
app.mount('#app')

步骤 4: 在 Vue 组件中使用 Store

在 Vue 组件中,像平常一样使用 Store。

<template>
  <div>{{ count }}</div>
</template>

<script setup>
import { computed } from 'vue'
import useMyStore from '@/stores/myStore'

const store = useMyStore()
const count = computed(() => store.count)
</script>

代码过程步骤解析

  • 定义 Store
    :使用
    defineStore
    定义一个 Pinia Store。
  • 服务器端创建和序列化
    :在服务器端,创建 Store 实例,设置初始状态,并使用
    JSON.stringify
    序列化状态。
  • 客户端恢复状态
    :在客户端,解析服务器端发送的状态字符串,并恢复到 Store 的
    $state
    中。
  • 组件中使用 Store
    :在 Vue 组件中,通过调用 Store 函数获取 Store 实例,并使用 computed 属性来访问状态。

一句话,Pinia支持SSR有助于提高应用的初始加载性能和SEO优化。

8. 映射辅助函数

在 Pinia 中,映射辅助函数用于将 Store 中的状态(state)、获取器(getters)、动作(actions)映射到组件的计算属性(computed properties)、方法(methods)或响应式属性上。这些辅助函数提供了一种便捷的方式来使用 Store,而无需在每个组件中重复编写相同的代码。

Pinia 的映射辅助函数主要包括:

  • mapState
    :将 Store 中的状态映射为组件的计算属性。
  • mapGetters
    :将 Store 中的获取器映射为组件的计算属性。
  • mapActions
    :将 Store 中的动作映射为组件的方法。

案例实现

下面是一个使用 Pinia 映射辅助函数的步骤解析,包括代码示例:

步骤 1: 定义 Store

首先,定义一个 Pinia Store。

import { defineStore } from 'pinia'

export const useCartStore = defineStore('cart', {
  state: () => ({
    items: []
  }),
  getters: {
    itemCount: (state) => state.items.length
  },
  actions: {
    addItem(item) {
      this.items.push(item)
    }
  }
})

步骤 2: 使用映射辅助函数

在组件中,使用
mapState

mapGetters

mapActions
将 Store 的属性映射到组件上。

<template>
  <div>
    <p>Item Count: {{ itemCount }}</p>
    <button @click="addItem({ id: 1, name: 'Apple' })">Add Apple</button>
  </div>
</template>

<script setup>
import { mapState, mapGetters, mapActions } from 'pinia'
import useCartStore from '@/stores/cart'

const store = useCartStore()

// 使用 mapState 映射状态
const items = mapState(store, 'items')

// 使用 mapGetters 映射获取器
const itemCount = mapGetters(store, 'itemCount')

// 使用 mapActions 映射动作
const { addItem } = mapActions(store, ['addItem'])
</script>

步骤 3: 在模板中使用映射的属性

在组件的模板中,直接使用映射的计算属性和方法。

<p>Item Count: {{ itemCount }}</p>
<button @click="addItem({ id: 1, name: 'Apple' })">Add Apple</button>

代码过程步骤解析

  • 定义 Store
    :使用
    defineStore
    定义一个包含状态、获取器和动作的 Pinia Store。
  • 使用映射辅助函数

    • mapState
      :将
      items
      状态映射为组件的计算属性。
    • mapGetters
      :将
      itemCount
      获取器映射为组件的计算属性。
    • mapActions
      :将
      addItem
      动作映射为组件的方法。
  • 模板中使用
    :在模板中,使用映射的计算属性
    itemCount
    来显示项目数量,使用映射的方法
    addItem
    来添加新项目。

使用映射辅助函数,Pinia 可以让开发者以声明式的方式在组件中使用 Store 的状态和行为,从而减少样板代码并提高组件的可读性和可维护性。此外,这些辅助函数还有助于保持响应性,确保当 Store 中的状态变化时,组件能够自动更新。

最后

不得不说,Pinia 提供了一种灵活且高效的方式来管理 Vue 应用的状态,无论是在单页应用还是服务器端渲染的场景下,都有出色的表现,写完收工,欢迎关注威哥爱编程,一起走全栈之路。

在Python开发中,日期和时间处理是一个常见的需求。Python提供了多种模块和方法来处理日期和时间,以下是一些常用的模块和操作。通过介绍一些系统的Python类库以及第三方的类库,我们可以快速的实现各种时间日期历法节气等相关信息的处理。

1、系统内置日期时间模块

1)
datetime
模块

datetime
是Python中处理日期和时间的主要模块。它提供了多个类,如
datetime
,
date
,
time
,
timedelta
, 和
tzinfo

from datetime importdatetime

now
=datetime.now()print(now) #当前日期和时间

获取当前日期

today = datetime.today().date() print(today) #只包含日期部分

日期和时间的格式化

formatted = now.strftime("%Y-%m-%d %H:%M:%S")print(formatted)  #以指定格式输出

解析字符串为日期

date_str = "2024-08-23 10:15:00"date_obj= datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")print(date_obj)

2)
time
模块

time
模块主要用于处理时间戳和延迟操作。

获取当前时间戳

importtime

timestamp
=time.time()print(timestamp) #当前时间戳

延迟执行

time.sleep(5)  #程序暂停5秒

3)
calendar
模块

calendar
模块提供了与日历相关的功能,比如生成日历、判断闰年等。

生成某个月的日历

importcalendar

year
= 2024month= 8 print(calendar.month(year, month))

判断是否为闰年

is_leap = calendar.isleap(2024)print(is_leap)  #输出: True

让我们来看看我汇总的一些测试例子,以及它们的输出

import time  #引入time模块
import calendar  #引入calendar模块
from datetime import datetime  #引入datetime模块
ticks=time.time()print("当前时间戳为:", ticks)#Python函数用一个元组装起来的9组数字处理时间:
localtime =time.localtime(time.time())print("本地时间为 :", localtime)#格式化日期:
localtime =time.asctime(time.localtime(time.time()))print("本地时间为 :", localtime)#格式化日期:
localtime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())print("本地时间为 :", localtime)print(time.strftime("%b %B", time.localtime()))print("判断是否为闰年:{0}".format(calendar.isleap(2000)))  #判断是否为闰年
print("计算2000年到2020年有多少个闰年:{0}".format(calendar.leapdays(2000, 2020))
)
#计算2000年到2020年有多少个闰年

我们还可以根据需要组合一些函数来实现更加强大的日期处理。

#中文月份和星期字符串
ch_month_names =["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月",
]
ch_week_names
= ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]defchinese_calendar(year, month):
cal
=calendar.month(year, month)
lines
= cal.split("\n")
ch_lines
=[ch_week_names[:]]for line in lines[2:]: #跳过前两行非日期信息 ch_lines.append([i if i != " " else "" for i inline.split()])return ch_month_names[month - 1], ch_linesprint()#获取当前年月 now =datetime.now()
year, month
=now.year, now.month
weekname
= ch_week_names[now.weekday()]
#打印当前月份的中文日历
month_name, ch_calendar =chinese_calendar(year, month)print(f"{year}年{month_name}, {weekname}\n")

输出结果

2024年八月, 星期五

组合一些扩展的函数处理,我们还可以进行农历年、节气的处理,如下所示。

今天是2024年8月23日 星期五 农历甲辰[龙]年七月廿十 今日节气:#处暑#

2、第三方日期时间处理库

Python第三方库非常多也非常方便,我们可以根据自己需要选择一些比较优秀的第三方库来处理日期和时间的问题。

pytz
是一个第三方库,用于处理时区转换。

from datetime importdatetimeimportpytz

tz
= pytz.timezone("Asia/Shanghai")
now_shanghai
=datetime.now(tz)print(now_shanghai) #输出当前时间,带时区信息 #输出带时区的时间 print(now_shanghai.strftime("%Y-%m-%d %H:%M:%S %Z%z"))#输出不带时区的时间 print(now_shanghai.astimezone(pytz.utc).strftime("%Y-%m-%d %H:%M:%S"))

输出信息

2024-08-23 10:52:56.794000+08:00
2024-08-23 10:52:56 CST+0800
2024-08-23 02:52:56

dateutil
是另一个功能强大的第三方库,提供了日期解析、相对时间计算等高级功能。

解析日期字符串

from dateutil importparser

date
= parser.parse("August 23, 2024 10:15 AM")print(date)

处理相对时间

from dateutil.relativedelta importrelativedelta

next_month
= now + relativedelta(months=+1)print(next_month) #当前时间加一个月

输出信息如下所示。

2024-08-23 10:15:00
2024-09-23 10:51:47.368256

在Python中,还有一些类似于JavaScript的
moment.js

day.js
的第三方库,用于简化日期和时间的处理。以下是几个常用的库:

.
Pendulum

Pendulum
是一个更高级的
datetime
替代品,具有更直观和人性化的API,同时内置时区处理和格式化功能。

importpendulum#获取当前时间
now =pendulum.now()print(now)  #带有时区信息

#创建特定日期时间
specific_date = pendulum.datetime(2024, 8, 23, 10, 15)print(specific_date)#时间差的表示
diff =specific_date.diff(now)print(diff.in_days())  #输出差异的天数

#格式化日期
formatted =now.to_formatted_date_string()print(formatted)  #输出: Aug 23, 2024

Arrow

Arrow
是另一个灵活的日期处理库,提供了更加简洁的API来处理常见的日期和时间操作。

importarrow#获取当前时间
now =arrow.now()print(now)#解析日期字符串
date = arrow.get("2024-08-23 10:15:00", "YYYY-MM-DD HH:mm:ss")print(date)#格式化日期
formatted = date.format("YYYY-MM-DD HH:mm:ss")print(formatted)#时区转换
utc =arrow.utcnow()
local
= utc.to("America/New_York")print(local)

Maya

Maya
是一个简单而强大的库,旨在简化日期和时间的使用,尤其是在处理相对时间和自然语言输入时。

importmaya#获取当前时间
now =maya.now()print(now)#解析自然语言日期
date = maya.when("next friday at 5pm")print(date)#转换为其他格式
iso_format =date.iso8601()print(iso_format)

Delorean

Delorean
是一个用于日期和时间的高级库,它整合了
Arrow

pytz

dateutil
等多个库的功能,并提供了简化的API。

importdelorean#获取当前时间
now =delorean.now()print(now)#时区转换
utc = now.shift("UTC")print(utc)#日期操作
future = now.shift(days=10)print(future)

Moment.py

Moment.py
是一个模仿JavaScript的
moment.js
的库,提供类似的API来处理日期和时间。

mport moment#获取当前时间
now =moment.now()print(now)#解析字符串
date = moment.date("2024-08-23 10:15:00")print(date)#格式化日期
formatted = now.format("YYYY-MM-DD HH:mm:ss")print(formatted)#相对时间
relative = now.add(days=5)print(relative.format("YYYY-MM-DD HH:mm:ss"))

这些库如
Pendulum
,
Arrow
,
Maya
,
Delorean
, 和
Moment.py
都提供了更强大和更直观的API来处理日期和时间。选择哪个库取决于你的具体需求和偏好。
Pendulum

Arrow
是目前最流行的选择,提供了广泛的功能和简单易用的接口。

3、中文日历的处理

在处理中文日历和与中国文化相关的日期时,Python也提供了一些有用的库和方法。特别是对于农历、节气、中文日期格式等需求,以下是一些有用的工具和技巧:

1)
lunarcalendar

lunarcalendar
是一个用于处理中国农历(阴历)的Python库,它可以将公历转换为农历,并处理农历的各个方面。

from lunarcalendar importConverter, Solar, Lunar#公历转农历
solar = Solar(2024, 8, 23)
lunar
=Converter.Solar2Lunar(solar)print(lunar) #输出农历日期 #农历转公历 lunar = Lunar(2024, 7, 18)
solar
=Converter.Lunar2Solar(lunar)print(solar) #输出公历日期 #输出农历日期 ld =Lunar.from_date(datetime.now())print(ld)
Lunar(year=2024, month=7, day=20, isleap=False)
Solar(year
=2024, month=8, day=21)
Lunar(year
=2024, month=7, day=20, isleap=False)

2)
chinese-calendar

chinese-calendar
是另一个处理中国农历和节日的库,它提供了判断中国法定节假日、节气等功能。

importchinese_calendar as calendarimportdatetime#检查某天是否为节假日
today = datetime.date(2024, 8, 22)

is_holiday
=calendar.is_holiday(today)print(is_holiday) #返回 True 或 False on_holiday, holiday_name=calendar.get_holiday_detail(today)print(on_holiday, holiday_name) #返回 True 或 False,节假日名称 #获取某天的节气 end_of_day = datetime.date(2024, 8, 31)
term
=calendar.get_solar_terms(today, end_of_day)print(term) #输出节气名称,如"处暑"

3)
lunar_python

unar是一款无第三方依赖的公历(阳历)、农历(阴历、老黄历)、佛历和道历工具,支持星座、儒略日、干支、生肖、节气、节日、彭祖百忌、吉神(喜神/福神/财神/阳贵神/阴贵神)方位、胎神方位、冲煞、纳音、星宿、八字、五行、十神、建除十二值星、青龙名堂等十二神、黄道日及吉凶、法定节假日及调休等。

安装

pip install lunar_python

使用代码

from lunar_python importLunar#通过指定年月日初始化阴历
lunar = Lunar.fromYmd(2024, 7, 19)#打印阴历
print(lunar.toFullString())#阴历转阳历并打印
print(lunar.getSolar().toFullString())

输出内容

二〇二四年七月二十 甲辰(龙)年 壬申(猴)月 己未(羊)日 子(鼠)时 纳音[覆灯火 剑锋金 天上火 海中金] 星期
五 东方青龙 星宿[亢金龙](凶) 彭祖百忌[己不破券二比并亡 未不服药毒气入肠] 喜神方位[艮](东北) 阳贵神方位[坎](正北) 阴贵神方位[坤](西南) 福神方位[坎](正北) 财神方位[坎](正北) 冲[(癸丑)牛] 煞[西]
2024-08-23 00:00:00 闰年 星期五 处女座

4)
cnlunar

还有一个也比较强大的中国日历,历法,节气等信息的结合体类库,可以通过它获得更多的相关信息。

安装:

pip install cnlunar
#获取农历日期
dt_date = datetime.datetime(2024, 8, 22)
a
= cnlunar.Lunar(dt_date, godType="8char")

dic
={"日期": a.date,"农历数字": (
a.lunarYear,
a.lunarMonth,
a.lunarDay,
"" if a.isLunarLeapMonth else "",
),
"农历": "%s %s[%s]年 %s%s" %(a.lunarYearCn, a.year8Char, a.chineseYearZodiac, a.lunarMonthCn, a.lunarDayCn),"星期": a.weekDayCn,#未增加除夕 "今日节日": (
a.get_legalHolidays(),
a.get_otherHolidays(),
a.get_otherLunarHolidays(),
),
"八字": " ".join([a.year8Char, a.month8Char, a.day8Char, a.twohour8Char]),"今日节气": a.todaySolarTerms,"下一节气": (a.nextSolarTerm, a.nextSolarTermDate, a.nextSolarTermYear),"今年节气表": a.thisYearSolarTermsDic,"季节": a.lunarSeason,"今日时辰": a.twohour8CharList,"时辰凶吉": a.get_twohourLuckyList(),"生肖冲煞": a.chineseZodiacClash,"星座": a.starZodiac,"星次": a.todayEastZodiac,"彭祖百忌": a.get_pengTaboo(),"彭祖百忌精简": a.get_pengTaboo(long=4, delimit="<br>"),"十二神": a.get_today12DayOfficer(),"廿八宿": a.get_the28Stars(),"今日三合": a.zodiacMark3List,"今日六合": a.zodiacMark6,"今日五行": a.get_today5Elements(),"纳音": a.get_nayin(),"九宫飞星": a.get_the9FlyStar(),"吉神方位": a.get_luckyGodsDirection(),"今日胎神": a.get_fetalGod(),"神煞宜忌": a.angelDemon,"今日吉神": a.goodGodName,"今日凶煞": a.badGodName,"宜忌等第": a.todayLevelName,"": a.goodThing,"": a.badThing,"时辰经络": a.meridians,
}
for i indic:
midstr
= "\t" * (2 - len(i) // 2) + ":" + "\t" print(i, midstr, dic[i])

输出详细结果如下所示。

以上即是一些系统的Python类库以及第三方的类库,通过它们,我们可以快速的实现各种时间日期历法节气等相关信息的处理。

傅里叶变换

对于周期信号,如果满足
\(Dirichlet\)
条件,就可以尝试将其分解为傅里叶级数,并绘制成频谱的形式,但是在实际使用的过程中我们遇到的信号往往既不是周期的信号又难以获取解析式。对于复杂的现实信号,我们可以将问题的难点拆分开,我们先解决不是周期信号但解析式已知的情况,再去解决难以获取解析式的情况。

从周期信号到非周期信号

首先我们先看一个问题:
对于周期矩形脉冲信号,周期不断变大过程中,频谱如何发生变化?
经过实验我们得到下面的一组图:
周期信号周期变化后频谱变化规律
我们可以观察到,随着周期的不断变大,频谱的谱线之间距离变得越来越近。

上面的分析结果虽然是针对于周期矩形脉冲信号而言的,但是其具有一定的普适性,对于其他周期信号而言也能得到类似的结论。由此得到一个简单的想法————
非周期信号可以视作信号的周期为
\(\infty\)
的周期信号

。另外,我们知道信号的能量是有限值,对于周期信号,由帕斯瓦尔定理,我们知道幅度谱线的纵坐标的数值是与能量相关的参数,在改变信号周期的时候我们并未对其中的能量进行改变,也就是总的面积不变。基于这样的考虑,我们引入一个函数 "频谱密度函数",函数中的每一个值都是在一个频率点附近能量和频率的比值,类似于概率论中概率密度函数中离散和连续的情况。

接下来就按照上述思想进行公式推导,看能否得到什么?
指数形式的傅里叶级数:

\[f(t) = \sum_{n = -\infty}^{\infty} F(n \omega )e^{j n \omega t} \\
\]

频谱为:

\[F(n \omega) = \frac{1}{T} \int_{-\frac{T}{2}}^{\frac{T}{2}} f(t) e^{{-j n \omega t}} \text{{d}t }\\
\]

两边同时乘上周期或者说是除以频率:

\[F(n \omega) T= \frac{2\pi}{\omega} F(n \omega) = \int_{-\frac{T}{2}}^{\frac{T}{2}} f(t) e^{{-j n \omega t}} \text{{d}t }\\
\]

当周期
\(T\to \infty\)
,有
\(\omega \to 0\)
,同时还有
\(n \to 1\)
。定义函数:

\[F(\omega) = \lim_{ \omega \to 0 } \frac{2\pi}{\omega} F(n \omega) = \lim_{ T \to \infty } F(n \omega) T
\]

式子中
\(\displaystyle{\frac{2\pi F(n \omega)}{\omega}}\)
就是频谱密度,称
\(F(\omega)\)
为频谱密度函数,简称频谱函数。

由此我们便能够得到下面这样一组式子:

\[F(\omega) = \int_{-\infty}^{\infty} f(t) e^{-j \omega t}\text{d}x
\]

\[f(t) = \frac{1}{2\pi} \int _{-\infty}^{\infty} F(\omega) e^{j \omega t} \, \text{d}x
\]

上面的两个式子便是傅里叶变换公式和傅里叶反变换公式,并且称上面的
\(f(t)\)

\(F(\omega)\)
为一个傅里叶变换对,简记为
\(f(t)\leftrightarrow F(\omega)\)
.

其中的
\(F(\omega)\)
是个复数,将他的模值
\(|F(\omega) |\)
作为幅度谱,相位作为相位谱。于是就得到了非周期信号的两谱图。

与周期信号离散的谱图类似,非周期信号的连续谱图中,幅度谱为偶函数,相位谱图为奇函数。

上面的推理过程在数学上并不是严格的,理论上傅里叶变化也不是任何函数都能进行变换,需要满足条件:

\[\int _{-\infty}^{\infty} | f(t)|\, \text{d}x < \infty
\]

但是对于大多数信号都是满足上面的关系的,并且傅里叶变换是个很好的工具,对于某些奇异函数也是适用的(如冲激信号和阶跃信号等)。

典型非周期信号的傅里叶变换

(一)单边指数信号

原信号:

\[f(t) =
\begin{cases}
e^{-at} \qquad &(t \geqslant 0)\\ \\
0 \qquad &(t < 0)\\
\end{cases}
\]

傅里叶变换后的式子:

\[\begin{cases}
F(\omega) = \displaystyle\frac{1}{a+j \omega}\\ \\
| F(\omega) | = \displaystyle{\frac{1}{\sqrt{a^{2} + \omega^{2}}}}\\ \\
\varphi(\omega) = -\arctan\left( \displaystyle\frac{\omega}{a} \right)\\
\end{cases}
\]

(二)双边指数信号

双边指数信号:

\[f(t) = e^{-a | t |} \qquad (-\infty < t<\infty)
\]

双边指数信号的傅里叶变换:

\[\begin{cases}
F(\omega) = \displaystyle\frac{2a}{a^{2}+\omega^{2}}\\ \\
| F(\omega) | = \displaystyle{\frac{2a}{{a^{2} + \omega^{2}}}}\\ \\
\varphi(\omega) = 0\\
\end{cases}
\]

(三)矩形脉冲信号

矩形脉冲信号:

\[f(t) = E\left[u\left( t - \displaystyle{\frac{\tau}{2}} \right) - u\left( t - \displaystyle{\frac{\tau}{2}} \right) \right]
\]

矩形脉冲信号的傅里叶变换:

\[\begin{cases}
F(\omega) = E\tau \cdot\text{Sa}\left( \displaystyle{\frac{\omega\tau}{2}} \right)\\ \\
|F(\omega)| = E\tau \cdot \left|\text{Sa}\left( \displaystyle{\frac{\omega\tau}{2}} \right)\right|\\ \\
\varphi(\omega) = \begin{cases}0\\
\pi\\ \end{cases}
\end{cases}
\]

(四)钟形脉冲信号

钟形信号:

\[f(t) = Ee^{- (\frac {t}{\tau})^{2}}
\]

钟形信号的傅里叶变换:

\[F(\omega) =\sqrt{ \pi } E\tau \cdot e^{-(\frac{\omega \tau} {2})^{2}}
\]

依旧是钟形的

(五)符号函数

符号函数:

\[f(t) = \text{sgn}(t)
\]

符号函数本身不满足绝对可积分,但是可以对他进行傅里叶变换

不妨设想双边奇对称指数信号,如果
\(a\to 0\)
的话就非常的接近符号函数,对他进行傅里叶变换,就可以得到

符号函数的傅里叶变换:

\[\begin{cases}
F(\omega) & = \displaystyle \frac{2}{j \omega}\\ \\
| F(\omega) | & = \displaystyle{ \frac{2}{| \omega |}}\\ \\
\varphi (\omega) & = \begin{cases}
-\displaystyle \frac{\pi}{2}\quad & & (\omega > 0 )\\ \\
\displaystyle \frac{\pi}{2} \quad & & (\omega <0)\\
\end{cases}
\end{cases}
\]

(六)升余弦脉冲信号

升余弦脉冲信号:

\[f(t) = \frac{E}{2} \left[ 1 + \cos \left( \frac{\pi t}{\tau} \right) \right] \quad (0 \leqslant |t| \leqslant \tau)
\]

就是把余弦信号向上平移半个峰峰值,第一个凸起的信号

升余弦信号的傅里叶变换

\[\begin{aligned}
F(\omega) & = \frac {E\sin(\omega \tau)}{\omega \left[ 1 - \left(\frac{\omega \tau}{\pi}^{2}\right) \right]}\\ \\
& = \frac{E\tau\text{Sa}(\omega t)}{1 - \left(\frac{\omega \tau }{\pi}\right)^{2}}
\end{aligned}
\]

前面我们计算了一些常见的傅里叶变换,但是这样的计算更多是为了便于读者熟悉傅里叶变换这样的工具。当读者熟悉傅里叶变换的相关性质,上面的部分信号有更加简便的求解方式。下面我们继续学习一些奇异信号的傅里叶变换。

奇异信号的傅里叶变换

(一)冲激函数的傅里叶变换

冲激函数的傅里叶变换为:

\[\mathscr{F} \Big[\delta(t)\Big] = 1
\]

单位冲激函数的频谱为常数,或者说是“白色谱”。

冲激函数的傅里叶逆变换(从冲激函数的傅里叶变化可以推出):

\[\mathscr{F}^{-1} \Big[ \delta(\omega)\Big]= \frac{1}{2\pi}
\]

切记特殊信号的傅里叶逆变换直接进行积分运算可能较为复杂,如果不熟悉复变函数的积分会得到错误结论。
比如下面的。

\[\mathscr{F}(1) = 2\pi \delta(\omega)
\]

直流信号的傅里叶变换是零点处的冲激信号。

(二)冲击偶函数的傅里叶变换

冲击偶函数的傅里叶变换可以通过下面方法得到:

\[\begin{aligned}
& \delta(t) = \frac{1}{2\pi} \int _{-\infty}^{\infty} e^{j \omega t} \, \text{d}\omega \\ \\
& \quad (\text{对两边求导})\\ \\
& \frac{\text{d}}{\text{d}t} \delta(t) = \frac{1}{2\pi} \int _{-\infty}^{\infty} j \omega e^{j \omega t} \, \text{d}\omega
\end{aligned}
\]

由此可以知道冲激函数的傅里叶变换为:

\[\mathscr{F} \Big[ \frac{\text{d}}{\text{d}t} \delta(t) \Big] = j \omega
\]

如果直接采用傅里叶变换的定义和冲击偶函数的性质也可以得到上面的结论。

同理可以知道:

\[\mathscr{F} \Big[ \frac{\text{d}^{n}}{\text{d}t^{n}} \delta(t) \Big] = (j \omega )^{n}
\]

\[\mathscr{F} \Big[ t^{n} \Big] = 2\pi (j)^{n} \frac{\text{d}^{n}}{\text{d}\omega^{n}} \delta(\omega )
\]

(三)阶跃函数的傅里叶变换

\[\mathscr{F}\Big[u(t)\Big]= \pi \delta(\omega) + \frac{1}{j\omega}
\]

傅里叶变换的性质

傅里叶变换的性质能够极大的简化某些复杂信号的计算过程,因此势必要仔细理解每一条性质并且活学活用。

(一)对称性


\(F(\omega) = \mathscr{F}\Big[ f(t)\Big]\)
,则
\(\mathscr{F}\Big[ F(t)\Big] = 2\pi f(-\omega)\)
.

如果知道
\(f(t)\)
,我们需要求他的傅里叶变换后图形的傅里叶变换,则可以通过求解
\(2\pi f(-\omega)\)
得到。

(二)叠加性

先叠加再傅里叶变换等于先傅里叶变换再叠加。也就是傅里叶变换满足线性变换的性质。

(三)奇偶虚实性

1、
\(f(t)\)
是实函数

实函数的傅里叶变换的实部为偶函数,虚部为奇函数,幅度谱为偶函数,相位谱为奇函数。

\[\begin{cases}
R(\omega) = R(-\omega)\\ \\
X(\omega) = -X(-\omega)\\ \\
F(-\omega) = F^{*}(\omega)\\
\end{cases}
\]

推论:实偶函数的傅里叶变换是实偶函数,实奇函数的傅里叶变换是虚奇函数。

2、
\(f(t)\)
为虚函数

虚函数的傅里叶变换的实部为奇函数,虚部为偶函数。

\[\begin{cases}
R(\omega) = R(-\omega)\\ \\
X(\omega) = -X(-\omega)\\ \\
F(-\omega) = F^{*}(\omega)\\
\end{cases}
\]

3、
\(f(t)\)
为实函数或者复函数

\[\begin{cases}
\mathscr{F}\Big[ f(-t)\Big] = F(-\omega)\\ \\
\mathscr{F}\Big[ f^{*}(t)\Big] = F^{*}(-\omega)\\ \\
\mathscr{F}\Big[ f^{*}(-t)\Big] = F^{*}(\omega)\\
\end{cases}
\]

(四)尺度变换特性


\(\mathscr{F}\Big[ f(t)\Big] = F(\omega)\)
, 则

\[\mathscr{F}\Big[ f(at)\Big] = \frac{1}{|a|}F\left( \frac{\omega}{a} \right)
\]

时域上信号的压缩,等于频域上信号的扩展;
时域上信号的扩展,等于频域上信号的压缩。

关于
\(\displaystyle \frac{1}{|a|}\)
的物理含义:
时域上的压缩,信号能量减少,因此需要乘上
\(\displaystyle \frac{1}{|a|}, \quad(a>1)\)

时域上的扩展,信号能量变多,因此需要乘上
\(\displaystyle \frac{1}{|a|}, \quad(a<1)\)
.

信号的等效脉宽与占有的等效带宽成反比例,要想获得更快的传输密度,需要压缩脉宽,同时需要占用更大的频带,是对频带资源的浪费。

(五)时移特性


\(\mathscr{F}\Big[ f(t)\Big] = F(\omega)\)
, 则

\[\mathscr{F}\Big[ f(t - t_{0})\Big] = e^{-j \omega t_{0}}F\left( \omega \right)
\]

时间上的移动会导致频带上幅度谱的谱值不变,并且带来相位上
\(-\omega t_{0}\)
. 从这里可以知道时间上的滞后不会影响信号的能量.

(六)频移特性


\(\mathscr{F}\Big[ f(t)\Big] = F(\omega)\)
, 则

\[\mathscr{F}\left[ f(t) e^{j\omega_{0} t}\right] = F\left( \omega - \omega_{0}\right)
\]

频移技术:将原信号乘上一个正弦、余弦或者正余弦混合信号,等效于乘上
\(e^{j \omega_{0} t}\)
随后就将信号的频谱进行了移动,使其移动到合适的未被占用的频带。这项技术广泛应用于通信中。

(七)微分特性


\(\mathscr{F}\Big[ f(t)\Big] = F(\omega)\)
, 则

\[\mathscr{F}\left[ \frac{\text{d}^{n}f(t)}{\text{d}t^{n}} \right] = (j\omega)^{n} F\left( \omega \right)
\]


\(\mathscr{F}\Big[ F(t)\Big] = F(\omega)\)
, 则

\[\mathscr{F}^{-1} \left[ \frac{\text{d}^{n}F(\omega)}{\text{d}\omega^{n}} \right] = (-jt)^{n} f(t)
\]

(八)积分特性


\(\mathscr{F}\Big[ f(t)\Big] = F(\omega)\)
, 则

\[\mathscr{F}\left[ \int_{-\infty}^{t} f(\tau) \text{d} \tau \right ] = \frac{F\left( \omega \right) }{j \omega} + \pi F(0) \delta(\omega)
\]


\(\mathscr{F}\Big[ F(t)\Big] = F(\omega)\)
, 则

\[\mathscr{F}^{-1} \left[ \int_{-\infty}^{\omega} F(\Omega) \text{d} \Omega \right] = - \frac{f(t)}{jt} + \pi f(0) \delta(t)
\]

(九)卷积性质

1、时域卷积定理

给定两个时间函数
\(f_1(t)\)

\(f_2(t)\)
已知:

\[\begin{aligned}
\mathscr{F}[f_{1}(t)] & = F_{1}(\omega)\\
\mathscr{F}[f_{2}(t)] & = F_{2}(\omega)
\end{aligned}
\]

则:

\[\mathscr{F}[f_{1}(t) * f_{2}(t)] = F_{1}(\omega) F_{2}(\omega)
\]

两函数的卷积的傅里叶变换等于两函数傅里叶变换后的乘积。

2、频域卷积定理

给定两个时间函数
\(f_1(t)\)

\(f_2(t)\)
已知:

\[\begin{aligned}
\mathscr{F}[f_{1}(t)] & = F_{1}(\omega)\\
\mathscr{F}[f_{2}(t)] & = F_{2}(\omega)
\end{aligned}
\]

则:

\[\mathscr{F}[f_{1}(t) \cdot f_{2}(t)] = \frac{1}{2\pi}F_{1}(\omega) * F_{2}(\omega)
\]

两函数的傅里叶变换在频域上的卷积再乘上
\(\displaystyle{\frac{1}{2\pi}}\)
后的结果与两函数相乘后在进行傅里叶变换相同。

频域卷积定理主要用在通信系统的调制与解调。

结语

傅里叶变换是信号分析和通信的基础,后续的其他类型的傅里叶变换也是基于最原始的傅里叶变化得到的,无论是离散时间傅里叶变换还是离散傅里叶变换,都需要对原始的傅里叶变换非常熟悉。浅显描述傅里叶变换和傅里叶级数,傅里叶级数相当于把信号一巴掌拍散,出现无限个有一定间隔的幻影,而傅里叶变换将信号拍成致密幻影。

文章的最后附上一张傅里叶变换表,方便读者查阅。

性质 时域 \(f(t)\) 频域 \(F(\omega)\) 时域频域对应关系
1. 线性 \(\sum_{i=1}^{n}{a_{i}f_{i}(t)}\) \(\sum_{i=1}^{n}{a_{i}F_{i}(\omega)}\) 线性叠加
2. 对称性 \(F(t)\) \(2\pi f(-\omega)\) 对称
3. 尺度变换 \(f(at)\) \(\frac{1}{| a |}F\left( \frac{\omega}{a} \right)\) 压缩与扩展
4. 时移 \(f(t-t_{0})\) \(F(\omega)e^{-j\omega t_{0}}\) 时移与相移
5. 频移 \(f(t)e^{j\omega_{0}t}\) \(F(\omega-\omega_{0})\) 调制与频移
6. 时域微分 \(\frac{\text{d}^{n}f(t)}{\text{d}t^{n}}\) \((j\omega)^{n} F( \omega)\)
7. 频域微分 \((-jt)^{n} f(t)\) \(\frac{\text{d}^{n}F(\omega)}{\text{d}\omega^{n}}\)
8. 时域积分 \(\int_{-\infty}^{t} f(\tau) \text{d} \tau\) \(\frac{F\left( \omega \right) }{j \omega} + \pi F(0) \delta(\omega)\)
9. 时域卷积 \(f_{1}(t) * f_{2}(t)\) \(F_{1}(\omega) F_{2}(\omega)\)
10. 频域卷积 \(f_{1}(t) \cdot f_{2}(t)\) \(\frac{1}{2\pi}F_{1}(\omega) * F_{2}(\omega)\)