01. Python3 数据类型(一)

01. Python3 数据类型(一)

python3 基本数据类型大致分为可变数据类型不可变数据类型,不可变有Number(数字)、String(字符串)、Tuple(元组) ,可变有List(列表)Dictionary(字典)Set(集合)

Number

python3 支持int、float、bool、complex(复数)

关于Number一些运算和内置函数

# /运算结果为小数
print(4 / 2)  # ==>2.0
print(5 / 4)  # ==>1.25

# //运算结果舍弃余数
print(9.0 // 4)  # ==>2.0
print(9.2 // 4)  # ==>2.0
print(9 // 4)  # ==>2

# %运算保留余数
print(9.0 % 4)  # ==>1.0
print(9.2 % 4)  # ==>1.1999999999999993
print(9 % 4)  # ==>1

# **运算为幂运算
print(2 ** -1)  # ==>0.5

# 绝对值abs(Number)
print(abs(-3))  # ==>3

# 向原点0取整
print(int(-2.3))  # ==>-2

# 四舍五入round(no_complex, 0)
print(round(3.1415, 3))  # ==>3.142

字符串

字符串是以单引号’或双引号"括起来的任意文本,比如’abc’,"xyz"等等。

索引 (通用序列操作)

# 正索引
print("abcde"[0])  # ==>a

# 负索引,-n:倒数第n个元素
print("abcde"[-1])  # ==>e

切片[左索引:右索引:步长] (通用序列操作)

# 切片str[m:n],m默认为0,n默认为len(str)
print("abcde"[1:3])  # ==>bc
print("abcde"[1:])  # ==>bcde
print("abcde"[:2])  # ==>ab
print('abcde'[0:5:2])  # ==>ace

常用方法

# 大小写转换
print("Python".upper())  # ==>PYTHON
print("Python".lower())  # ==>python
print("hello world".capitalize())  # ==>Hello world
print("hello world".title())  # ==>Hello World

# 非重叠字符串的数量
print("ablllabllab".count("ll"))  # ==>2

# 字符串查找
print("hihihi".find("hi"))  # ==>0
print("hihihi".rfind("hi"))  # ==>4

# 字符串替换所有
print("To a To b".replace('To', 'to'))  # ==>to a to b

# 首尾去除,默认空格
print("first-first-end".strip('find'))  # ==>rst-first-e
print("first-first-end".strip('first-'))  # ==>end
print("first-end-end".rstrip('-end'))  # ==>first

# 用于输出的对齐,默认填充空格
print("hello".ljust(9, '-') + "hello".center(9, '+') + "hello".rjust(9, '-')) 
# ==>hello----++hello++----hello

格式化方法format()

List(列表)

列表是一种序列,因此索引和切片与字符串类似

序列 UML

在这里插入图片描述

检验值是否存在序列中 (通用序列操作)
print(2 in [1, 2, 3, 4])  # ==>True
print('bc' in 'abcde')  # ==>True

序列的*

# 一个包含 3 个列表的列表, 嵌套的 3 个列表各自有 3 个元素来代表井字游戏的一行方块
board = [['_'] * 3 for i in range(3)]  # [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
board[1][2] = 'X'  # [['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]
# *的一个特性,复制的是引用
weird_board = [['_'] * 3] * 3  # [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
weird_board[1][2] = 'O'  # [['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]

序列的+ 和 +=

l1 = [1, 2, 3, 4, 5]
l2 = l1
# +操作是先将[1, 2, 3, 4, 5]和[6, 7, 8]相加,将结果的引用赋值给l2
l2 = l2 + [6, 7, 8]
print(l1)  # ==>[1, 2, 3, 4, 5]
print(l2)  # ==>[1, 2, 3, 4, 5, 6, 7, 8]
print(id(l1) == id(l2))  # ==>False

l1 = [1, 2, 3, 4, 5]
l2 = l1
# +=操作是就地在l2后面加[6, 7, 8]
l2 += [6, 7, 8]
print(l1)  # ==>[1, 2, 3, 4, 5, 6, 7, 8]
print(l2)  # ==>[1, 2, 3, 4, 5, 6, 7, 8]
print(id(l1) == id(l2))  # ==>True

常用方法

l1 = ['1', '2', '3', '4', '1']
l1.count('1')  # 统计某个元素出现的次数
l1.index('1')  # 找出某个元素的第一次出现的位置
l1.reverse()  # 将元素进行反转
l1.sort(reverse=True)  # 对列表进行反向排序,列表被改变,返回值是空
l1.append('5')  # 向末尾添加一个元素
l1.insert(3, '6')  # 向指定索引位置添加一个元素
l1.extend(['1', '2'])  # 向末尾添加,等同于 li + ['1','2']
l1.pop(3)  # 删除指定索引位置的一个元素,默认末尾
l1.remove('1')  # 移除列表中的第一个匹配元素
l1.clear()  # 清空所有元素

# split和join
print("1,2,3,4,5".split(','))  # ==>['1', '2', '3', '4', '5']
# join参数中的元素不能为数字
print(",".join(['1', '2', '3', '4', '5']))  # ==>1,2,3,4,5

列表推导式和生成器表达式

列表推导是构建列表(list) 的快捷方式, 而生成器表达式则可以用来创建其他任何类型的序列。 如果你的代码里并不经常使用它们, 那么很可能你错过了许多写出可读性更好且更高效的代码的机会。

易读性

# 把一个字符串变成 Unicode 码位的列表
symbols = '$¢£¥€¤'
code = []
for symbol in symblos:
    codes.append(ord(symbol))
    
# 等价于
symbols = '$¢£¥€¤'
code = [ord(symbol) for symbol in symbols]

生成器表达式

虽然也可以用列表推导来初始化元组、 数组或其他序列类型, 但是生成器表达式是更好的选择。 这是因为生成器表达式背后遵守了迭代器协议, 可以逐个地产出元素, 而不是先建立一个完整的列表, 然后再把这个列表传递到某个构造函数里。 前面那种方式显然能够节省内存。

生成器表达式的语法跟列表推导差不多, 只不过把方括号换成圆括号而已。

symbols = '$¢£¥€¤'
tuple(ord(symbol) for symbol in symbols)
[ord(symbol) for symbol in symbols if ord(symbol) > 0]

列表推导同filter和map的比较

filter 和 map 合起来能做的事情, 列表推导也可以做, 而且还不需要借助难以理解和阅读的 lambda 表达式。

# 选出所有大于127的Unicode 码位
symbols = '$¢£¥€¤'
beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))  
# beyond_ascii==>[162, 163, 165, 8364, 164]
# 等价于
beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]

