跳转至

python数据类型

add_circle2025-03-01update2025-03-03

前面提到,所谓的变量实际上就是存储数据的内存空间,而变量值则是保存到计算机中描述客观事物的信息。

因为数据在使用过程中会有不同的使用方式或者具有不同的功能,所以python会对数据进行划分成不同的类型,这样可以方便Python解释器很好地去存储和调用这些数据。

image-20241225131955896

我们可以通过以下代码获取变量值的类型以及存储地址。

num = 100
print(type(num)) # <class 'int'>
print(id(num))   # 0x7ffe04fc04c8

name = "小黑子"
print(type(name)) # <class 'str'>
print(id(name))   # 0x7ffe04fc0512

1. 数据类型概述

image-20241225131745412

在Python中,数据类型可以根据它们的值是否可以被修改分为可变类型(mutable)和不可变类型(immutable)。

不可变类型(Immutable Types),指一旦创建,其值不能被改变。如果改变其值,实际上是创建了一个新的数据,其内存地址就会发生变化。

  1. 整型(int)
  2. 浮点型(float)
  3. 字符串(str)
  4. 元组(tuple)

可变类型(Mutable Types),指可以在原地修改其值的数据类型。

  1. 列表(list)
  2. 字典(dict)
  3. 集合(set)

不可变类型因为值不可被修改,所以是可hash的,可变类型则是不可hash的。

data = 123
ret = hash(data)
print(ret)


data = "hello world"
ret = hash(data)
print(ret)

data = 3.5
ret = hash(data)
print(ret)

data = (1,3)
ret = hash(data)
print(ret)

2. 数值类型

2.1 int

在python中整数都属于整型,即int类型,不管正负大小都属于int

d1 = 100
d2 = 0
d3 = -991
# 为了方便阅读,python支持使用下划线作为千分分位符,万分分位符。
d4 = 100_000_000
d5 = 10000_0000

在python中针对变量值中存储的数据是不需要声明数据类型的,因为python内部会自动推断数据类型,我们可以通过type函数来查看变量的数据基本类型。

print(type(d1))  # <class 'int'>  integer

但是在PEP484类型提示规范中,为了让大家写出阅读性更好,维护性更强的代码,所以建议在标记符首次出现时加上类型注解(Type Annotations)。

所谓的类型注解就是在变量/属性/参数/返回值后面使用:标记上对应数据类型。

d2: int = 100

因此后续学习中,我们声明变量都会尽量加上类型提示。当然,不加也不会报错,毕竟规范知识建议而已。

2.1.1 不同进制的整型

说到整型,就不得不提到整型数字之间进制,在计算机中除了默认的十进制以外,还存在着其他的不同进制的数值也是经常被使用到的。

# 二进制【满2进1,由0、1组成的数字,为了与默认使用的十进制进行区分,所以数字左边以0b开头】
print(0b101)
print(0B101)

# 十六进制【满16进1,由0~9,a-f组成的数字,其中a=10,b=11,...f=15,为了与默认使用的十进制进行区分,所以数字左边以0x开头】
print(0x11)
print(0x111)

# 八进制【满8进1,由0-7组成的数字,为了与默认使用的十进制进行区分,所以数字左边以0o开头】
print(0o12)
print(0o23)

我们可以使用python内置的函数来完成不同进制间的数值转换。

image-20241225220201900

代码:

#  十进制转换为二进制
print(bin(3))

#  十进制转换为十六进制
print(hex(19))

# 十进制转换为八进制
print(oct(10))

# 其他进制转十进制
print(int(0b0100))
print(int(0xffff))
print(int(0o777))

2.2 float

在python中,所有的小数不管正负都属于浮点型(float),有两种写法:十进制表示法和科学计数法。

2.2.1 十进制表示法(常用)

PI: float =  3.1415926
print(PI)  # 3.1415926
print(type(PI)) # <class 'float'>

pi: float =  3.141_592_653_589
print(pi)  # 3.1415926
print(type(pi)) # <class 'float'>

2.2.2 科学计数法

也叫指数计数法。是一种表示非常大或非常小的数字的方法,它将数字表示为一个[1,10)之间的实数与10的幂次方:a×10n。例如:

3.2e5 = 3.2×105,其中3.2是尾数,5是指数,表示10的5次方,100,000。 2.7e-3 = 2.7×10-3,其中2.7是尾数,-3是指数,表示10的-3次方,0.001。 0.2e8 = 0.2×108,其中0.2是尾数,8是指数,表示10的8次方,100,000,000。

f2: float =  3.2e5
print(f2)  # 320000.0
print(type(f2)) # <class 'float'>

f3: float =  3.2e-5
print(f3)  # 3.2e-05
print(type(f3)) # <class 'float'>

f5: float = 0.0000181111
print(f5)

注意:因为计算机存储位是有上限的,而CPU计算需要把数值转换成二进制,浮点数在转换成二进制时很容易出现循环小数,因此浮点数在计算或打印时都会存在精度丢失的问题。

