2.【Python】Python3 基本数据类型
第一步:分析与整理基本数据类型
1. 变量与赋值
- Python 变量不需要声明类型,赋值即创建。
- 变量本身无类型,类型依附于对象。
- 等号
=赋值,左边变量名,右边值。
counter=100# 整型变量miles=1000.0# 浮点型变量name="runoob"# 字符串多变量赋值:
a=b=c=1# 三个变量指向同一个整数对象a,b,c=1,2,"runoob"# 分别赋值不同类型查看类型:type(variable)或isinstance(variable, type)
x=10print(type(x))# <class 'int'>print(isinstance(x,int))# Truetype()与isinstance()区别:
type()不认为子类是父类类型。isinstance()会认为子类是父类类型。
2. 标准数据类型概览
| 类型 | 示例 | 可变性 |
|---|---|---|
| Number(数字) | 1,3.14,True,4+3j | 不可变 |
| String(字符串) | "hello" | 不可变 |
| bool(布尔) | True,False | 不可变(是int子类) |
| Tuple(元组) | (1,2,"a") | 不可变 |
| List(列表) | [1,2,"a"] | 可变 |
| Set(集合) | {1,2,3} | 可变 |
| Dictionary(字典) | {"name":"runoob"} | 可变 |
| bytes(字节串) | b"hello" | 不可变 |
3. Number(数字)
- 支持
int,float,bool,complex。 - 只有一种整数类型
int,无长度限制。 - 布尔是
int子类:True→1,False→0。
a,b,c,d=20,5.5,True,4+3jprint(type(a),type(b),type(c),type(d))# <class 'int'> <class 'float'> <class 'bool'> <class 'complex'>数值运算:
5+4# 94.3-2# 2.33*7# 212/4# 0.5 (浮点数除法)2//4# 0 (整数除法,向下取整)17%3# 2 (取余)2**5# 32 (乘方)删除对象引用:del var1, var2
进制字面量:
- 二进制:
0b10(2) - 八进制:
0o17(15) - 十六进制:
0x69(105) - 注意 Python 3 不允许前导零(如
080非法)。
4. String(字符串)
- 单引号
'或双引号"均可。 - 使用
\转义,r前缀表示原始字符串。 - 字符串不可变。
索引与切片:[start:end:step],start 包含,end 不包含。
my_str='Runoob'print(my_str[0:-1])# Runooprint(my_str[2:5])# nooprint(my_str*2)# RunoobRunoobprint(my_str+"TEST")# RunoobTEST原始字符串:print(r'Ru\noob')输出Ru\noob(\n 不转义)。
多行字符串:三引号'''...'''或"""..."""。
5. bool(布尔类型)
- 只有
True和False。 - 是
int子类,可参与算术运算:True + 1→2。 - 使用
bool()转换:bool(0)→False,bool(42)→True。 - 空值/空序列转换为
False:None,0,0.0,'',[],(),{}等。
print(TrueandFalse)# Falseprint(TrueorFalse)# Trueprint(notTrue)# False⚠️ 警告:1 is True会引发SyntaxWarning,因为is比较对象身份,应使用==比较值。
6. List(列表)
- 可变序列,元素类型可不同,支持嵌套。
- 用方括号
[],逗号分隔。
my_list=['abcd',786,2.23,'runoob',70.2]print(my_list[1:3])# [786, 2.23]my_list[0]=9# 修改元素my_list[2:5]=[]# 删除索引 2~4 的元素常用操作:+拼接,*重复,append(),pop()等方法。
步长切片:my_list[1:4:2]每隔一个取一个。负数步长可反向。
7. Tuple(元组)
- 不可变序列,用小括号
()。 - 单元素元组需加逗号:
tup2 = (20,)。
my_tuple=('abcd',786,2.23,'runoob',70.2)print(my_tuple[1:3])# (786, 2.23)# my_tuple[0] = 'x' # 错误!不可变元组可包含可变对象(如列表),但元组本身的引用不可变。
8. Set(集合)
- 无序、不重复、可变。
- 用大括号
{}或set()创建。空集合必须用set()(因为{}是空字典)。
sites={'Google','Taobao','Runoob'}a=set('abracadabra')# {'a','b','c','d','r'}b=set('alacazam')# {'a','c','l','m','z'}print(a-b)# 差集print(a|b)# 并集print(a&b)# 交集print(a^b)# 对称差集9. Dictionary(字典)
- 键值对映射,键必须不可变(数字、字符串、元组等),值任意。
- 用大括号
{}或dict()创建。 - Python 3.7+ 保持插入顺序。
my_dict={'name':'runoob','code':1}print(my_dict['name'])# 访问my_dict['site']='www.runoob.com'# 添加print(my_dict.keys())# dict_keys(['name', 'code', 'site'])字典推导式:{x: x**2 for x in (2,4,6)}→{2:4, 4:16, 6:36}
10. bytes 类型
- 不可变的二进制序列,元素是 0~255 的整数。
- 创建:
b'hello'或bytes("hello", encoding="utf-8")。
x=b"hello"print(x[0])# 104 (ASCII of 'h')y=x[1:3]# b'el'z=x+b"world"# b'helloworld'常用于处理文件、网络二进制数据。
11. 数据类型转换函数
| 函数 | 描述 |
|---|---|
int(x) | 转整数 |
float(x) | 转浮点数 |
str(x) | 转字符串 |
list(s) | 转列表 |
tuple(s) | 转元组 |
set(s) | 转集合 |
dict(d) | 转字典 |
chr(x) | 整数转字符 |
ord(c) | 字符转整数(Unicode码点) |
hex(x) | 整数转十六进制字符串 |
第二步:费曼学习法教学
核心思想:把数据想象成不同的“容器”
Python 中的变量就像一个贴标签。你不必事先告诉电脑这个标签会贴在什么形状的盒子上,你只需要把盒子(对象)贴好标签(赋值),电脑自己知道盒子里是什么。
1. 变量赋值:贴标签
counter=100# 把标签 counter 贴在装有整数 100 的盒子上你可以同时贴多个标签指向同一个盒子:a = b = c = 1。
也可以一次贴多个不同盒子:a, b, c = 1, 2, "runoob"。
关键点:变量本身没有类型,盒子里的东西才有类型。所以a = 10后,a = "hello"完全合法 —— 标签可以撕下来贴到另一个盒子上。
2. 为什么需要区分可变和不可变?
- 不可变(数字、字符串、元组):盒子里的东西不能改变。如果要修改,只能造一个新盒子,然后把标签贴过去。例如
s = "hello",你不能改s[0],只能s = "j" + s[1:]。 - 可变(列表、字典、集合):盒子里的东西可以增删改,标签还贴在原盒子上。例如
lst = [1,2,3],你可以lst[0]=99,盒子还是那个盒子。
好处:不可变类型更安全(如字典的键必须是不可变的),函数传参时不会被意外修改。
3. 列表 vs 元组:什么时候用哪个?
- 列表
[1,2,3]:可变的,像购物清单,随时增减。常用。 - 元组
(1,2,3):不可变的,像身份证号码,一旦定义不能改。用于保护数据或作为字典的键。
4. 集合:自动去重,快速成员检测
fruits={'apple','banana','apple'}# 自动去重 -> {'apple', 'banana'}if'apple'infruits:# 极快print("有苹果")实际应用:统计唯一IP地址、标签去重、集合运算(共同好友)。
5. 字典:万能查找表
键 → 值映射,像电话本:姓名(键)→ 号码(值)。
键必须是不可变的(字符串、数字、元组)。
phonebook={'Alice':'123-4567','Bob':'987-6543'}print(phonebook['Alice'])工作中的价值:配置参数、缓存数据、统计频率。
6. bytes:二进制数据的载体
当你从网络接收数据、读取图片文件时,得到的是bytes类型。它像字符串,但每个元素是 0~255 的整数。
data=b'\x89PNG\r\n\x1a\n'# PNG 文件头7. 类型转换:明确转换
Python 不会自动把字符串转数字(与 JavaScript 不同),你需要手动转换:
age=input("年龄:")# age 是字符串age_num=int(age)# 转成整数常见陷阱:bool("False")是True,因为非空字符串为真。
学习建议
- 动手:每学一个类型,在交互式环境(IDLE)里创建、切片、拼接、修改。
- 对比:列表和元组的相同点(索引、切片)与不同点(可变性)。
- 实际任务:写一个脚本,从日志文件中提取 IP 地址,存入集合去重,然后用字典统计每个 IP 出现次数。
综合示例:学生成绩统计
# 功能:输入多个学生多门课成绩,计算总分、平均分、等级,并输出报告defmain():students={}# 字典:键=姓名,值=字典{科目:成绩}whileTrue:name=input("输入学生姓名(空回车结束): ").strip()ifnotname:breakscores={}whileTrue:subject=input(f" 输入{name}的科目(空回车结束): ").strip()ifnotsubject:breakscore=float(input(f" 输入{subject}成绩: "))scores[subject]=score students[name]=scores# 统计并输出print("\n===== 成绩报告 =====")forname,scoresinstudents.items():total=sum(scores.values())avg=total/len(scores)grade='A'ifavg>=90else'B'ifavg>=80else'C'ifavg>=70else'D'ifavg>=60else'F'print(f"{name}: 总分={total:.1f}, 平均={avg:.1f}, 等级={grade}")# 列出各科成绩forsubj,sinscores.items():print(f"{subj}:{s}")if__name__=="__main__":main()讲解:
- 使用了字典存储嵌套结构(学生→科目→成绩)。
items()遍历键值对。sum()和len()处理成绩列表。- 条件表达式简化
if-elif-else。
第三步:芯片验证工程师视角的应用示例
作为验证工程师,我们经常需要分析仿真波形数据(VCD 或 FSDB 文件导出的文本),提取信号跳变时间,统计频率,或者比较 golden 模型输出与 DUT 输出。下面我给出一个从 CSV 文件读取信号时间序列、计算平均周期、检测毛刺的实用脚本。
场景描述
某个数字模块输出一个flag信号,我们每隔一定时间采样得到一组时间戳和值。CSV 格式如下:
time_ns,value 0,0 100,1 200,0 310,1 420,0需要计算:
- 信号的周期(两次上升沿之间的时间差,取平均)
- 是否存在毛刺(高电平宽度小于最小预期值 20ns)
- 统计高电平占空比
# filename: analyze_signal.py# 功能:分析数字波形CSV数据,检测周期、占空比、毛刺# 用法:python analyze_signal.py signal.csvimportsysimportcsvfrompathlibimportPathdefanalyze_signal(csv_path,min_high_ns=20):""" 分析信号波形,返回周期、占空比、有无毛刺 """times=[]values=[]try:withopen(csv_path,'r',encoding='utf-8')asf:reader=csv.reader(f)header=next(reader)# 跳过表头forrowinreader:iflen(row)>=2:t=int(row[0])v=int(row[1])times.append(t)values.append(v)exceptExceptionase:print(f"读取文件错误:{e}",file=sys.stderr)returnNoneiflen(times)<2:print("数据点不足",file=sys.stderr)returnNone# 找出所有上升沿(从0到1)的时间点rise_times=[]foriinrange(1,len(values)):ifvalues[i-1]==0andvalues[i]==1:rise_times.append(times[i])iflen(rise_times)<2:print("不足两个上升沿,无法计算周期",file=sys.stderr)returnNone# 计算周期(相邻上升沿时间差)periods=[rise_times[i+1]-rise_times[i]foriinrange(len(rise_times)-1)]avg_period=sum(periods)/len(periods)# 检测毛刺:检查每个高电平区间宽度glitch_detected=Falseforiinrange(1,len(times)):ifvalues[i]==1:# 高电平开始时间?需要找到连续高电平的起止start=times[i-1]ifvalues[i-1]==0elseNone# 简化方法:遍历连续高电平段pass# 更简单的方法:高电平宽度 = 下降沿时间 - 上升沿时间fall_times=[]foriinrange(1,len(values)):ifvalues[i-1]==1andvalues[i]==0:fall_times.append(times[i])# 确保上升和下降数量匹配(边缘情况忽略)min_len=min(len(rise_times),len(fall_times))high_widths=[]foriinrange(min_len):width=fall_times[i]-rise_times[i]high_widths.append(width)ifwidth<min_high_ns:glitch_detected=Trueprint(f"警告: 在时间{rise_times[i]}~{fall_times[i]}处高电平宽度{width}ns <{min_high_ns}ns")avg_high=sum(high_widths)/len(high_widths)ifhigh_widthselse0duty_cycle=avg_high/avg_period*100ifavg_period>0else0# 输出结果print(f"文件:{csv_path}")print(f"采样点数:{len(times)}")print(f"上升沿次数:{len(rise_times)}")print(f"平均周期:{avg_period:.2f}ns (期望: 100ns → 10MHz)")print(f"平均高电平宽度:{avg_high:.2f}ns")print(f"占空比:{duty_cycle:.1f}%")print(f"毛刺检测:{'是'ifglitch_detectedelse'否'}")return{"avg_period_ns":avg_period,"duty_cycle_percent":duty_cycle,"glitch":glitch_detected}if__name__=="__main__":iflen(sys.argv)!=2:print("用法: python analyze_signal.py <csv文件路径>")sys.exit(1)csv_file=sys.argv[1]ifnotPath(csv_file).exists():print(f"错误: 文件{csv_file}不存在")sys.exit(1)analyze_signal(csv_file)示例详解(验证工程师角度)
为什么用 CSV?
仿真工具可以导出信号值到 CSV,便于 Python 快速处理,无需安装专用波形查看器。数据类型应用:
- 列表
times,values存储时间序列。 - 列表推导式
[rise_times[i+1] - rise_times[i] for i in range(...)]计算周期差。 - 字典(可选)返回多个指标。
- 异常处理
try-except处理文件缺失或格式错误。
- 列表
实际工作流:
- 跑回归测试后,自动收集所有模块输出的 CSV。
- 运行脚本生成报告,标记周期误差超限或毛刺的测试用例。
- 集成到 CI 流程,不合格则自动发送邮件提醒。
扩展方向:
- 支持更多信号(使用 pandas 库)。
- 与黄金模型输出比对(类似前一步的 FIR 验证)。
- 生成可视化图表(matplotlib)。
通过这个例子,你可以看到 Python 基础数据类型(列表、整数、字符串、字典、异常)如何组合成实用的验证自动化工具。掌握这些,你就能从繁琐的手工检查中解放出来,专注于真正的设计问题。