切片

像列表(list)、元组(tuple) 和字符串(str) 这类序列类型都支持切片操作, 但是实际上切片操作比人们所想象的要强大很多。

对对象进行切片

s[a:b:c] 的形式对 s 在 a 和 b之间以 c 为间隔取值。 c 的值还可以为负, 负值意味着反向取值。

s = 'bicycle'
print(s[::3])  # ==>bye
print(s[::-1])  # ==>elcycib
print(s[::-2])  # ==>eccb

切片原理

a:b:c这种用法只能作为索引或者下标用在 [] 中来返回一个切片对象: slice(a, b, c)。

seq[start:stop:step]进行求值的时候, Python 会调用seq.__getitem__(slice(start, stop, step))

class MySeq:
    def __getitem__(self, index):
        return index
s = MySeq()
print(s[1])  # ==>1
print(s[1:4])  # ==>slice(1, 4, None)
print(s[1:4:2, 7:9])  # ==>(slice(1, 4, 2), slice(7, 9, None))

通过审查 slice 它有 start、 stop 和 step 数据属性, 以及indices 方法。

# S.indices(len) -> (start, stop, stride) 
# 给定长度为 len 的序列, 计算 S 表示的扩展切片的起始(start)和结尾(stop) 索引, 以及步幅(stride) 。 超出边界的索引会被截掉, 这与常规切片的处理方式一样。 

# 假设有个长度为 5 的序列, 例如 'ABCDE'
# 'ABCDE'[:10:2] 等同于 'ABCDE'[0:5:2]
slice(None, 10, 2).indices(5)  # ==>(0, 5, 2)
# 'ABCDE'[-3:] 等同于 'ABCDE'[2:5:1]
slice(-3, None, None).indices(5)  # ==>(2, 5, 1)

给切片赋值

如果把切片放在赋值语句的左边, 或把它作为 del 操作的对象, 我们就可以对序列进行嫁接、 切除或就地修改操作。

ps : 赋值语句右边必须是一个可迭代对象

l = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l[2:5] = [20, 30]   # [0, 1, 20, 30, 5, 6, 7, 8, 9]
del l[5:7]          # [0, 1, 20, 30, 5, 8, 9]
l[3::2] = [11, 22]  # [0, 1, 20, 11, 5, 22, 9]

