Python 数据类型
运算符
算数运算
+ - * / // % **
//
整数除法,与 C 中的对整数的除法相同,结果向下取整**
幂运算
位运算
<< >> & | ^ ~
比较运算
< > <= >= == != && ||
默认的相等性比较 ==
!=
依据的是对象的内存引用。
逻辑运算
not or and
其他运算符
in
元素是否在某个序列类型中is
对象是否来自同一个内存空间
运算符重载
数字类型
字面量和内置函数
int
float
complex
数字类型均支持所有算术运算符(除了复数不支持取余运算),在二元及以上的算数运算操作符中,其操作数会进行更宽的隐式转换。
其中复数类型字面量为 a + bj
或 a + bJ
,其中 j/J
用来标识虚部。
abs(x)
对所有 numerics 有效,复数也是。round(x, n = 0)
用来将实数类型(int 和 float)舍入到 n 位小数complex.conjugate()
用来计算一个复数实例的共轭。
int type 的方法
extends numbers.Integral
:
bit_length() -> int | 返回数字的二进制位长度 |
---|---|
无 |
to_bytes(length, byteorder, signed=False) -> bytearray | 返回整数的字节列表 |
---|---|
length | int,列表长度 |
byteorder | string,字节序, ‘big’ | ‘little’ |
signed=False | bool,使用补码 |
int.from_bytes(bytes, byteorder, *, signed=False) -> int | 从字节列表返回一个整数 |
---|---|
bytes | int[],字节列表, 或一个迭代器 |
byteorder | string,字节序, ‘big’ | ‘little’ |
signed=False | bool,使用补码 |
其中 byteorder
参数可以选择使用本地主机的字节序,它作为常量放在 sys.bytpeorder
中,随环境改变。
注意,对于 complex
,其字面量后可以直接引用方法,而 int
和 float
必须使用一个左值进行引用,或套上括号。
233.bit_length() # SyntaxError
intVal = 233
intVal.bit_length() # ok
(233).bit_length() # ok
float type 的方法
extends numbers.Real
:
Is_integer -> bool | 该浮点数是否可以用整数表示 |
---|---|
无 |
hex -> string | 返回十六进制的科学计数法表示 |
---|---|
无 |
float.fromhex -> float | 十六进制的科学计数法返回一个浮点数 |
---|---|
hex | string |
迭代器类型
对于 for ele in obj: ...
这样的结构会使用 obj 的迭代器协议实现。
迭代器协议要求类实现 __iter__
和 __next__
方法,前者返回一个拥有 __next__
实现的迭代器对象,而 __next__
方法被要求实现返回下一个值。
因为 CPython 的 GIL(全局解释器锁),python 的多线程不会比单线程更快,除非是一些 IO 密集的程序,但这种情况下建议使用协程。
而对于一些需要分离控制流的程序,例如需要进行渲染,并同时处理键盘事件 —— 一个典型的游戏实现 —— 此时在功能上确实需要多线程实现,但这情况下实际上更建议使用 python 的多进程。
当然多线程带来的是更内聚的(或者说更方便的)数据处理,不需要花费力气去实现进程间通信,但会损失性能,总之各有利弊。
所以一般来讲,我们不会考虑并发的问题,迭代器实现的一般实践是将对象本身作为迭代器对象:
class Example:
def __init__(self, arr):
self.index = 0
self.arr = arr
def __iter__(self):
self.index = 0
return self
def __next__(self):
if self.index + 1 >= len(self.srr):
raise StopIteration
self.index += 1
return self.arr[self.index]
上述实现在并发环境下的遍历是混乱的,如果要在多线程环境(或任意可能发生调度的环境下)中使用强一致性的迭代器,则需要遵循这个规则,即针对每次的 iter
调用,均返回一个包含重置了上下文的迭代器对象,以下是一种实现:
class Example:
class __it:
def __init__(self, arr):
self.index = 0
self.arr = arr
def __next__(self):
if self.index + 1 >= len(self.srr):
raise StopIteration
self.index += 1
return self.arr[self.index]
def __init__(self, arr):
self.arr = arr
def __iter__(self):
return self.__it(self.arr)
生成器迭代器类型
该类型是生成器函数返回的对象,该对象是一个迭代器,称生成器迭代器,可简称为生成器。
yield
生成器函数定义为一个返回生成器迭代器对象的函数,在函数中使用 yield
表达式后,该函数就将成为一个生成器函数。
-
生成器迭代器实现了
__next__
方法,每次迭代将执行函数到下一个yield
表达式,然后挂起函数并返回yield
的操作数。 -
生成器迭代器的
send(value)
方法允许向迭代器内部传递一个值,并迭代返回下一个值,传递到内部的值将作为从yield
挂起处返回时的返回值,对于使用__next__
进行的迭代,迭代器函数内部yield
挂起处总返回None
。 -
生成器迭代器的
close
将在对象销毁(例如被回收)时自动调用,也可以手动调用,这将会在yield
挂起处引发GeneratorExit
并捕获和处理 ,并允许执行当前挂起处可及的finally
下的语句。
def generator(arr):
try:
i = -1
while i != len(arr):
yield arr[i]
i += 1
finally:
# do sth.
yield from
yirle from
接收一个迭代器作为其下层迭代器,此后的迭代行为将进入该下层迭代器中进行,结束后从该处返回。
注意下层迭代器不一定实现了 send(value)
方法,对上层迭代器进行的 send
调用如果在下层迭代器没有对应的实现,则引发 TypeError
。
基本序列类型
list
tuple
range
通用序列类型
三个基本序列类型均是通用序列类型,它们都可以使用以下操作和方法。
操作
-
ele in list
、ele not in list
-
list1 + list2
: 浅拼接 -
list1 * number
: 浅重复拼接自身 -
切片
-
list[begin:end]
: 从 begin 开始,到下标 end - 1 的元素序列当 begin 或 end 为负数时,对应将被转换为
len(list) - abs(i)
,例如对于五个元素的 listlist[0:-2]
等价于list[0:3]
-
list[begin:end:step]
: 从 begin 开始,根据步长 step 计到 end - 1,并取所有经过的下标。当 step 为负数时,将倒置序列,此时 begin 应较 end 大,根据步长 step 计到 end + 1
-
方法
index(x[, i[, j]]) -> int | x 在 list 中的下标 |
---|---|
x | 欲寻找的元素 |
i | int, 左闭区间 |
j | int, 右开区间 |
类似于 list[i:j].index(x)
,只不过后者返回的是切片内部的下标。
count(x) -> int | x 在 list 中出现的次数 |
---|---|
x | 元素 |
可变序列类型
list
是可变序列类型,可变序列类型额外支持这些操作和方法
操作
-
list[i] = ele
-
list[i:j] = iterable
、list[i:j:step] = iterable
list[len(list):len(list)] = iterable
等同于list.extend(iterable)
list[i:i] = ele
等同于list.insert(i, ele)
-
del list[i]
、del list[i:j]
、del list[i:j:step]
del list[i]
等同于list.pop(i)
方法
append(ele) -> None | 添加元素 |
---|---|
ele | 元素 |
clear() -> None | 清空所有元素 |
---|---|
无 |
copy() -> Sequence | 创建一个浅拷贝 |
---|---|
无 |
extend(iterable) -> None | 拼接序列 |
---|---|
iterable | 可迭代对象 |
insert(i, ele) -> None | 插入元素 |
---|---|
i | int, 插入位置 |
ele | 元素 |
pop([i]) -> eleType | 移除元素 |
---|---|
i | int, 移除位置 |
remove(ele) -> None | 移除顺序第一个 ele 元素 |
---|---|
ele | 元素 |
reverse() -> None | 就地翻转序列 |
---|---|
无 |
不可变序列类型
tuple
和 range
是不可变序列类型。
不可变序列类型与可变序列类型的主要区别在于
- 不可变序列类型没有实现可变序列类型特别提供的方法
- 可变序列类型未实现
__hash__
方法,这带来的一个主要不同点是可变序列类型无法作为dict
的 key。
list
创建
- 使用字面量
[1, 2, 3,]
、[ele for ele in iterable]
- 使用构造函数
list(iterable)
特有方法
sort(*, key=None, reverse=False) -> None | 原地排序 |
---|---|
key=None | funciton, 将每个比较键应用了该方法后再进行排序 |
reverse=False | bool, 反序 |
tuple
创建
-
使用字面量
()
、1,
、1, 2
(1, 2)
注意
(ele)
会被解释为单纯带有括号的ele
,因为 tuple 的标志实际上是逗号而不是圆括号,圆括号是在发生语法歧义时的解决方案。 -
使用构造函数
tuple(iterable)
range
range 是一个不那么 native 的类型,但在标准库文档中与 list 及 tuple 并列给出,相比 list 和 tuple,它没有独特的符号支持,开发者完全可以轻松写出功能完全相同的类型。
range 的迭代器协议实现是通过计算而不是存储得到的,即 range 仅通过 begin end 以及 step 即可得到迭代的所有信息,所以每次迭代仅仅是进行计算并返回,不会真的存储在内存中。这一点与 tuple 和 list 有较大不同。
这一点以 cpython 的实现为例,https://github.com/python/cpython/Objects/rangeobject.c
/* Initialize a rangeiter object. If the length of the rangeiter object
is not representable as a C long, OverflowError is raised. */
static PyObject *
fast_range_iter(long start, long stop, long step)
{
rangeiterobject *it = PyObject_New(rangeiterobject, &PyRangeIter_Type);
unsigned long ulen;
if (it == NULL)
return NULL;
it->start = start;
it->step = step;
ulen = get_len_of_range(start, stop, step);
if (ulen > (unsigned long)LONG_MAX) {
Py_DECREF(it);
PyErr_SetString(PyExc_OverflowError,
"range too large to represent as a range_iterator");
return NULL;
}
it->len = (long)ulen;
it->index = 0;
return (PyObject *)it;
}
创建
-
使用构造函数
range(end)
、range(begin, end[, step])
其规则与切片的规则相同
基本序列类型的比较规则
- 跨类型的次序比较是不被支持的
- 跨类型的一致性比较总是返回 False
- 除 range 不支持次序比较外,其他基本序列类型实现了次序比较和一致性比较,它们将根据字典序对每个元素进行依次比较,支持
== > <
等比较运算符
字符串序列类型
字符串是通用序列类型,且与 java 相同,是不可变的,不过可以从多个已有片段高效地构建: str.join(iterable)
和 io.StringIO
。
构造函数
字符串类型可以从其他对象类型上构造而来,调用构造函数 str(object)
将首先从 object.__str__
方法中获取字符串值,如果没有则从 __repr__
方法中提取字符串值。
字面量
字符串
-
字符串序列类型可以从单引号和双引号字面量创建
'content'
、"content"
-
更方便的的多行字符串,也可以从单引号和双引号创建
""" line1 line2 """ ''' line1 line2 '''
-
多个空白符分隔的相邻字符串会合并在一起
>>> r'\hello' "world" '\\helloworld'
在字符串字面量中使用反斜杠进行转义。
转义序列 | 意义 | 备注 |
---|---|---|
\newline | 忽略反斜杠与换行符 | |
\\ | 反斜杠(\ ) | |
\' | 单引号(' ) | |
\" | 双引号(" ) | |
\a | ASCII 响铃(BEL) | |
\b | ASCII 退格符(BS) | |
\f | ASCII 换页符(FF) | |
\n | ASCII 换行符(LF) | |
\r | ASCII 回车符(CR) | |
\t | ASCII 水平制表符(TAB) | |
\v | ASCII 垂直制表符(VT) | |
\ooo | 八进制数 ooo 字符 | 与 C 标准一致,接受最多三个八进制数字。 |
\xhh | 十六进制数 hh 字符 | 与 C 标准不同,必须为两个十六进制数字。 |
字符串字面值专用的转义序列:
转义序列 | 意义 | 备注 |
---|---|---|
\N{name} | Unicode 数据库中名为 name 的字符 | |
\uxxxx | 16 位十六进制数 xxxx 码位的字符 | 必须为 4 个十六进制数码。 |
\Uxxxxxxxx | 32 位 16 进制数 xxxxxxxx 码位的字符 | 表示任意 Unicode 字符。必须为 8 个十六进制数码。 |
字节串
以 b
和 B
为前缀的字符串字面量将返回一个 bytes
实例。
原始字符串
字符串和字节串均可以以 r
和 R
开头,返回一个将反斜杠作为原意字符而不会转义的字符串。
>>> br'\n'b'\\n'>>> b'\n'b'\n'
格式字符串
以 f
和 F
为前缀的字符串字面量,类似于 js 中的模板字符串,在字面量中以花括号 {exp}
为标记进行表达式的值替换,其中双花括号 {{}}
会被转义为单花括号。
方法
字符串的方法非常多,这里举一些常用的例子。
集合
创建
-
使用字面量
{ ele1, ele2 }
-
使用构造函数
set([iterable])
、frozenset([iterable])
后者的实例为 hashable。
操作和方法
-
成员运算
in
、not in
-
是否相交
obj.isdisjoint(other)
-
实例是否是 other 的子集
obj.issubset(other)
或者使用
<=
<
,子集和真子集 -
实例是否是 other 的超集
obj.isupset(ther)
或者使用
>=
>
,超集和真超集 -
求并集
obj.union(*others)
或使用
|
-
求交集
obj.intersection(*other)
或使用
&
-
求差集
obj.difference(*other)
或使用
-
-
在两个集合中,但不在交集中的元素
obj.symmetric_difference(other)
-
在比较时,set/frozenset 重载了这些方法,将进行逐元素对比。
仅可用于 set 而不可用于 frozenset 的方法,这些方法将会改变实例本身
-
在实例上求并集
obj.union(*other)
也可以使用
set |= other
-
在实例上求交集
obj.intersection_update(*other)
set &= other
-
在实例上求差集
obj.difference_update(*other)
set -= other
-
在实例上求对称差
obj.symmetric_difference_update(*other)
set ^= other
-
添加元素
obj.add(ele)
-
移除元素
obj.remove(ele)
,不存在时抛出KeyError
使用
obj.discard(ele)
不会抛出异常使用哦
pop
会移除并返回任意一个元素,集合空时抛出KeyError
-
清空集合
obj.clear()
在使用运算符版本的方法时,要求操作符为 set/frozenset 类型,而使用对应的方法,则允许是一个 iterable
。
字典(hash map)
字典被称为 mapping 对象,mapping 对象指实现了 Mapping
或 MutableMapping
抽象基类的实现类实例,这就像 iterable 一样,是一种 native 的约定。
创建
-
字面量
{ key1: value1, key2: value2 }
注意,key 默认使用符号的字面量或符号在上下文中作为变量的值。
即作为变量时类似于 js 中的
{ [key1]: value1, [key2]: value2 }
-
构造函数
dict(**keyword)
dict(mapping, **keyword)
dict(iterable, **keyword)
第三种重载中,
iterable
的元素必须是一个拥有两个元素的元组,且第一个元素作为 key,必须是hashable
的。
操作和方法
-
添加
map[key] = value
setdefault(key,[, default])
确保调用后集合中存在 (key, default) 键值对,并返回 value,即当 key 存在时直接返回 default,不存在时添加一个 (key, default) 键值对。
update([other: iterable | mapping],**keyword)
用一个 mapping 或一个元素为二元元组表示的键值对的 iterable 来更新覆盖当前实例。
map1 |= other
相当于update
。 -
获取
map[key]
key 不存在时抛出
KeyError
。get(key[, default])
key 不存在时返回可能为 None 的 default。
字典的子类通过实现
__missing__(self)
方法可以覆盖抛出异常的行为。 -
删除
del map[key]
key 不存在时抛出
KeyError
。map.clear()
清空字典。
pop(key[, default])
key 存在时被移除并返回 value,不存在时返回 def
ult,若未给出则抛出
KeyError
。popitem()
3.7 以先进后出 (栈) 的顺序移除和返回一个键值对元组,字典空时抛出
KeyError
-
map1 | map2
合并返回一个新字典
keys()
、values()
以及 items()
:
-
keys()
返回一个关于当前实例的 key 视图对象,该视图对象实现了迭代器协议,且迭代的内容根据字典实例动态变化(在迭代时不要更改字典实例,可能会抛出RuntimeError
或无法完全迭代所有条目)。字典实例本身是一个 key 的可迭代对象。
-
values()
返回一个关于 value 的动态视图对象。 -
itmes()
返回一个由 (key, value) 元组作为迭代元素的动态视图对象。
键的实现约定
字典的 key 必须为可哈希 (hashable) 的,这包括两个约定方法:
__hash__(self)
用来获取实例的哈希值__eq__(self, other)
发生哈希冲突时确定对象是否相同的依据
开发者自定义类型将默认拥有这两个函数的基于 id
的实现(在 CPython 中就是虚拟内存地址),也就是自定义类型默认均可哈希,不过每个实例均会哈希到不同的地址。
上下文管理器类型
语法
with open('/path/to/file', '<flag>') as f:
f.read()
# ...
使用 with
语句来创建上下文,并自动管理某个对象资源。
实现约定
-
__enter__()
进入上下文时被调用,
as
后的引用将指向该方法的返回值。 -
__exit__(excType, excVal, excTb)
退出上下文时被调用,如果上下文中抛出异常,则参数列表依次为异常类型、异常值以及调用栈。
其返回值约定为一个
bool
,表明抛出的异常是否应当不再继续抛出。应当返回一个
False
,因为该方法有责任处理上下文中的异常,且在该方法中引发的新异常将覆盖上下文中的异常并抛出。
方法对象
对于 build-in 类型,其方法在解释器底层被描述和支持。
对于自定义类型,实例的方法对象将通过两个只读属性 method.__self__
以及 method.__func__
来描述。
前者时操作该方法的实例,后者是函数本身。
直接调用方法对象,将调用一个 method-wrapper 对象,将会自动将 method.__self__
添加到参数列表中。
>>> type(obj1.__hash__.__call__)
<class 'method-wrapper'>
所以我们调用 obj.method.__func__(obj)
与 obj.method()
行为相同。