f5: float = 0.9999999999999999999999999
print(f5)

print(0.1+0.2) # 0.30000000000000004

浮点型与整型之间的类型转换。

f6: float = 0.7833333
d3: int = int(f6)
print(d3)

d3: int = 50
f7: float = float(d3)
print(f7)

扩展知识:在数据科学,数据分析,自动化办公等方向,有时候会遇到三个特殊的浮点数:

# 非数字
nan: float = float('nan')
print(type(nan))

# 无穷大
Inf: float = float('inf')
print(type(Inf))

# 无穷小
ninf: float = float('-inf')
print(type(ninf))

2.3 bool

布尔型(Boolean)来源于布尔数学体系,在python中也是一种基本数据类型,这种类型只有两种值,即"真"与"假",用于表示逻辑判断的结果。

在python中用 bool表示布尔类型,“真"用关键字True表示,有成立的意思;“假"用False表示,有不成立的意思。

我们可以使用3种方式得到布尔类型的值,分别是:明确赋值,运算结果,类型转换。

1.明确赋值

b1: bool = True
print(b1)
b2: bool = False
print(b2)

2.经过比较、判断都会产生布尔类型的计算结果。

print(4 == 2) # False
print(5 > 1)  # True

3.单独一个数据没有进行计算也可以通过类型转换得到对应的布尔值,这就会涉及到布尔的零值

# 任意数据类型都存在着一个具体值对应布尔值为False,这个值一般称之为“零值”。
print(bool(0))  # 整型的零值 0
print(bool(0.0))  # 整型的零值 0
print(bool("")) # 字符串的零值 ""   空字符串

# 非零值都是True,排除了零值以外,其他都是True
print(bool(-1))
print(bool("0"))
print(bool(" "))
print(bool("-1"))
print(bool("moluo"))
print(bool(-1))
print(bool(0.0000000000000000000000000000000000000000000000000001))

3. 容器类型

容器类型(Container Types)是指可以存储多个成员(元素)的数据类型,在python中,字符串、列表、元组、集合、字典都属于容器类型。

image-20241225223740655

容器类型的主要特点是它们可以容纳多个值,并且可以通过某种方式访问这些值。容器类型包括以下几种:

  • 序列类型(Sequence):如列表(list)、元组(tuple)、字符串(str)、字节数组(bytearray)等。
  • 无序类型(Unordered ):如集合(set)和冻结集合(frozenset)、字典(dict)等。

3.1 序列类型

在 Python 中,序列类型(Sequence Type)是一类可以存储多个元素(通常是有序的)的数据结构。序列类型的主要特点是它们可以通过索引(index)访问元素,并且可以进行切片(slicing)操作。常见的序列类型包括 列表(list)元组(tuple)字符串(str)

image-20241225223829281

3.1.1 str

字符串(String)是一串由0个或多个字符组成有限定长度的序列。字符串的内容可以包含各种各样的文字信息,例如字母、标点、特殊符号、中文、日文等,在程序开发中,用户名、密码、手机号码、邮箱、地址、文章等等信息在存储时都属于字符串类型。

image-20241225224142703

在python中字符串可以使用单引号''、双引号""、或三引号"""/'''来声明字符串,从写法上分单行字符串与多行字符串。

s1: str = "hello"
print(s1)

s2: str = 'Python是世界上最流行的编程语言之一。'
print(s2)

s3: str = """
    你好,我是墨落。
    欢迎~