元组

元组除了用作不可变的列表, 它还可以用于没有字段名的记录。

# 经纬度
lax_coordinates = (33.9425, -118.408056) 
# 东京市的一些信息:市名、年份、人口(单位: 百万)、人口变化(单位: 百分比)和面积(单位: 平方千米)。
city, year, pop, chg, area = ('Tokyo', 2003, 32450, 0.66, 8014) 

元组拆包

# 简单的拆包
lax_coordinates = (33.9425, -118.408056)
latitude, longitude = lax_coordinates # 元组拆包

# 交换变量的值
a, b = 1, 2
a, b = b, a

# 用 * 运算符把一个可迭代对象拆开作为函数的参数
# 20除8 等于 2余4
print(divmod(20, 8))  #==>(2, 4)
t = (20, 8)
print(divmod(*t))  #==>(2, 4)

# 使用_占位符
_, b = (1, 2)
print(b)  #==>2

# Python3 在平行赋值中使用*args
a, b, *rest = range(5)  # (0, 1, [2, 3, 4])
a, b, *rest = range(3)  # (0, 1, [2])
a, b, *rest = range(2)  # (0, 1, [])
a, *body, c, d = range(5)  # (0, [1, 2], 3, 4)
*head, b, c, d = range(5)  # ([0, 1], 2, 3, 4)

# 嵌套元组拆包
(a, b, (c, d)) = (1, 2, (3, 4))

具名元组

collections.namedtuple 是一个工厂函数, 它可以用来构建一个带字段名的元组和一个有名字的类——这个带名字的类对调试程序有很大帮助

from collections import namedtuple
City = namedtuple('City', 'name country population coordinates')
tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
print(tokyo)
# ==>City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722,139.691667))
print(tokyo.population)
# ==>36.933
print(tokyo.coordinates)
# ==>(35.689722, 139.691667)

字典

字典这个数据结构活跃在所有 Python 程序的背后, 即便你的源码里并没有直接用到它。

Map UML

在这里插入图片描述

字典推导

字典推导(dictcomp) 可以从任何以键值对作为元素的可迭代对象中构建出字典。

DIAL_CODES = [
    (86, 'China'),
    (91, 'India'),
    (1, 'United States'),
    (62, 'Indonesia'),
    (55, 'Brazil'),
    (92, 'Pakistan'),
    (880, 'Bangladesh'),
    (234, 'Nigeria'),
    (7, 'Russia'),
    (81, 'Japan'),
]
country_code = {country: code for code, country in DIAL_CODES}
# {'China': 86, 'India': 91, 'Bangladesh': 880, 'United States': 1,'Pakistan': 92,'Japan': 81, 'Russia': 7, 'Brazil': 55, 'Nigeria':234, 'Indonesia': 62}
x = {code: country.upper() for country, code in country_code.items() if code < 66}
# {1: 'UNITED STATES', 55: 'BRAZIL', 62: 'INDONESIA', 7: 'RUSSIA'}

dict、 defaultdict 和 OrderedDict

后面两个数据类型是 dict 的变种, 位于 collections 模块内。

不同方法

dictdefaultdictOrderedDict说明
d.__copy__()用于支持 copy.copy
d.default_factory__missing__函数中被调用的函数, 用以给未找到的元素设置值
d.__missing__(k)__getitem__找不到对应键的时候, 这个方法会被调用
d.move_to_end(k,[last])把键为 k 的元素移动到最靠前或者最靠后的位置(last 的默认值是 True)
d.__reversed__()返回倒序的键的迭代器
  • default_factory 并不是一个方法, 而是一个可调用对象( callable) , 它的值在defaultdict 初始化的时候由用户设定。

相同方法

方法说明
d.clear()移除所有元素
d.__contains__(k)检查 k 是否在 d 中
d.copy()浅复制
d.fromkeys(it,[initial])将迭代器 it 里的元素设置为映射里的键, 如果有 initial 参数,
就把它作为这些键对应的值(默认是 None)
d.get(k,[default])没有键 k, 则返回 None 或者default
d.items()返回 d 里所有的键值对
d.__iter__()获取键的迭代器
d.keys()获取所有的键
d.pop(k, [defaul])返回键 k 所对应的值, 然后移除这个键值对。 如果没有这
个键,返回 None 或者 defaul
d.popitem()随机返回一个键值对并从字典里移除它
d.setdefault(k,[default])若字典里有键k, 则把它对应的值设置为 default,
然后返回这个值; 若无, 则让 d[k] =default, 然后返回 default
d.update(m,[**kargs])m 可以是映射或者键值对迭代器, 用来更新 d 里对应的条目
d.values()返回字典里的所有值
  • OrderedDict.popitem() 会移除字典里最先插入的元素( 先进先出) ; 同时这个方法还有一个可选的 last 参数, 若为真, 则会移除最后插入的元素( 后进先出) 。

