数字Number:
命令
整数的情况
- 1
- 1+1
- 1*1
浮点数的情况
- 0.1
- 1*0.1
- 2/2
注意:2/2为浮点类型不是整数,如果要整形则写成2//2这个方式为整除
布尔值
为什么布尔值为数字类型?
答:因为True可以转换为10进制1,False可以转换为10进制的0。
什么情况为True什么情况为False?
所有空的情况,空字符串,空数组等还有0转换为Bool类型后都为False
所有非零或者非空的情况下转换为Bool类型都为True
复数
所有数字后面加上j(例:32j)
进制转换
二进制
表示方法:0b10
别的进制转换为二进制方法:bin(oxE)
八进制
表示方法:0o10
别的进制转换为二进制方法:oct(oxE)
十进制
十六进制
表示方法:0x1F
别的进制转换为二进制方法:hex(oxE)
序列
字符串
怎么输出 let’s go ?
答:1. ‘let\’s go’ (转义字符) 2. “let’s go”
三引号
适用于多行输入 (多用于函数的注释)
例:输入时候带回车""" aaa
aaa """
会返回带转义字符\n: ' aaa\naaa'
如果想表现出换行效果 使用print函数: print(' aaa\naaa')
原始字符串
print(r’xxx\nxxx’) 输出为’xxx\n xxx’不会被转义,r代表原始字符串意思是字符串为本身不需要转义。
转义字符
s = 'a\nb\tc'
print(s)
a
b c
# 最后字符串有5个元素
字符串的运算
拼接字符串
“hello” + “world” => “helloworld”
“hello”
成倍出现字符串
截取字符串中的自符
从前往后截取,索引从0开始
从后往前截取,索引从1开始
在”hello world”中截取w字母"hello world"[6]
或者"hello world"[-5]
从字符串中截取一段字符串
“hello world” 中截取world"hello world"[6:11]
注意:截取从索引6到索引11-1"hello world"[6:]
:后面没数字表示截取到最后一个字符,如果:前面没数字同理前面取字符串的第一个字符开始
遍历字符串
name = 'Andrew'
for c in name:
print(c)
字符串不可变,只能通过创建新的字符串(改变字符串需要O(N))
name = 'Andrew'
name[0]='H' #这样操作会报错
#两种方法将Andrew改成Hndrew
name= 'H'+ name[1:]
s=s.replace('a','H') # replace将 第一个参数oldvalue替换成第二个参数newvalue
自从 Python2.5 开始,每次处理字符串的拼接操作时(str1 += str2),Python 首先会检测 str1 还有没有其他的引用。如果没有的话,就会尝试原地扩充字符串 buffer 的大小,而不是重新分配一块内存来创建新的字符串并拷贝。这样的话,上述例子中的时间复杂度就由O(n2)变为 O(n) 了。
使用字符串函数
拼接函数
string.join(iterable)
把每一个元素按照格式拼接起来
l=[]
for n in range(0,1000):
l.append(str(n))
l=''.join(l) #总体时间复杂度为O(n)
拆分函数
string.split(separator),表示把字符串按照 separator 分割成子字符串,并返回一个分割后子字符串组合的列表。
def query_data(namespace,table):
"""
given namespace and table, query database to get corresponding data
"""
path='hive://ads/training_table'
namespace=path.split('//')[1].split('/')[0] # 返回ads
data = query_data(namespace, table)
string.strip(str),表示去掉首尾的 str 字符串;
string.lstrip(str),表示只去掉开头的 str 字符串;
string.rstrip(str),表示只去掉尾部的 str 字符串。
查找函数
string.find(sub, start, end),表示从 start 到 end 查找字符串中子字符串 sub 的位置
字符串的格式化
通常,我们使用一个字符串作为模板,模板中会有格式符。这些格式符为后续真实值预留位置,以呈现出真实值应该呈现的格式。通常用于日志log中
print('no data available for person with id: {}, name: {}'.format(id, name))
其中的 string.format(),就是所谓的格式化函数;而大括号{}就是所谓的格式符,用来为后面的真实值——变量 name 预留位置。
思考题
对于python(2.5+)中下面两个字符串拼接操作哪个更优?
s = ''
for n in range(0, 100000):
s += str(n)
l = []
for n in range(0, 100000):
l.append(str(n))
s = ' '.join(l)
答:如果字符串拼接的次数较少,比如range(100),那么方法一更优,因为时间复杂度精确的来说第一种是O(n),第二种是O(2n),如果拼接的次数较多,比如range(1000000),方法二稍快一些,虽然方法二会遍历两次,但是join的速度其实很快,列表append和join的开销要比字符串+=小一些。
列表
长度大小不固定,可以随意地增加、山间改变元素
[列表元素1,列表元素2…]
同一个列表的不同元素可以是不同数据结构
例: type([1,False,0.1,"Andrew"])
也是列表类型
同时列表可以嵌套
例: type([[1,False],[2,2],["Andrew",4]])
此为嵌套列表
列表的基本操作
追加 ["andrew"]+["anna"]
追加列表所有内容三倍 ["Andrew"]*3
删除不能使用减号
列表的抓取元素操作和之前的字符串运算一样
常用内置函数
l = [3, 2, 3, 7, 8, 1]
l.count(3)
2
l.index(7)
3
l.reverse()
l
[1, 8, 7, 3, 2, 3]
l.sort()
l
[1, 2, 3, 3, 7, 8]
- count(item) 表示统计列表 / 元组中 item 出现的次数。i
- ndex(item) 表示返回列表 / 元组中 item 第一次出现的索引。
- list.reverse() 和 list.sort() 分别表示原地倒转列表和排序(注意,元组没有内置的这两个函数)。
- reversed() 和 sorted() 同样表示对列表 / 元组进行倒转和排序,reversed() 返回一个倒转后的迭代器(上文例子使用 list() 函数再将其转换为列表);sorted() 返回排好序的新列表。
列表内部的实现
一个数组,容量为allocated,
allocated意思是容量多少,不等于元素的个数,因为它包含了数组不想频繁分配空间从而会预先分配的空间
ob_size:实际数组内元素的个数,即len(list)
allocated >= len(list) = ob_size
如果当前列表分配的空间已满(即 allocated == len(list)),则会向系统请求更大的内存空间,并把原来的元素全部拷贝过去。
元组
长度大小固定,无法增加山间或者改变
括号括起来
type((1)) 为int,因为(1)中的括号是一个运算符号运算出来后为(1),如果想要表示只有一个元素的元组必须要(1,)
加上一个逗号。
常用内置函数
tup = (3, 2, 3, 7, 8, 1)
tup.count(3)
2
tup.index(7)
3
list(reversed(tup))
[1, 8, 7, 3, 2, 3]
sorted(tup) #注意返回的是list
[1, 2, 3, 3, 7, 8]
- count(item) 表示统计列表 / 元组中 item 出现的次数。i
- ndex(item) 表示返回列表 / 元组中 item 第一次出现的索引。
- list.reverse() 和 list.sort() 分别表示原地倒转列表和排序(注意,元组没有内置的这两个函数)。
- reversed() 和 sorted() 同样表示对列表 / 元组进行倒转和排序,reversed() 返回一个倒转后的迭代器(上文例子使用 list() 函数再将其转换为列表);sorted() 返回排好序的新列表。
序列总结
str,list,tuple都是序列类型
字符串和元组为不可变类型
序列每一个元素都有一个序号(下标)[1,2,3,4,5][-1:]
序列的操作
切片(截取)
判断
3是否在[1,2,3,4]中?3 in [1,2,3,4]
3是否不在[1,2,3,4]中?3 not in [1,2,3,4]
max() min() len() 对于数字序列和字符串序列都可以使用,这些函数也对基本数据类型管用min("hello world") =》" "
列表和元素的存储方式差异
区别
l = [1, 2, 3]
l.__sizeof__()
64
tup = (1, 2, 3)
tup.__sizeof__()
48
由于列表是动态的,所以它需要存储指针,来指向对应的元素(上述例子中,对于 int 型,8 字节)。另外,由于列表可变,所以需要额外存储已经分配的长度大小(8 字节),这样才可以实时追踪列表空间的使用情况,当空间不足时,及时分配额外空间。
l = []
l.__sizeof__() // 空列表的存储空间为40字节
40
l.append(1)
l.__sizeof__()
72 // 加入了元素1之后,列表为其分配了可以存储4个元素的空间 (72 - 40)/8 = 4
l.append(2)
l.__sizeof__()
72 // 由于之前分配了空间,所以加入元素2,列表空间不变
l.append(3)
l.__sizeof__()
72 // 同上
l.append(4)
l.__sizeof__()
72 // 同上
l.append(5)
l.__sizeof__()
104 // 加入元素5之后,列表的空间不足,所以又额外分配了可以存储4个元素的空间
为了减小每次增加 / 删减操作时空间分配的开销,Python 每次分配空间时都会额外多分配一些,这样的机制(over-allocating)保证了其操作的高效性:增加 / 删除的时间复杂度均为 O(1)。
列表和元祖的性能
元组要比列表更加轻量级一些,所以总体上来说,元组的性能速度要略优于列表。
对于一些静态变量,比如元组,如果它不被使用并且占用空间不大时,Python 会暂时缓存这部分内存。这样,下次我们再创建同样大小的元组时,Python 就可以不用再向操作系统发出请求,去寻找内存,而是可以直接分配之前缓存的内存空间,这样就能大大加快程序的运行速度。
创建相同的元素情况下 元祖比列表块
索引操作时 两者速递差别不大,几乎可以忽略不计
增加、删减、改变元素使用列表,因为元祖要通过新建一个元组来完成
使用场景:
1. 如果存储的数据和数量不变,比如你有一个函数,需要返回的是一个地点的经纬度,然后直接传给前端渲染,那么肯定选用元组更合适。
def get_location():
.....
return (longitude, latitude)
- 如果存储的数据或数量是可变的,比如社交平台上的一个日志功能,是统计一个用户在一周之内看了哪些用户的帖子,那么则用列表更合适。
viewer_owner_id_list = [] # 里面的每个元素记录了这个viewer一周内看过的所有owner的id
records = queryDB(viewer_id) # 索引数据库,拿到某个viewer一周内的日志
for record in records:
viewer_owner_id_list.append(record.id)
面试题
```python创建空列表,这两种效率上有区别吗?优先考虑使用哪种?
option A
empty_list = list()
option B
empty_list = []
答:区别主要在于list()是一个function call,Python的function call会创建stack,并且进行一系列参数检查的操作,比较expensive,反观[]是一个内置的C函数,可以直接被调用,因此效率高
<a name="WuOmC"></a>
# 集合(Set)
集合是无序,并且所有元素是不重复的
<a name="jPIe2"></a>
## 创建集合方式
```python
s1 = {1, 2, 3}
s2 = set([1, 2, 3])
s1 == s2
True
s = {1, 'hello', 5.0} # 支持混合类型
集合操作
集合并不支持索引操作,因为集合本质上是一个哈希表,和列表不一样。__集合不支持切片或者下标访问元素。
# 判断set中是否存在该元素
s = {1, 2, 3}
1 in s
True
10 in s
False
s = {1, 2, 3}
s.add(4) # 增加元素4到集合
s
{1, 2, 3, 4}
s.remove(4) # 从集合中删除元素4
s
{1, 2, 3}
#排序
s = {3, 4, 2, 1}
sorted(s) # 对集合的元素进行升序排序
[1, 2, 3, 4]
# 差集:
{1,2,3,4,5,6} - {3,4} 结果为{1,2,5,6} 在集合中剔除几个元素使用“-”
# 交集
{1,2,3,4} & {3,5} #结果为 {3}
# 添加元素并且不重复(不能使用“+”)
{1,2} | {3} #结果为{1,2,3}
注意:集合的 pop() 操作是删除集合中最后一个元素,可是集合本身是无序的,你无法知道会删除哪个元素,因此这个操作得谨慎使用。
性能
字典(dict)
很多个key和value,但是key不能重复,key必须是不可变的类型(不能是变量,不能是列表因为列表里面内容可以改变),value可以是任意类型
创建字典方式
d1 = {'name': 'jason', 'age': 20, 'gender': 'male'}
d2 = dict({'name': 'jason', 'age': 20, 'gender': 'male'})
d3 = dict([('name', 'jason'), ('age', 20), ('gender', 'male')])
d4 = dict(name='jason', age=20, gender='male')
d1 == d2 == d3 ==d4
True
字典元素访问
# 根据key得到value,key不存在会抛异常
d = {'name': 'jason', 'age': 20}
d['name']
'jason'
# 通过get(key,default)函数获取索引,会返回一个默认值
d= {'name': 'jason', 'age': 20}
d.get('name')
'jason'
d.get('location', 'null')
'null'
字典元素操作
# 判断字典哪是否存在该key
d = {'name': 'jason', 'age': 20}
'name' in d
True
'location' in d
False
d = {'name': 'jason', 'age': 20}
d['gender'] = 'male' # 增加元素对'gender': 'male'
d['dob'] = '1999-02-01' # 增加元素对'dob': '1999-02-01'
d
{'name': 'jason', 'age': 20, 'gender': 'male', 'dob': '1999-02-01'}
d['dob'] = '1998-01-01' # 更新键'dob'对应的值
d.pop('dob') # 删除键为'dob'的元素对
'1998-01-01'
d
{'name': 'jason', 'age': 20, 'gender': 'male'}
根据key或者value排序
d = {'b': 1, 'a': 2, 'c': 10}
d_sorted_by_key = sorted(d.items(), key=lambda x: x[0]) # 根据字典键的升序排序
d_sorted_by_value = sorted(d.items(), key=lambda x: x[1]) # 根据字典值的升序排序
d_sorted_by_key
[('a', 2), ('b', 1), ('c', 10)] # 列表中的每个元素,是由原字典的键和值组成的元组
d_sorted_by_value
[('b', 1), ('a', 2), ('c', 10)]
字典底层工作原理(dict & set)
哈希值+key+value
数据结构:
Indices
----------------------------------------------------
None | index | None | None | index | None | index ...
----------------------------------------------------
Entries
--------------------
hash0 key0 value0
---------------------
hash1 key1 value1
---------------------
hash2 key2 value2
---------------------
...
---------------------
# 哈希表结构存储形式
indices = [None, 1, None, None, 0, None, 2]
entries = [
[1231236123, 'name', 'mike'],
[-230273521, 'dob', '1999-01-01'],
[9371539127, 'gender', 'male']
]
插入操作
- 计算key的hash值
- 计算索引:hash 值 & mask(mask=PyDicMinSize-1)(等同于 hash值 % PyDicMinSize)
- 这一步为了防止空间过于浪费,将索引控制在0~PyDicMinSize区间
- 例如 两个key但是一个存储在索引为1的位置一个存储在索引为100w的位置
- 这一步为了防止空间过于浪费,将索引控制在0~PyDicMinSize区间
插入元素
计算key的hash值
- 计算索引:hash 值 & mask(mask=PyDicMinSize-1)(等同于 hash值 % PyDicMinSize)
- 这一步为了防止空间过于浪费,将索引控制在0~PyDicMinSize区间
- 例如 两个key但是一个存储在索引为1的位置一个存储在索引为100w的位置
- 这一步为了防止空间过于浪费,将索引控制在0~PyDicMinSize区间
- 根据之前算下来的索引位置去查找hash值和key是否相同
- 相同则返回
- 不同则继续查找,知道找到空位抛出异常
插入操作
对于删除操作,Python 会暂时对这个位置的元素,赋于一个特殊的值,等到重新调整哈希表的大小时,再将其删除。
哈希冲突的发生,往往会降低字典和集合操作的速度。因此,为了保证其高效性,字典和集合内的哈希表,通常会保证其至少留有 1/3 的剩余空间。随着元素的不停插入,当剩余空间小于 1/3 时,Python 会重新获取更大的内存空间,扩充哈希表。不过,这种情况下,表内所有的元素位置都会被重新排放。