【Python】2. Python数据类型

Python 数据类型

运算符

算数运算
+     -     *     /     //     %     **
  • // 整数除法,与 C 中的对整数的除法相同,结果向下取整
  • ** 幂运算
位运算
<<    >>    &     |     ^     ~
比较运算
<     >     <=    >=     ==    !=     &&     ||

默认的相等性比较 == != 依据的是对象的内存引用。

逻辑运算
not     or     and
其他运算符
  • in 元素是否在某个序列类型中
  • is 对象是否来自同一个内存空间

运算符重载

数字类型

字面量和内置函数

int float complex

数字类型均支持所有算术运算符(除了复数不支持取余运算),在二元及以上的算数运算操作符中,其操作数会进行更宽的隐式转换。

其中复数类型字面量为 a + bja + 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返回整数的字节列表
lengthint,列表长度
byteorderstring,字节序, ‘big’ | ‘little’
signed=Falsebool,使用补码
int.from_bytes(bytes, byteorder, *, signed=False) -> int从字节列表返回一个整数
bytesint[],字节列表, 或一个迭代器
byteorderstring,字节序, ‘big’ | ‘little’
signed=Falsebool,使用补码

其中 byteorder 参数可以选择使用本地主机的字节序,它作为常量放在 sys.bytpeorder 中,随环境改变。

注意,对于 complex,其字面量后可以直接引用方法,而 intfloat 必须使用一个左值进行引用,或套上括号。

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十六进制的科学计数法返回一个浮点数
hexstring

迭代器类型

对于 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 listele not in list

  • list1 + list2: 浅拼接

  • list1 * number: 浅重复拼接自身

  • 切片

    • list[begin:end]: 从 begin 开始,到下标 end - 1 的元素序列

      当 begin 或 end 为负数时,对应将被转换为 len(list) - abs(i),例如对于五个元素的 list list[0:-2] 等价于 list[0:3]

    • list[begin:end:step]: 从 begin 开始,根据步长 step 计到 end - 1,并取所有经过的下标。

      当 step 为负数时,将倒置序列,此时 begin 应较 end 大,根据步长 step 计到 end + 1

方法
index(x[, i[, j]]) -> intx 在 list 中的下标
x欲寻找的元素
iint, 左闭区间
jint, 右开区间

类似于 list[i:j].index(x),只不过后者返回的是切片内部的下标。

count(x) -> intx 在 list 中出现的次数
x元素

可变序列类型

list 是可变序列类型,可变序列类型额外支持这些操作和方法

操作
  • list[i] = ele

  • list[i:j] = iterablelist[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插入元素
iint, 插入位置
ele元素
pop([i]) -> eleType移除元素
iint, 移除位置
remove(ele) -> None移除顺序第一个 ele 元素
ele元素
reverse() -> None就地翻转序列

不可变序列类型

tuplerange 是不可变序列类型。

不可变序列类型与可变序列类型的主要区别在于

  • 不可变序列类型没有实现可变序列类型特别提供的方法
  • 可变序列类型未实现 __hash__ 方法,这带来的一个主要不同点是可变序列类型无法作为 dict 的 key。

list

创建
  • 使用字面量 [1, 2, 3,][ele for ele in iterable]
  • 使用构造函数 list(iterable)
特有方法
sort(*, key=None, reverse=False) -> None原地排序
key=Nonefunciton, 将每个比较键应用了该方法后再进行排序
reverse=Falsebool, 反序

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忽略反斜杠与换行符
\\反斜杠(\
\'单引号('
\"双引号("
\aASCII 响铃(BEL)
\bASCII 退格符(BS)
\fASCII 换页符(FF)
\nASCII 换行符(LF)
\rASCII 回车符(CR)
\tASCII 水平制表符(TAB)
\vASCII 垂直制表符(VT)
\ooo八进制数 ooo 字符与 C 标准一致,接受最多三个八进制数字。
\xhh十六进制数 hh 字符与 C 标准不同,必须为两个十六进制数字。

字符串字面值专用的转义序列:

转义序列意义备注
\N{name}Unicode 数据库中名为 name 的字符
\uxxxx16 位十六进制数 xxxx 码位的字符必须为 4 个十六进制数码。
\Uxxxxxxxx32 位 16 进制数 xxxxxxxx 码位的字符表示任意 Unicode 字符。必须为 8 个十六进制数码。
字节串

bB 为前缀的字符串字面量将返回一个 bytes 实例。

原始字符串

字符串和字节串均可以以 rR 开头,返回一个将反斜杠作为原意字符而不会转义的字符串。

>>> br'\n'b'\\n'>>> b'\n'b'\n'
格式字符串

fF 为前缀的字符串字面量,类似于 js 中的模板字符串,在字面量中以花括号 {exp} 为标记进行表达式的值替换,其中双花括号 {{}} 会被转义为单花括号。

方法

字符串的方法非常多,这里举一些常用的例子。

集合

创建
  • 使用字面量 { ele1, ele2 }

  • 使用构造函数

    set([iterable])frozenset([iterable])

    后者的实例为 hashable。

操作和方法
  • 成员运算 innot 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 对象指实现了 MappingMutableMapping 抽象基类的实现类实例,这就像 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() 行为相同。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高厉害

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值