用setdefault处理找不到的键 (用于更新操作)

当字典 d[k] 不能找到正确的键的时候, Python 会抛出异常, 这个行为符合 Python 所信奉的“快速失败”哲学。

my_dict.setdefault(key, []).append(new_value)
# 跟这样写
if key not in my_dict:
	my_dict[key] = []
my_dict[key].append(new_value)
#二者的效果是一样的,只不过后者至少要进行两次键查询——如果键不存在的话,就是三次,用 setdefault 只需要一次就可以完成整个操作

映射的弹性键查询

有时候为了方便起见, 就算某个键在映射里不存在, 我们也希望在通过这个键读取值的时候能得到一个默认值。

**defaultdict: 处理找不到的键的一个选择 **

在用户创建 defaultdict 对象的时候, 就需要给它配置一个为找不到的键创造默认值的方法。

'''
比如,我们新建了这样一个字典:dd = defaultdict(list),如果键'new-key' 在 dd 中还不存在的话,表达式 dd['new-key'] 会按照以下的步骤来行事。
(1) 调用 list() 来建立一个新列表。
(2) 把这个新列表作为值, 'new-key' 作为它的键, 放到 dd 中。
(3) 返回这个列表的引用。
而这个用来生成默认值的可调用对象存放在名为 default_factory 的实例属性里。
'''
d = collections.defaultdict(list)
print(d.default_factory)  # ==><class 'list'>
print(d['key'])  # ==>[]
  • defaultdict 里的 default_factory 只会在__getitem__里被调用,get(key)则会返回 None。

特殊方法__missing__

所有的映射类型在处理找不到的键的时候, 都会牵扯到__missing__方法。 这也是这个方法称作“missing”的原因。 虽然基类 dict 并没有定义这个方法, 但是 dict 是知道有这么个东西存在的。

# 在查询的时候把非字符串的键转换为字符串
class StrKeyDict0(dict):
	def __missing__(self, key):
		if isinstance(key, str): 
            raise KeyError(key)
		return self[str(key)]
	def get(self, key, default=None):
		try:
			return self[key]
	except KeyError:
		return default 
def __contains__(self, key):
	return key in self.keys() or str(key) in self.keys()
  • __missing__ 方法只会被__getitem__调用(比如在表达式 d[k] 中)

  • k in my_dict.keys()这种操作在 Python 3 中是很快的, 而且即便映射类型对象很庞大也没关系。 这是因为dict.keys()的返回值是一个“视图”。 视图就像一个集合, 而且跟字典类似的是, 在视图里查找一个元素的速度很快。

字典的变种

**collections.OrderedDict **

这个类型在添加键的时候会保持顺序, 因此键的迭代次序总是一致的。 OrderedDict 的 popitem方法默认删除并返回的是字典里的最后一个元素, 但是如果像 my_odict.popitem(last=False)这样调用它, 那么它删除并返回第一个被添加进去的元素。

**collections.ChainMap **

该类型可以容纳数个不同的映射对象, 然后在进行键查找操作的时候, 这些对象会被当作一个整体被逐个查找, 直到键被找到为止。 这个功能在给有嵌套作用域的语言做解释器的时候很有用, 可以用一个映射对象来代表一个作用域的上下文。

**collections.Counter **

这个映射类型会给键准备一个整数计数器。 每次更新一个键的时候都会增加这个计数器。 所以这个类型可以用来给可散列表对象计数, 或者是当成多重集来用——多重集合就是集合里的元素可以出现不止一次。 Counter 实现了 + 和 - 运算符用来合并记录, 还有像most_common([n])这类很有用的方法。

ct = collections.Counter('abracadabra')
# Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
ct.update('aaaaazzz')
# Counter({'a': 10, 'z': 3, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
ct.most_common(2)
# [('a', 10), ('z', 3)]

**colllections.UserDict **