"""
print(s3)

s4: str = '''
    hello,my name is moluo。
    welcome~
'''
print(s4)
3.1.1.1 转义字符

转义字符(scape character)‌是一种在编程语言中具有特殊意义的字符,通常以反斜杠(\)开始,用于表示一些无法直接输入的字符或特殊的控制字符。

转义字符 说明
\n 换行符,将光标位置移到下一行开头。
\r 回车符,将光标位置移到本行开头。
\t 水平制表符,也即 Tab 键,一般相当于四个空格。
\b 退格(Backspace),将光标位置移到前一列。
\\ 反斜线,因为反斜杠作为转义字符的开始标记符了,所以要输出普通的反斜杠,就需要转义
\' 单引号,因为单引号已经作为字符串的开始和结束的标记符了,所以要输出普通的单引号,就需要转义
\" 双引号,因为双引号已经作为字符串的开始和结束的标记符了,所以要输出普通的双引号,就需要转义
\ 在字符串行尾的续行符,即一行未完,转到下一行继续写。

转义字符:https://docs.python.org/zh-cn/3.13/reference/lexical_analysis.html#escape-sequences

代码:

s1: str = "hi libai\nhi,moluo"
print(s1)

s2: str = 'I\'m moluo'
print(s2)

s3: str = "C:\\code\\python313\\scripts\\node\\python.exe"
print(s3)
3.1.1.2 输入与输出

我们可以使用input函数,接受用户在终端下通过键盘输入的信息。

username: str = input("请输入您的登录账号:")
print(username)

python中要在终端下输出内容,可以使用print语句,使用了print打印出来的内容默认都是字符串内容。

print("输出一行内容") # 输出内容,末尾自动补充\n
print("输出一行内容", end="") # 输出内容,末尾不再补充任何字符。

# 一次性输出多个变量或多个内容
name: str = "xiaoming"
message: str = "hello~ my name is "
print(message, name)
3.1.1.3 格式化输出

上面讲到 print() 函数的用法,只是最简单的使用方式,print() 还有一些高级的用法,比如格式化输出。

举个例子,大家在日常生活中通常都会收到一些短信验证码或者邮件验证码,大概如下:

image-20241225105442568

我们可以模拟上面的例子看下效果【当然不是发短信,我们会学习到,但是那是打牢基础后的事】。

msg1: str = "【中国电信】您正在登录“中国电信”APP,验证码为692352,请在3分钟内输入。避免隐私泄露,验证码切勿告知他人。"
print(msg1)

msg2: str = "【中国电信】您正在登录“中国电信”APP,验证码为213281,请在3分钟内输入。避免隐私泄露,验证码切勿告知他人。"
print(msg2)

上面的例子存在问题,第二条短信与第一条短信及其相似,只有验证码部分发生改变,但是我们却需要重新再写一段文字。所以如果希望能在保留大段文字的情况下,仅仅改变部分内容,那我们可以使用格式化输出来解决这个问题。

在目前版本的Python解释器中,支持3种写法对内容进行格式化输出,分别是%格式占位符,format命名绑定以及F-String格式字符串。

3.1.1.3.1 %格式占位符

print() 函数使用以%开头的格式占位符(也叫转换说明符)对各种类型的数据进行格式化输出,常用占位符如下所示。

格式占位符 解释 例子
%s 使用 str() 函数将表达式转换为字符串 print("我的祖国是%s" % '中国')
%r 使用 repr() 函数将表达式转换为字符串 print("我的祖国是%r" % '中国')
%d、%i 把数值转换为十进制整数,%d与%i没差别。 print("%d" % 3.15)
%o 把整数转换为八进制整数。 print("%o" % 10)
%x、%X 把整数转换为十六进制整数(%x为小写字母,%X为大写字母)。 print("%x、%X" % (15,15))
%e、%E 把数值转化为科学计数法表示的浮点数(e 小写,E大写) print("%e、%E" % (10000, 10000))
%f、%F 把数值转化为十进制浮点数 print("%F、%f" % (3.1415926,-3.1415926))
%c 把整数格式化为字符及其 ASCII 码 print("%c %c" % (65, 97))

代码:

year: int = 2022
month: int = 1
date: int = 7
# %02d 以整数输出,如果不足2位整数的,左边加0补充进去
print("%d-%02d-%02d" % (year, month, date))
year: int = 2022
month: int = 10
date: int = 17
print("%d-%02d-%02d" % (year, month, date))

在 print() 函数中,左侧就是格式化字符串(字符串模板),可以放置一些格式占位符。上面代码中的格式化字符串中包含%s%d两个格式占位符,它最终会被后面的name和age变量的值所替代。中间的%是一个分隔符,用于区分格式化字符串与后面要输出的变量数据。

在实际开发中,一般都是对用户输入的内容进行格式化处理或格式化输出:

username: str = input("请输入您的账号:")
password: str = input("请输入您的密码:")
print("账号:%s" % username)
print("密码:%s" % password)

# 格式化输出多个变量,需要在 % 后面把对应的变量使用小括号括起来。
print("账号:%s\n密码:%s" % (username, password))
3.1.1.3.2 format

format的用法不同于上面使用%号来完成格式化占位,而是采用了大括号来完成格式化占位,同时还支持在大括号里面添加序号和标记符绑定的方式,让格式化变得更加灵活。

username: str = input("请输入您的账号:")
password: str = input("请输入您的密码:")
sms_code: str = input("请输入短信验证码:")
print("账号:{}".format(username))
print("密码:{0}".format(sms_code))
print("密码:{password}".format(password=password))

# 格式化输出多个变量
print("账号:{}\n密码:{}\n验证码:{}".format(username, password, sms_code))
print("账号:{0}\n验证码:{}\n密码:{1}".format(username, password, sms_code))
print("账号:{username}\n密码:{password}\n验证码:{sms_code}".format(username=username, password=password, sms_code=sms_code))
3.1.1.3.3 F-string

F-string,Formatted String Literals的简写,通过在字符串前添加f或F,可以让字符串内部通过英文大括号{}嵌入变量或Python表达式的值。

表达式

在数学中,表达式(expression)是由数字、未知数和运算符(如 +、-、*、/ 等)组成的组合,它表示一个值。数学表达式可以很简单,比如一个单独的数字或未知数,也可以很复杂,包含多个运算和括号。例如:

  • 简单的表达式:3, x, 2x + 1
  • 复杂的表达式:(3 + 4) * (x - 2) / 5

编程的表达式同样由变量、字面量(直接给出的值,即数值类型和字符串等)、运算符和函数调用组成。编程表达式的结果可以是任何数据类型的值,这取决于表达式的内容和编程语言的运算规则。例如,在Python中:

  • 简单的表达式:3, 变量x, 2 * x + 1
  • 复杂的表达式:(3 + 4) * (x - 2) / 5, len("hello")

基本使用

username: str = input("请输入您的账号:")
password: str = input("请输入您的密码:")
print(f"账号:{username}")
print(f"密码:{password}")

# 格式化输出多个变量
print(f"账号:{username}\n密码:{password}")

嵌入表达式

a: int = 5
b: int = 10
print(f"a+b={a + b}")
# 简写
print(f"{a+b=}")

格式化字符串

name: str = "moluo"
print(f"He said his name is {name!s}.")  # !s是默认值,可以不写

name: str = "moluo"
print(f"He said his name is {name!r}.")

# 填充与对齐
message: str = "hello world"
print(f"{message=:<20}")
print(f"{message=:>20}")
print(f"{message=:^20}")

# 添加填充内容
message: str = r"welcome to Python!"
print(f"{message:*>50}")
print(f"{message:*<50}")
print(f"{message:*^50}")

# 填充与截断
message: str = "hello moluo"
print(f"{message:_>10.5}")

格式化浮点数

pi: float = 3.1415926
print(f"{pi:.2f}")

sale: float = 0.88
print(f"{sale:.2%}")

num: float = 10000.31
print(f"{num:10.5f}")

格式化整型

# 补零填充
uid: int = 100
print(f"{uid:06d}")

# 使用千分位符号显示数值
num: int = 1000000000
print(f"{num:,}")
print(f"{num:_}")

num1: int = 16
print(f"{num1:0b}") # 显示成二进制
print(f"{num1:#0b}") # 显示成带符号的二进制
print(f"{num1:0o}") # 显示成八进制
print(f"{num1:#0o}") # 显示成带符号的八进制
print(f"{num1:0x}") # 显示成十六进制
print(f"{num1:#0x}") # 显示成带符号的十六进制

格式化日期时间

from datetime import datetime
current: datetime = datetime.now()
print(f"{current:%Y-%m-%d %H:%M:%S}")

大括号嵌套

places: int = 2
num: float = 1.23456
print(f'num={num:.{places}f}')

注意,以下用法在Python版本<3.12的解释器中会报错:

# F-string不支持嵌套
print(f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}")

# 不支持使用\反斜杠
print(f"第一行{\n}第二行")

# 不支持大括号内部使用#号等注释
print(f"{1+3  # 这是f-string内部的大括号中的注释
}")
3.1.1.4 常用操作
3.1.1.4.1 获取长度

也叫获取成员/元素的个数。

string: str = "hello moluo"
print(len(string))
3.1.1.4.2 索引操作

字符串属于序列类型,所谓序列,指的是一块可存放多个值的连续内存空间,这些值按一定顺序排列,可通过每个值所在位置的编号(称为索引、下标)访问它们。

msg: str = "hello moluo"

image-20241228065547053

Python 还支持索引值是负数,此类索引是从右向左计数,换句话说,从最后一个元素开始计数,从索引值 -1 开始,如图 所示。

image-20241228065442755

代码:

msg: str = "hello moluo"
print(s[6]) # m
print(s[-10]) # e
3.1.1.4.3 切片操作

在Python中,切片(slice)是一种从序列类型数据中(如列表、元组、字符串等)中提取子序列的操作方式。

基本格式如下:

序列类型变量名[start : end : step]

上面格式中:

  1. 取出的元素数量为:结束位置(end) - 开始位置(start),如果计算结果小于0,则提取不到成员。

  2. 取出元素遵循左闭右开原则,也就是包含开始位置(start)对应的字符,但是不包含结束位置(end)对应的字符。

  3. 当缺省开始位置时,表示从开头提取到结束位置,同理,当缺省结束位置时,表示提取到末尾,两者同时缺省时,相当于复制本身。

  4. 步长(step)的值为正数时,表示从左向右切取一段成员,步长(step)为负数时则表示从右向左切取一段成员。

step默认值为1,表示间隔1个位置切取1次,当step的绝对值为n,则间隔n个位置切取一次。

string: str = "hello moluo"
print(string[1:4]) # ell  => 取索引1到索引3(左闭右开)
print(string[:4])  # hell => start缺省,默认从0取
print(string[1:])  # ello moluo  => end缺省,默认取到最后
print(string[1:-1]) # ello molu

print(string[6:9]) # mol
print(string[-4:-1]) # olu
print(string[-1:-4]) # 空
print(string[-1:-4:-1]) #oul  step为1:从左向右一个一个取。为-1 ,从右向左一个取
3.1.1.4.4 成员判断

判断成员是否存在:Python 中,可以使用 in 关键字检查某元素是否为序列的成员。

string: str = "hello moluo"
print("moluo" in string) # True
3.1.1.4.5 相加拼接与乘法叠加

支持两种类型相同的序列使用“+”运算符做相加操作,它会将两个序列进行连接,但不会去除重复的元素。

使用数字 n 乘以一个序列会生成新的序列,其内容为原来序列被重复 n 次的结果

string: str = "hello"+" moluo"
print(string) # hello moluo

string: str = "*"*10
print(s) # **********
3.1.1.4.6 解包
a,b = "wb"
print(a) # w
print(b) # b

3.1.2 list

列表(list)就是一列有顺序的数据。开发中经常需要将一组(0个或多个)数据存储成列表,以便后边的代码使用,例如:用户名单,商品列表,地址列表等等。

image-20241228235233042

列表会将所有元素都放在一对中括号[ ]里面,相邻元素之间用逗号,分隔,如下所示:

[element1, element2, element3, ..., elementN]

注意:python的列表可以存放不同的,任意的数据类型的成员,在其他语言(如Cjava等)中也有类似的数据类型叫数组,但并不能存放多种不同类型的成员。

3.1.2.1 声明列表
data: list = []
print(data, type(data))

data: list = ["小明","小红","小黑", "小白"]
print(data,type(data))
3.1.2.2 常用操作
3.1.2.2.1 获取长度

列表和字符串一样,可以通过len()函数来获取成员的个数

data: list = ["xiaohei","moluo","xiaobai"]
print(len(data))
3.1.2.2.2 索引取值

列表也是 Python 序列的一种,我们可以使用索引(Index)访问列表中的某个元素(得到的是一个元素的值),也可以使用切片访问列表中的一组元素(得到的是一个新的子列表)。

image-20241229084854951

代码:

data: list = [10,11,12,13,14]
print(data[2]) # 12
print(data[-1]) # 14
3.1.2.2.3 成员操作

我们可以通过索引对列表的成员值进行改动,这种操作并不会产生新的列表。

image-20241229074655865

代码:

data: list = ["小明", "小红", "小黑", "小白"]
print(data, id(data))
data[0] = "小蓝"
print(data, id(data))

在python中,列表是一种可变的序列类型,所以修改自身成员值以后,并不会产生新的内存空间,而是在原来的数据中进行修改。我们后续学习的集合与字典也是可以直接修改自身成员值而不会产生新的内存空间,这是可变类型的特点。除了上面的更新成员以外,还可以进行添加新成员,删除成员等操作,但是这些内容,我们到后面数据类型进阶的章节中再学习。

3.1.2.2.4 切片操作

作为序列类型之一,列表与字符串一样,可以通过同样的切片操作提取列表中的一部分成员,列表的切片操作的返回结果是列表。

data: list = ["小明", "小红", "小黑", "小白", "小灰", "小橙", "小黄"]
print(data[2:5]) # ["小黑", "小白", "小灰"]
print(data[-3:-1]) # ["小灰", "小橙"]
print(data[:3]) # ["小明", "小红", "小黑"]
print(data[1:]) # ["小红", "小黑", "小白", "小灰", "小橙", "小黄"]
print(data[:]) # 复制 ["小明", "小红", "小黑", "小白", "小灰", "小橙", "小黄"]
print(data[2:4]) # ["小黑", "小白"]
print(data[-1:-3]) # 因为结束下标比开始下标小,所以无法提取切片,[]
print(data[-1:-3:-1]) # ['小黄', '小橙']
print(data[::2]) # ["小明","小黑", "小灰", "小黄"]
3.1.2.2.5 成员判断

in 关键字检查某元素是否为序列的成员

data: list = ["小明", "小红", "小黑", "小白"]
print("小白" in data) # True
print("小绿" in data) # False
3.1.2.2.6 相加拼接与乘法叠加
data1: list = [1,2,3]
data2: list = [4,5,6]
print(data1+data2) # [1, 2, 3, 4, 5, 6]

print(data2*3) # [4, 5, 6, 4, 5, 6, 4, 5, 6]
3.1.2.2.7 解包
a,b = [1,2]
print(a) # 1
print(b) # 2

3.1.3 tuple

Python的元组与列表类似,不同之处在于元组的元素只能读,不能修改,所以我们可以理解为不能修改成员的列表。通常情况下,元组用于保存无需修改的一组固定内容,例如:生活中的常识信息,表单中的选项列表,程序中的配置参数,图片的像素信息等等。

image-20241229130941267

元组会将所有元素都放在一对小括号()里面,相邻元素之间用逗号,分隔,如下所示:

(element1, element2, element3, ..., elementN)
3.1.3.1 声明元组

代码:

# 1. 使用小括号把成员包起来
levels: tuple = ("青铜", "白银", "黄金", "铂金", "钻石", "皇冠", "王牌")
print(levels, type(levels)) # ("青铜", "白银", "黄金", "铂金", "钻石", "皇冠", "王牌") <class 'tuple'>

# 2. 可以省略小括号,使用逗号连起来
levels: tuple = "青铜", "白银", "黄金", "铂金", "钻石", "皇冠", "王牌"
print(levels, type(levels)) # ("青铜", "白银", "黄金", "铂金", "钻石", "皇冠", "王牌") <class 'tuple'>

# 3. 使用tuple类型函数来声明元组或把其他序列的数据转换成元组
t3: tuple = tuple()
print(t3, type(t3)) # () <class 'tuple'>
str1: str = "hello"
t4: tuple = tuple(str1)
print(t4, type(t4)) # ('h', 'e', 'l', 'l', 'o') <class 'tuple'>
t5: tuple = tuple([1, 2, 3])
print(t5, type(t5)) # (1, 2, 3) <class 'tuple'>

注意:当创建的元组中只有一个元素时,该元素后面必须要加一个逗号,。否则 Python 解释器会忽略掉小括号,导致无法正确声明元组。

t1: tuple = (1)  # 1 <class 'int'> # 单个成员在小括号中不加逗号,并非元组
print(t1, type(t1)) # (1,) <class 'tuple'>

t2: tuple = (1,)
print(t2, type(t2)) # (1,) <class 'tuple'>

t3: tuple = 1,
print(t3, type(t3)) # (1,) <class 'tuple'>
3.1.3.2 常用操作

和列表一样,元组也是一种序列类型,因此也支持获取长度、索引取值和切片操作、判断成员是否存在,解包。常用于存储一些不可改变的序列数据。

3.1.3.2.1 获取长度
rainbow: tuple = ("Red","Orange","Yellow","Green","Blue", "Indigo", "Violet")
print(len(rainbow))
3.1.3.2.2 索引取值

列表是 Python 序列的一种,我们可以使用索引(Index)访问列表中的某个元素(得到的是一个元素的值),也可以使用切片访问列表中的一组元素(得到的是一个新的子列表)。

image-20241225120858341

代码:

rainbow: tuple = ("Red","Orange","Yellow","Green","Blue", "Indigo", "Violet")
print(rainbow[2]) # Yellow
print(rainbow[-1]) # Violet
3.1.3.2.3 切片操作
rainbow: tuple = ("Red","Orange","Yellow","Green","Blue", "Indigo", "Violet")
print(rainbow[2:5])
print(rainbow[-3:-1])
print(rainbow[:3])
print(rainbow[1:])
print(rainbow[:])
print(rainbow[2:4])
print(rainbow[-3:-1])
print(rainbow[-1:-3])
print(rainbow[-1:-3:-1])
print(rainbow[::2])
3.1.3.2.4 成员判断

in 关键字检查某元素是否为序列的成员

rainbow: tuple = ("Red","Orange","Yellow","Green","Blue", "Indigo", "Violet")
print("White" in rainbow) # False
print("Blue" in rainbow) # True
3.1.3.2.5 相加拼接与乘法叠加
dec_data: tuple = ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
let_data: tuple = ("A", "B", "C", "D", "E", "F")
hex_data: tuple = dec_data + let_data
print(hex_data) # ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')

test_points: tuple = ((255, 0, 0),) * 100
print(test_points)
3.1.3.2.6 解包
(x, y) = (100, 200)    # 小括号可以省略   ===>      x, y = 100, 200
print(a) # 100
print(b) # 200

交换2个变量的值,代码:

x: int = 10
y: int = 20
x,y = y,x
print(x,y) # 20 10

3.2 无序类型

无序类型,顾名思义就是没有顺序的容器类型,包括集合,字典都属于无序的容器类型。

3.2.1 set

Python中的集合(set),和数学中的集合概念相似,是一个由不同的可hash元素组成的集合,常用于存储一些需要去重的集合信息。

3.2.1.1 声明集合

Python 集合会将所有元素放在一对大括号 {} 中,相邻元素之间用“,”分隔,如下所示:

{element1,element2,...,elementN}

其中,elementN 表示集合中的元素,个数没有限制,但是不会重复。

同一集合中,只能存储不可变数据类型(包括整型、浮点型、字符串、元组)的成员,无法存储可变数据类型(包括列表、字典、集合这些),否则Python 解释器会抛出 TypeError类型错误。

members: set = {"小明", "小黑", "小红"}
3.2.1.2 常用操作

由于集合中的元素是无序的,所以无法像列表那样使用下标访问元素,接下来我们先学习几个常用操作先,关于集合的更多操作后续在数据类型进阶章节中学习。

3.2.1.2.1 获取长度
my_set: set = {1, 2, 3}
print(len(my_set))
3.2.1.2.2 去重操作

我们可以通过类型转换给列表、元祖等数据的成员进行去重操作。

data1: tuple = (1, 3, 5, 6 ,5, 3)
new_set: set = set(data1)
print(new_set)
new_data: tuple = tuple(new_set)
print(new_data)


data2: list = ["西瓜", "葡萄", "西瓜", "苹果", "苹果"]
new_set: set = set(data2)
print(new_set)
new_data: list = list(new_set)
print(new_data)

3.2.2 dict

字典(dict)经常用于保存“键值对”这种映射关系的数据类型,类似大家的身份证实际上就可以理解是一种映射关系的字典。

image-20241225194114341

3.2.2.1 声明字典

python使用 { } 创建字典,由于字典中每个元素都包含键(key)和值(value)两部分,因此在创建字典时,键和值之间使用冒号:分隔,相邻元素之间使用逗号,分隔,所有元素放在大括号{ }中。语法格式如下:

{
    "key1": "value1", 
    "key2": "value2", 
    ..., 
    "keyN": "valueN"
}

注意:

1、同一字典中的各个键必须唯一,不能重复,重复则会自动覆盖之前的键。

2、低版本中,字典的键值对是无序的,但在3.6版本以后字典的键值对默认按插入顺序。

3、字典中的键可以是任意的不可变类型数据

代码:

id_card: dict = {
    "username": "韦小宝",
    "gender": "男",
    "nationality": "汉",
    "born": "1654-12-20",
    "address": "北京市东城区景山前街4号紫禁城敬事房",
    "number": "11204416541220243x"
}
3.2.2.2 常用操作
3.2.2.2.1 按键取值
print(id_card["username"])
print(id_card["address"])
3.2.2.2.2 成员判断
print("price" in book)
3.2.2.2.3 修改成员
# 修改已有键的值
book["address"] = "扬州丽春院"
# 添加一个新的键值对成员
book["wife"] = {'阿珂', '苏荃', '双儿', '曾柔', '沐剑屏', '方怡', '建宁公主'}
# 删除成员
del book["number"]
3.2.2.3 练习

如果把《鹿鼎记》这本书的基本信息使用字典来记录的话,应该怎么写呢?

image-20241225203437499

代码:

book: dict = {
    "title": "鹿鼎记"
    "author": "金庸",
    "type": "长篇小说",
    "publish": "广州出版社",
    "price": 99.90,
    "id": "9787546206127",
}

4. 类型转换

在编程中为了满足运算规则或程序调用要求,而把变量值的数据类型转换成另一种数据类型的过程,叫类型转换。通常存在2种不同的类型转换方式:

  • 隐式类型转换,也叫类型自动转换,是指在编程中,当不同类型的数据进行运算操作时,解释器自动将较小数据类型的值转换为较大数据类型的值。

精度大小:bool < int < float

  • 显式类型转换,也叫类型强制转换,是指在编程中,开发人员可以通过使用类型函数对变量值强制转换成另一种类型。

4.1 隐式类型转换

# float = int + float
data = 3 + 3.5
print(data)

# float = bool + float
data = True + 3.14
print(data)

# int = bool + int
data = True + 2
print(data)

4.2 显式类型转换

Python是一门动态类型的编程语言,所以在Python语法上创建一个变量是不需要声明这个变量的数据类型的,因为动态类型语言的解释器内部存在类型自动推断。虽然为了满足PEP484规范,Python建议我们加上类型注解,但是Python依然是一门动态类型,与Java、Go、C/C++等静态类型语言有本质上的区别。

类型函数 把其他类型数据转换成 注意事项
int() 整型 仅支持浮点型、布尔型、纯数字组成的字符串
float() 浮点型 仅支持整型、布尔型、纯数字组成的字符串
bool() 布尔型 任意类型,只需要记住零值即可。
str() 字符串 任意类型
list() 列表 容器类型
tuple() 元组 容器类型
set() 集合 容器类型
dict() 字典 能组合成键值对结构的嵌套类型,如:列表,元组。

代码:

i: int = int("3")
print(i,type(i)) # 3 <class 'int'>

s: str = str(3.14)
print(s,type(s)) # 3.14 <class 'str'>

x: int = int(True)
print(x, type(x)) # 1 <class 'int'>

y: bool = bool(0)
print(y, type(y)) # False <class 'bool'>

a: float = float(True)
print(a, type(a)) # 1.0 <class 'float'>

a: float = float("3.15")
print(a, type(a)) # 3.15 <class 'float'>

# # 以下错误示例:
# a: float = float("3.15元")
# print(a, type(a)) # ValueError: could not convert string to float: '3.15元'

# 列表可以通过转换数据类型为集合,可以去除重复的成员
data: list = [1,3,4, 5, 3, 2, 1]
print(data, type(data))  # [1, 3, 4, 5, 3, 2, 1] <class 'list'>

ret: set = set(data)
print(ret, type(ret))  # {1, 2, 3, 4, 5} <class 'set'>

data: list = list(ret)
print(data, type(data)) # [1, 2, 3, 4, 5] <class 'list'>

# 字典转列表,可以直接提取字典的所有key出来
data: dict = {"A":1, "B":2}
ret: list = list(data)
print(ret) # ['A', 'B']


# 元组可以通过转换数据成列表,修改成员的排列位置
old_data: tuple = (1, 2, 3, 4)
data: list = list(old_data)
data[1], data[2] = data[2], data[1]
new_data: tuple = tuple(data)
print(new_data) # (1, 3, 2, 4)

扩展:我们可以通过类型函数直接获取各种数据类型的零值。

# 整型
i: int = int()
print(f"{i=}", bool(i))

# 浮点型
f: float = float()
print(f"{f=}", bool(f))

# 字符串
s: str = str()
print(f"{s=}", bool(f))

# 布尔值
b: bool = bool()
print(f"{b=}", bool(b))

# 列表
l: list = list()
print(f"{l=}", bool(l))

# 元祖
t: tuple = tuple()
print(f"{t=}", bool(t))

# 集合
set1: set = set()
print(f"{set1=}", bool(set1))

# 字典
d: dict = dict()
print(f"{d=}", bool(d))

5. 变量引用机制

在Python中,变量和数据在内存实际上是分开存储的。在变量赋值时,Python解释器内部的工作流程,大概如下:

image-20241231001113951

变量首次赋值给一个新数据时,Python解释器会把变量名存入到命名空间这个字典中,并开辟一个独立的内存空间存储变量值,所以变量所在的空间并不保存真实的数据,而是保存着数据在内存中的地址,因此我们称之引用。

a = 10
b = 20
# 获取当前模块的命名空间。
data: dict = globals()
print(data)

6. 变量缓存机制

在计算机中,内存是最重要的计算机资源之一,直接关系到程序的运行速度和流畅度。所以Python官方为了达到节省内存的目的,在Python解释器启动时会开辟一个全局内存空间,这个内存空间保存了一个叫interned的字典专门用于保存一些满足条件的常用小数据。因为这个空间专门用于保存小数据,因此这个内存空间就叫小数据池,也叫内存驻留机制或变量缓存机制(memory residency mechanisms)。

小数据池的工作流程可以简单归纳成以下:

  • Python解释器启动时把一些常用小数据保存到interned字典的内存空间中作为该字典的键值对成员,而不再重新开辟一个空间。
  • Python解释器在程序运行时,当首次遇到一些满足条件的小数据时,会判断interned字典的内存空间中是否已经保存了该数据,如果没有,则保存到字典中,如果有,则自动调用该数据空间中的小数据在后续程序中进行引用。

image-20241230143317287

从上图中,我们可以知道小数据池的优缺点:

优点: 能够提⾼一些常用字符串,整数的存储和处理速度,因为省略了数据创建和内存分配过程。 缺点: 在小数据池中缓存新的小数据时,需要花费更多的操作时间。

我们可以使用is关键字或者id函数与==来判断2个变量的内存地址是否一样。

# 方式1:
num1: int = 10
num2: int = 10
print(id(num1) == id(num2))

# 方式2:
num1: int = 10
num2: int = 10
print(num1 is num2)

IDE编辑器为了加速运行程序会缓存更多的数据到内存中,因此看不出Python本来的内存驻留机制效果,因此以下内容我们使用终端命令行进行演示。

6.1 自动驻留

Python中的解释器会对以下满足条件的小数据实现自动驻留在内存中interned字典里面:

  • 整型,-5~256范围内
  • 布尔型:True与False
  • 字符串,长度小于2的ascii字符,由字母、数字、下划线组成的字符串内容。
  • 空元组
  • 内置常量(Python内置的常量不多,除了布尔型的2个,就只有4个:NoneNotImplementedEllipsis__debug__

除了上述列出的小数据,其他数据一概不会自动驻留。

常量

常量是一种不能被修改值的字面量。在其他语言中,常量是一种语法,但是在python中并没有常量这种语法。

在Python中,如果要实现常量类似的效果,一般约定俗成是使用变量,并把变量名设置为大写。

USER = "admin"

PATH = "C:/tool"

6.2 手动驻留

通过在源代码中使用sys模块中的intern函数来驻留字符串。这种方式并不是真实的把字符串驻留到内存中interned字典,而是另外创建了一个新的字典保存这些字符串。

import sys
x: str = sys.intern("admin@qq.com")
y: str = sys.intern("admin@qq.com")
print( x is y)

6.3 永生对象

满足被Python解释器自动驻留到内存空间的小数据,被称之为永生对象(Immortal Objects,或 不朽对象,不死对象 )。

永生对象是不会被Python解释器删除的。

我们可以通过以下代码(这些代码涉及到一些知识盲点,我们将会在面向对象进阶的章节之前学习到它们)来识别数据是否是永生对象。

# 导包
import sys
from typing import Callable
# 声明一个用于判断是否是永生对象的is_immortal函数
is_immortal: Callable = lambda obj: sys.getrefcount(obj)  == 2**32-1

# 判断None是否是永生对象
print(is_immortal(None))
# 判断256是否是永生对象
print(is_immortal(256))
# 判断257是否是永生对象
print(is_immortal(257))