数字Number:

整数+浮点数+复数+布尔类型

命令

type()查看括号内的数据类型

整数的情况

  • 1
  • 1+1
  • 1*1

浮点数的情况

  • 0.1
  • 1*0.1
  • 2/2

注意:2/2为浮点类型不是整数,如果要整形则写成2//2这个方式为整除

布尔值

True和False(首字母必须大写)

为什么布尔值为数字类型?

答:因为True可以转换为10进制1,False可以转换为10进制的0。

什么情况为True什么情况为False?

所有空的情况,空字符串,空数组等还有0转换为Bool类型后都为False
所有非零或者非空的情况下转换为Bool类型都为True

复数

所有数字后面加上j(例:32j)

进制转换

二进制

表示方法:0b10
别的进制转换为二进制方法:bin(oxE)

八进制

表示方法:0o10
别的进制转换为二进制方法:oct(oxE)

十进制

表示方法:5
别的进制转换为二进制方法:int(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代表原始字符串意思是字符串为本身不需要转义。

转义字符

image.png

  1. s = 'a\nb\tc'
  2. print(s)
  3. a
  4. b c
  5. # 最后字符串有5个元素

字符串的运算

拼接字符串

“hello” + “world” => “helloworld”
“hello”

成倍出现字符串

“hello” * 3 =>hellohellohello

截取字符串中的自符

从前往后截取,索引从0开始
从后往前截取,索引从1开始
在”hello world”中截取w字母
"hello world"[6]或者"hello world"[-5]

从字符串中截取一段字符串

“hello world” 中截取world
"hello world"[6:11]注意:截取从索引6到索引11-1
"hello world"[6:] :后面没数字表示截取到最后一个字符,如果:前面没数字同理前面取字符串的第一个字符开始

遍历字符串

  1. name = 'Andrew'
  2. for c in name:
  3. print(c)

字符串不可变,只能通过创建新的字符串(改变字符串需要O(N))

  1. name = 'Andrew'
  2. name[0]='H' #这样操作会报错
  3. #两种方法将Andrew改成Hndrew
  4. name= 'H'+ name[1:]
  5. s=s.replace('a','H') # replace将 第一个参数oldvalue替换成第二个参数newvalue

自从 Python2.5 开始,每次处理字符串的拼接操作时(str1 += str2),Python 首先会检测 str1 还有没有其他的引用。如果没有的话,就会尝试原地扩充字符串 buffer 的大小,而不是重新分配一块内存来创建新的字符串并拷贝。这样的话,上述例子中的时间复杂度就由O(n2)变为 O(n) 了。

使用字符串函数

拼接函数

string.join(iterable)
把每一个元素按照格式拼接起来

  1. l=[]
  2. for n in range(0,1000):
  3. l.append(str(n))
  4. l=''.join(l) #总体时间复杂度为O(n)

拆分函数

string.split(separator),表示把字符串按照 separator 分割成子字符串,并返回一个分割后子字符串组合的列表。

  1. def query_data(namespace,table):
  2. """
  3. given namespace and table, query database to get corresponding data
  4. """
  5. path='hive://ads/training_table'
  6. namespace=path.split('//')[1].split('/')[0] # 返回ads
  7. 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中

  1. print('no data available for person with id: {}, name: {}'.format(id, name))

其中的 string.format(),就是所谓的格式化函数;而大括号{}就是所谓的格式符,用来为后面的真实值——变量 name 预留位置。

思考题

对于python(2.5+)中下面两个字符串拼接操作哪个更优?

  1. s = ''
  2. for n in range(0, 100000):
  3. s += str(n)
  1. l = []
  2. for n in range(0, 100000):
  3. l.append(str(n))
  4. 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
删除不能使用减号
列表的抓取元素操作和之前的字符串运算一样

常用内置函数

  1. l = [3, 2, 3, 7, 8, 1]
  2. l.count(3)
  3. 2
  4. l.index(7)
  5. 3
  6. l.reverse()
  7. l
  8. [1, 8, 7, 3, 2, 3]
  9. l.sort()
  10. l
  11. [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,)加上一个逗号。

常用内置函数

  1. tup = (3, 2, 3, 7, 8, 1)
  2. tup.count(3)
  3. 2
  4. tup.index(7)
  5. 3
  6. list(reversed(tup))
  7. [1, 8, 7, 3, 2, 3]
  8. sorted(tup) #注意返回的是list
  9. [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:]

序列的操作

切片(截取)

[1,2,3,4,5][0:4]截取从索引0到索引3的

判断

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") =》" "

列表和元素的存储方式差异

区别

  1. l = [1, 2, 3]
  2. l.__sizeof__()
  3. 64
  4. tup = (1, 2, 3)
  5. tup.__sizeof__()
  6. 48

由于列表是动态的,所以它需要存储指针,来指向对应的元素(上述例子中,对于 int 型,8 字节)。另外,由于列表可变,所以需要额外存储已经分配的长度大小(8 字节),这样才可以实时追踪列表空间的使用情况,当空间不足时,及时分配额外空间。

  1. l = []
  2. l.__sizeof__() // 空列表的存储空间为40字节
  3. 40
  4. l.append(1)
  5. l.__sizeof__()
  6. 72 // 加入了元素1之后,列表为其分配了可以存储4个元素的空间 (72 - 40)/8 = 4
  7. l.append(2)
  8. l.__sizeof__()
  9. 72 // 由于之前分配了空间,所以加入元素2,列表空间不变
  10. l.append(3)
  11. l.__sizeof__()
  12. 72 // 同上
  13. l.append(4)
  14. l.__sizeof__()
  15. 72 // 同上
  16. l.append(5)
  17. l.__sizeof__()
  18. 104 // 加入元素5之后,列表的空间不足,所以又额外分配了可以存储4个元素的空间

为了减小每次增加 / 删减操作时空间分配的开销,Python 每次分配空间时都会额外多分配一些,这样的机制(over-allocating)保证了其操作的高效性:增加 / 删除的时间复杂度均为 O(1)。

列表和元祖的性能

元组要比列表更加轻量级一些,所以总体上来说,元组的性能速度要略优于列表。
对于一些静态变量,比如元组,如果它不被使用并且占用空间不大时,Python 会暂时缓存这部分内存。这样,下次我们再创建同样大小的元组时,Python 就可以不用再向操作系统发出请求,去寻找内存,而是可以直接分配之前缓存的内存空间,这样就能大大加快程序的运行速度。

创建相同的元素情况下 元祖比列表块
索引操作时 两者速递差别不大,几乎可以忽略不计
增加、删减、改变元素使用列表,因为元祖要通过新建一个元组来完成

使用场景:
1. 如果存储的数据和数量不变,比如你有一个函数,需要返回的是一个地点的经纬度,然后直接传给前端渲染,那么肯定选用元组更合适。

  1. def get_location():
  2. .....
  3. return (longitude, latitude)
  1. 如果存储的数据或数量是可变的,比如社交平台上的一个日志功能,是统计一个用户在一周之内看了哪些用户的帖子,那么则用列表更合适。
    1. viewer_owner_id_list = [] # 里面的每个元素记录了这个viewer一周内看过的所有owner的id
    2. records = queryDB(viewer_id) # 索引数据库,拿到某个viewer一周内的日志
    3. for record in records:
    4. viewer_owner_id_list.append(record.id)

    面试题

    ```python

    创建空列表,这两种效率上有区别吗?优先考虑使用哪种?

    option A

    empty_list = list()

option B

empty_list = []

  1. 答:区别主要在于list()是一个function callPythonfunction call会创建stack,并且进行一系列参数检查的操作,比较expensive,反观[]是一个内置的C函数,可以直接被调用,因此效率高
  2. <a name="WuOmC"></a>
  3. # 集合(Set)
  4. 集合是无序,并且所有元素是不重复的
  5. <a name="jPIe2"></a>
  6. ## 创建集合方式
  7. ```python
  8. s1 = {1, 2, 3}
  9. s2 = set([1, 2, 3])
  10. s1 == s2
  11. True
  12. s = {1, 'hello', 5.0} # 支持混合类型

集合操作

集合并不支持索引操作,因为集合本质上是一个哈希表,和列表不一样。__集合不支持切片或者下标访问元素。

  1. # 判断set中是否存在该元素
  2. s = {1, 2, 3}
  3. 1 in s
  4. True
  5. 10 in s
  6. False
  7. s = {1, 2, 3}
  8. s.add(4) # 增加元素4到集合
  9. s
  10. {1, 2, 3, 4}
  11. s.remove(4) # 从集合中删除元素4
  12. s
  13. {1, 2, 3}
  14. #排序
  15. s = {3, 4, 2, 1}
  16. sorted(s) # 对集合的元素进行升序排序
  17. [1, 2, 3, 4]
  18. # 差集:
  19. {1,2,3,4,5,6} - {3,4} 结果为{1,2,5,6} 在集合中剔除几个元素使用“-”
  20. # 交集
  21. {1,2,3,4} & {3,5} #结果为 {3}
  22. # 添加元素并且不重复(不能使用“+”)
  23. {1,2} | {3} #结果为{1,2,3}

注意:集合的 pop() 操作是删除集合中最后一个元素,可是集合本身是无序的,你无法知道会删除哪个元素,因此这个操作得谨慎使用。

性能

添加和查找O(1)

字典(dict)

很多个key和value,但是key不能重复,key必须是不可变的类型(不能是变量,不能是列表因为列表里面内容可以改变),value可以是任意类型

创建字典方式

  1. d1 = {'name': 'jason', 'age': 20, 'gender': 'male'}
  2. d2 = dict({'name': 'jason', 'age': 20, 'gender': 'male'})
  3. d3 = dict([('name', 'jason'), ('age', 20), ('gender', 'male')])
  4. d4 = dict(name='jason', age=20, gender='male')
  5. d1 == d2 == d3 ==d4
  6. True

字典元素访问

  1. # 根据key得到value,key不存在会抛异常
  2. d = {'name': 'jason', 'age': 20}
  3. d['name']
  4. 'jason'
  5. # 通过get(key,default)函数获取索引,会返回一个默认值
  6. d= {'name': 'jason', 'age': 20}
  7. d.get('name')
  8. 'jason'
  9. d.get('location', 'null')
  10. 'null'

字典元素操作

  1. # 判断字典哪是否存在该key
  2. d = {'name': 'jason', 'age': 20}
  3. 'name' in d
  4. True
  5. 'location' in d
  6. False
  7. d = {'name': 'jason', 'age': 20}
  8. d['gender'] = 'male' # 增加元素对'gender': 'male'
  9. d['dob'] = '1999-02-01' # 增加元素对'dob': '1999-02-01'
  10. d
  11. {'name': 'jason', 'age': 20, 'gender': 'male', 'dob': '1999-02-01'}
  12. d['dob'] = '1998-01-01' # 更新键'dob'对应的值
  13. d.pop('dob') # 删除键为'dob'的元素对
  14. '1998-01-01'
  15. d
  16. {'name': 'jason', 'age': 20, 'gender': 'male'}

根据key或者value排序

  1. d = {'b': 1, 'a': 2, 'c': 10}
  2. d_sorted_by_key = sorted(d.items(), key=lambda x: x[0]) # 根据字典键的升序排序
  3. d_sorted_by_value = sorted(d.items(), key=lambda x: x[1]) # 根据字典值的升序排序
  4. d_sorted_by_key
  5. [('a', 2), ('b', 1), ('c', 10)] # 列表中的每个元素,是由原字典的键和值组成的元组
  6. d_sorted_by_value
  7. [('b', 1), ('a', 2), ('c', 10)]

字典底层工作原理(dict & set)

哈希值+key+value

数据结构:

  1. Indices
  2. ----------------------------------------------------
  3. None | index | None | None | index | None | index ...
  4. ----------------------------------------------------
  5. Entries
  6. --------------------
  7. hash0 key0 value0
  8. ---------------------
  9. hash1 key1 value1
  10. ---------------------
  11. hash2 key2 value2
  12. ---------------------
  13. ...
  14. ---------------------
  1. # 哈希表结构存储形式
  2. indices = [None, 1, None, None, 0, None, 2]
  3. entries = [
  4. [1231236123, 'name', 'mike'],
  5. [-230273521, 'dob', '1999-01-01'],
  6. [9371539127, 'gender', 'male']
  7. ]

插入操作

  1. 计算key的hash值
  2. 计算索引:hash 值 & mask(mask=PyDicMinSize-1)(等同于 hash值 % PyDicMinSize)
    1. 这一步为了防止空间过于浪费,将索引控制在0~PyDicMinSize区间
      1. 例如 两个key但是一个存储在索引为1的位置一个存储在索引为100w的位置
  3. 插入元素

    1. 如果位置为空,直接插入
    2. 如果被占用则判断两个元素的hash值和key是否相同
      1. 如果hash和key都相同则更新该key的value为当前的值
      2. 如果hash和key不想等,那就是哈希冲突(两个元素的key不相等但是hash值相等),那么hash会寻找表中的空余位置
        1. 寻找空余位置最简单的就是线性寻找,从这个位置开始往后挨个寻找空位

          插入操作

  4. 计算key的hash值

  5. 计算索引:hash 值 & mask(mask=PyDicMinSize-1)(等同于 hash值 % PyDicMinSize)
    1. 这一步为了防止空间过于浪费,将索引控制在0~PyDicMinSize区间
      1. 例如 两个key但是一个存储在索引为1的位置一个存储在索引为100w的位置
  6. 根据之前算下来的索引位置去查找hash值和key是否相同
    1. 相同则返回
    2. 不同则继续查找,知道找到空位抛出异常

插入操作

对于删除操作,Python 会暂时对这个位置的元素,赋于一个特殊的值,等到重新调整哈希表的大小时,再将其删除。
哈希冲突的发生,往往会降低字典和集合操作的速度。因此,为了保证其高效性,字典和集合内的哈希表,通常会保证其至少留有 1/3 的剩余空间。随着元素的不停插入,当剩余空间小于 1/3 时,Python 会重新获取更大的内存空间,扩充哈希表。不过,这种情况下,表内所有的元素位置都会被重新排放。