这个类其实就是把标准 dict 用纯 Python 又实现了一遍。跟 OrderedDict、 ChainMap 和 Counter 这些开箱即用的类型不同, UserDict 是让用户继承写子类的。而更倾向于从 UserDict 而不是从 dict 继承的主要原因是, 后者有时会在某些方法的实现上走一些捷径, 导致我们不得不在它的子类中重写这些方法, 但是 UserDict 就不会带来这些问题。

不可变映射类型

从 Python 3.3 开始, types 模块中引入了一个封装类名叫MappingProxyType。 如果给这个类一个映射, 它会返回一个只读的映射视图。 虽然是个只读视图, 但是它是动态的。 这意味着如果对原映射做出了改动, 我们通过这个视图可以观察到, 但是无法通过这个视图对原映射做出修改。

d = {1: 'A'}
d_proxy = MappingProxyType(d)
print(d_proxy[1])  # ==>A
# 不可修改
d_proxy[2] = 'x'
# TypeError: 'mappingproxy' object does not support item assignment

集合

“集”这个概念在 Python 中算是比较年轻的, 同时它的使用率也比较低。

集合 UML

在这里插入图片描述

set 和 frozenset

  • set无序排序且不重复,是可变的,有add(),remove()等方法。

  • frozenset是冻结的集合,它是不可变的,存在哈希值,好处是它可以作为字典的key,也可以作为其它集合的元素。缺点是一旦创建便不能更改,没有add,remove方法。

集合字面量

除空集之外, 集合的字面量——{1}、 {1, 2}, 等等——看起来跟它的数学形式一模一样。 如果是空集, 那么必须写成 set() 的形式。

  • 只是写成 {} 的形式, 跟以前一样, 你创建的其实是个空字典。

集合推导

s = {chr(i) for i in range(32, 40)}
# {'!', '#', '"', '$', '%', "'", ' ', '&'}

集合的操作

集合的数学运算

数学符号Python运算符描述
S ∩ Zs & zs 和 z 的交集
S ∪ Zs | zs 和 z 的并集
S - Zs - zs 和 z 的差集, 或者叫作相对补集
S △ Zs ^ zs 和 z 的对称差集

集合的比较运算符 :

数学符号Python运算符描述
e ∈ Se in s元素 e 是否属于 s
S ⊆ Zs <= zs 是否为 z 的子集
S ⊂ Zs < zs 是否为 z 的真子集
S ⊇ Zs >= zs 是否为 z 的父集
S ⊃ Zs > zs 是否为 z 的真父集

dict和set的背后

想要理解 Python 里字典和集合类型的长处和弱点, 它们背后的散列表是绕不开的一环。

dict的实现及其导致的结果

  • 键必须是可散列的

    • 支持 hash()函数, 并且通过 __hash__()方法所得到的散列值是不变的。
    • 支持通过__eq__()方法来检测相等性。
    • a == b 为真, 则hash(a) == hash(b)也为真。
  • 字典在内存上的开销巨大

    • 字典使用了散列表, 而散列表又必须是稀疏的, 这导致它在空间上的效率低下。
    • 如果需要存放数量巨大的记录, 那么放在由元组或是具名元组构成的列表中会是比较好的选择 。
  • 键查询很快

    • dict 的实现是典型的空间换时间: 字典类型有着巨大的内存开销, 但它们提供了无视数据量大小的快速访问——只要字典能被装在内存里。
  • 键的次序取决于添加顺序

    • 往 dict 里添加新键而又发生散列冲突的时候, 新键可能会被安排存放到另一个位置。
  • 往字典里添加新键可能会改变已有键的顺序

    • 无论何时往字典里添加新的键, Python 解释器都可能做出为字典扩容的决定。 这个过程中可能会发生新的散列冲突, 导致新散列表中键的次序变化。

ps: 在 Python 3 中, .keys()、 .items() 和 .values() 方法返回的都是字典视图。

set的实现以及导致的结果

set 和 frozenset 的实现也依赖散列表, 但在它们的散列表里存放的只有元素的引用 。

上面所提到的字典和散列表的几个特点, 对集合来说几乎都是适用的。

  • 集合里的元素必须是可散列的。
  • 集合很消耗内存。
  • 可以很高效地判断元素是否存在于某个集合。
  • 元素的次序取决于被添加到集合里的次序。
  • 往集合里添加元素, 可能会改变集合里已有元素的次序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值