Python 数据类型概要
数据类型概要
内容简介
这篇文章是 Python 数据类型的概要文章,较为系统地解释数据类型。语言相对简洁,每个类型都有相应的实例,但不可能做到全面。详细说明一个数据类型也没必要,欲按照基本要求熟悉掌握重点数据类型,请参考关联文章。
基本要求
关于数据类型,一定要熟悉掌握的是以下9种:
- 整型:
int
- 布尔型:
bool
- 浮点型:
float
- 字符串:
str
- 列表:
list
- 元组:
tuple
- 集合:
set
- 字典:
dict
- 空值:
None
下面的内容,如果已经掌握类 class
相关的一些知识,会很好理解,甚至可以自己定义一套属于自己的数据系统。
标准数据类型(按层级结构)
特殊型
空值 - None
此类型只有一种取值。会由不显式地返回值的函数所返回(返回空的函数)。 它不支持任何特殊的操作。
此对象通过内置名称 None
访问,它的逻辑值为假。
bool(None) == False
另请参阅。
未实现值 - NotImplemented
此类型只有一种取值,只能通过内置名称 NotImplemented
访问。数值方法和丰富比较方法如未实现指定运算符表示的运算则应返回该值。 解释器会根据具体运算符继续尝试反向运算或其他回退操作。它不应被解读为布尔值,尽管会被当成 True
。
1 + 'a' # NotImplemented 引发 TypeError
1 > 'abs' # NotImplemented 引发 TypeError
详情参见实现算术运算。
省略值 - Ellipsis
在书写函数或类时,局部细节待定时经常使用省略。
此类型只有一种取值。是一个具有此值的单独对象。此对象通过字面值 ...
或内置名称 Ellipsis
访问。它的逻辑值为真。
class A:
...
bool(...) == True
数型 - Number
从熟悉的数字开始,python 中标准库的数字模块 numbers
中定义了 Number
这个抽象基类(abstract base class
),所有的数字型都要继承它。
class Number(metaclass=ABCMeta):
"""所有的数字型都要继承它
"""
__slots__ = ()
__hash__ = None # 具体的数据类型必须提供自己的哈希实现
如果要判断一个数据 a 是否为数字,使用 isinstance(a, Number)
。关于 __slots__
与 __hash__
,另请参照。
复数 - Complex
复数涵盖范围最大,是一种二元数。
class Complex(Number): # 下面简列一些方法和属性
"""定义了复数这一数据类型,和一些操作:求模、取实(虚)、加减乘除等。
"""
__slots__ = ()
@property
@abstractmethod
def imag(self):
"""返回虚部,是一个实数
"""
raise NotImplementedError
Complex.register(complex)
Complex
抽象类是不能实例化的,所以注册一个虚拟子类 complex
,其在内置模块 builtins.py
里面实现。于是可创建一个复数如下:
>>>c = complex(1, 2) # 创建复数 1 + 2j
>>>c.imag # 虚部 2.0(实数 float)
>>>c = 1 + 2j # 内置数据类型,直接使用字面值创建
>>>print(c) # (1+2j)
更多另请参照。
实数 - Real -> float
实数是复数的一个子集,在 python 中表现为浮点数型(float
)。
class Real(Complex): # 下面简列一些方法和属性
"""定义实数这一数据类型,继承复数的属性方法的同时,还增加一些如取整等
"""
__slots__ = ()
@abstractmethod
def __trunc__(self):
"""向0取整
"""
raise NotImplementedError
Real.register(float)
Real
抽象类是不能实例化的,所以注册一个虚拟子类 float
,其在内置模块 builtins.py
里面实现。于是可创建一个实数如下:
>>>f = float(1) # 1.0
>>>f = float() # 0.0
>>>f = float(1.8) # 1.8
>>>f = 1.8 # 内置数据类型,直接使用字面值创建
>>>f.__trunc__() # 1 向0取整
小数点是标配,更多另请参照。
有理数 - Rational
众所周知,实数分为有理数和无理数,计算机是指定内存的,所以在这不会去讨论无理数。
可以化成分数形式的实数就是有理数,或者说分数统称为有理数。常常也是最简分式的形式来存储,所以要清楚定义一个有理数,只需要弄清最简式的分子和父母即可。
class Rational(Real):
"""分子和分母 化成最简式"""
__slots__ = ()
@property
@abstractmethod
def numerator(self): # 分子
raise NotImplementedError
@property
@abstractmethod
def denominator(self): # 分母
raise NotImplementedError
def __float__(self): # 转换为小数,继承自 Real
"""float(self) = self.numerator / self.denominator
"""
return int(self.numerator) / int(self.denominator)
以上是有理数 Rational
抽象基类的所有实现,除了基础来自实数的属性和方法外,就只有分子和分母的声明了。
因为 Rational
没有注册虚拟子类,所以下面代码部分引入标准库中的分数模块 fractions.py
,创建一个分数。
import fractions
numerator, denominator = 1, 2
r = fractions.Fraction(numerator, denominator )
print(r) # 1/2
r = 1/2 # 得到的是 浮点数
整数 - int
分母为1的(分数)有理数就是整数。
class Integral(Rational):
"""定义整数这一数据类型,除了继承来的,还增加了一些 位运算等
"""
__slots__ = ()
@abstractmethod
def __invert__(self): # 按位取反
"""~self"""
raise NotImplementedError
@property
def denominator(self): # 分母为 1 的有理数
return 1
Integral.register(int)
Integral
抽象类是不能实例化的,所以注册一个虚拟子类 int
,其在内置模块 builtins.py
里面实现。于是可创建一个整数如下:
i = int(1) # 1
i = int(False) # 0
i = int(1.2) # 1
i = 10 # 内置数据类型,直接使用字面值创建
更多另请参照。
哈希(hash)
注意到,在 Number
代码块中提到的具体(不可变)对象必须有哈希实现,且 numbers.py
中的抽象基类的哈希实现都是 None
。但是各位的虚拟子类中都具有哈希实现Return hash(self)
,使用 hash
函数。一个数字实例化后,就变得不可变(可变性差,地址和空间大小锁定下来)。
- 不可变对象:数、元组、None,字符串等。
- 可变对象:列表、字典等。
一个不可变的对象,其有状态可以随时改变,这种对象不应该返回哈希值。 在其类的定义中 __hash__
的必须是 None。 比如说列表和字典等对象以及抽象类对象,它们的 __hash__
定义就是 None。
地址和空间锁定下来就可以给一个序号(算法很多),以便快速查找。
hash
和 id
地址函数可以相互转化。自定义对象一般:
class A:
...
a = A()
print(hash(a)) # 等同于 a.__hash__()
print(id(a))
print(id(a) / 16 == hash(a)) # True
更多另请参考。
布尔型 - bool
python 中的布尔型(bool
)继承自整型(int
)。
issubclass(bool, int)
结果为 True,表示 bool 是 int 子类。
class bool(int):
def __init__(self, x): # real signature unknown; restored from __doc__
pass
@staticmethod # known case of __new__
def __new__(*args, **kwargs): # real signature unknown
""""""
pass
常见数据转化后为 False
的有:
- 0,0.0,数值为0的
- “”,[], (), {},内容为空的
- 空值 None
- False
bool(0.0) == bool("") == bool([]) == bool(()) == bool({}) == bool(None) == bool(False) == False
布尔型作为 Python 中最常用的数据类型之一,另请参阅。
序列型
此类对象表示以非负整数作为索引的有限有序集。
通过内置函数 len(a)
获取条目数,通过 a[i]
进行索引,通过 a[i:j]
进行切片等操作都是适用的。
不可变序列
不可变序列类型的对象一旦创建就不能再改变。
如果包含对其它对象的引用,其中的可变对象是可以改变的;但是,一个不可变对象所直接引用的对象集是不能改变的。
元组 = ("int", "float", ["list", "tuple", "str"], (1,), 2.3, {'x': 1, 'y': 2})
元组[-1]['x'] = 2 # 可变
元组[-1] = "{x:1,y:2}" # 报错,不可变
字符串 - str
官方说辞:字符串是由代表 Unicode
码位的值组成的序列。 取值范围在 U+0000 - U+10FFFF
之内的所有码位都可在字符串中使用。
-
Python 没有
char
类型;而是将字符串中的每个码位表示为一个长度为 1 的字符串对象。 -
内置函数
ord()
可将一个码位由字符串形式转换为取值范围在0 - 10FFFF
之内的整数;chr()
可将一个取值范围在0 - 10FFFF
之内的整数转换为长度为 1 的对应字符串对象。 -
str.encode()
可以使用给定的文本编码格式将str
转换为bytes
,而bytes.decode()
则可以被用来实现相反的解码操作。
s = str(123) # "123"
s = "abc"
s[0] # 'a'
ord(s[0]) # 码位值为 97
ord(s) # 报错,只能对一个码位使用,码串报错
s.encode() # 编码成 b'abc' ,ASCII码范围内都是原字
"中国".encode() # b'\xe4\xb8\xad\xe5\x9b\xbd'
b'\xe4\xb8\xad'.decode() # 中
单引号、双引号和三引号都可以作为字符串的边界。当要嵌入中间时,满足:
- 同种需要转义,异种不用
- 三引号具有较强的边界性
s = "this is \"A\" score"
s = "this is 'A' score"
s = 'this is "A" score'
s = '''this is "A" score'''
s = '''this is 'A' score'''
s = """this is "A" score"""
s = """this is 'A' score"""
字符串作为 Python 中最常用的数据类型之一,另请参阅。
元组 - tuple
元组是不可变序列,通常用于储存异构数据的多项集,例如由 enumerate()
内置函数所产生的(序号,值)二元组;打包分包
等。 元组也被用于需要同构数据的不可变序列的情况(例如允许存储到 set
或 dict
的实例)。
可以用多种方式构建元组:
- 使用一对圆括号来表示空元组:
()
- 使用一个后缀的逗号来表示单元组:
a,
或(a,)
- 使用以逗号分隔的多个项:
a, b, c
或者(a, b, c)
- 使用内置的 tuple():
tuple()
或tuple(iterable)
t = ()
t = 1, # (1, )
t = 1, 2 # (1, 2)
t = tuple([1, 2, 3], 4) # (1, 2, 3, 4)
另外,元组是一个很强的数据类型,其圆括号:
- 表示构建器
- 构造元组
- 构造生成器
generator
,如:(i for i in range(0, 100) if i % 2)
- 表示层级关系
(1 + 2) / 3
VS1 + 2 / 3
- 表示函数或方法调用
function_name(*args)
,如:print("abc", "edfg", end="")
InstanceName.method(*args)
,如:float(-1.3).__trunc__()
注意事项:
t = (1, 2, [3, 4], [5],) # 同 (1, 2, 3, 4, 5)
t = ("abcd") # 同 ('a', 'b', 'c', 'd')
t = ("abcd", ) # 就是 ("abcd", )
元组作为 Python 中最常用的数据类型之一,另请参阅。
字节串 - bytes
字节串于是不可变序列,其中每个条目都是一个 8 位字节,以取值范围 0 <= x < 256 的整型数表示。
创建字节串对象:
- 字节串字面值,如·
b'abc'
- 内置的 bytes() 构造器,如:
bytes(range(3))
,同b\x01\x02\x03'
字节串对象还可以通过 decode() 方法解码为字符串。
china = b'\xe4\xb8\xad\xe5\x9b\xbd'
china.decode() # 中国
china.hex(sep="") # 16进制
可变序列
可变序列在被创建后仍可被改变。下标和切片标注可被用作赋值和 del (删除) 语句的目标。以数组为例:
数组 = ["int", "float", ["list", "tuple", "str"], (1,), 2.3, {'x': 1, 'y': 2}]
数组[0] = "整数"
del 数组[-1]
del 数组[0:2]
列表 - list
列表是最典型的可变序列类型,在 JS
中理解的是,相似于键为升序整数的字典。
而在 Python 中,也可以这样理解,因为升序的非负整数经过哈希变换后还是本身,所以可以理解二者检索的过程会很相似。
可以用以下方式构建列表:
- 使用一对方括号来表示空列表:
[]
- 使用方括号,其中的项以逗号分隔:
[a], [a, b, c]
- 使用列表推导式:
[x for x in iterable]
- 使用类型的构造器:
list()
或list(iterable)
l = [x for x in range(100) if x > 50]
l = list() # 空列表
l = list(一个列表) # 直接创建并防护一个列表的副本,相当于:一个列表[:]
l = ["abc"] # 即 ["abc", ]
l = [1, 5, 6, 2, 0]
l.sort()
值得注意的是,列表提供了 sort
方法。
列表作为 Python 中最常用的数据类型之一,另请参阅。
字节数组 - bytearray
字节数组对象属于可变数组。除了是可变的 (不可哈希),在其他方面字节数组提供的接口和功能都与不可变的 bytes
对象一致。
bytearray
对象没有专属的字面值语法,它们总是通过调用构造器来创建:
- 创建一个空实例:
bytearray()
- 创建一个指定长度的以零值填充的实例:
bytearray(10)
- 通过由整数组成的可迭代对象:
bytearray(range(20))
- 通过缓冲区协议复制现有的二进制数据:
bytearray(b'Hi!')
由于 bytearray
对象是可变的,除了与 bytes
共有操作之外,还支持一些可变序列的操作。
集合型
表示由不重复且不可变对象组成的无序且有限的集合。所以不能使用下标进行索引,但是可以迭代,也可以使用 len()
获取长度。
集合常见的用处是快速成员检测,去除序列中的重复项,以及进行交、并、差和对称差等数学运算。
集合 - set
set
类型是可变的 ,不可哈希。
- 增加:
add()
- 移除:
remove()
set
数据的构造:
- 字面值(非空):
{'jack', 'sjoerd'}
- 集合推导式:
{c for c in 'abracadabra' if c not in 'abc'}
- 类型构造器:
set()
,set('foobar')
,set(['a', 'b', 'foo'])
s = set()
s.add([1, 2]) # 错误,列表是可变的
s.add((1, 2)) # ok
集合作为 Python 中最常用的数据类型之一,另请参阅。
冰冻集合 - frozenset
frozenset
类型是不可变并且为可哈希,其内容在被创建后不能再改变;因此它可以被用作字典的键或其他集合的元素。
fs = frozenset(1, 2)
fs.add() # 错误
fs2 = frozenset(frozenset(1, 2), frozenset('a', 'b'))
映射型
将可哈希的值映射到任意对象,是可变对象,目前仅有一种标准映射类型 字典
。
字典 - dict
键值对。键可以是任意可哈希的对象
字典可用多种方式来创建:
- 花括号中键值对:
{'a': 97, 'b': 98}
或·{97: 'a', 98: 'd'}
或{}
- 字典推导式:
{x: 2*x for x in range(100)}
- 类型构造器:
dict()
,dict([('a', 97), ('b', 98)])
,dict(a=97, b=98)
d = { (1,): 1, 2: 2, '3': 3, }
print(d[(1,)]) # 元组可哈希,可以作为键
d1 = dict(a=97, b=98)
d1.get('a') # 97
d1.keys() # dict_keys(['a', 'b'])
del d1['a'] # 删除
d1['a'] = 97 # 添加(在末尾)
注意:字典会保留插入顺序,这意味着键将以它们被添加的顺序在字典中依次产生。 替换某个现有的键不会改变其顺序,但是移除某个键再重新插入则会将其添加到末尾而不会保留其原有位置。
字典作为 Python 中最常用的数据类型之一,另请参阅。
数据类型的判断
数据类型的判断在很多时候都会使用到,不论是代码调试还是代码里的流程控制。
type
语法:type(instance)
,返回值是一个字符串,里面包含数据的类型。
案例:
type(1) # <class 'int'>
type({}) # <class 'dict'>
...
isinstance
语法:isinstance(instance, ClassName)
,返回值是 True or False
。
案例:以下案例,根据整数类型,自定义自己的整数类型然后实现加法的重载的模板,其中 my_add
按需完善即可。
from numbers import Integral
class MyIntegral(Integral):
def __add__(self, other):
if isinstance(other, MyIntegral):
return my_add(self, other)
elif isinstance(other, Integral):
return my_add(self, other)
else:
return NotImplemented