数字处理和列表等内存模型

数字处理和列表等内存模型

3.1 分类

  • 数值型

    • int(整数)、float(浮点数)、complex(复数)、bool(布尔型)

  • 序列对象

    • 字符串 str

    • 列表 list

    • tuple(元组)

  • 键值对(非常重要的数据结构)

    • 集合 set

    • 字典 dict

3.1.1 数值型

  • 数值型

    • int、float、complex、bool 都是 class(类),1、5.0、2+3j 都是对象即实例。

    • int : python3 的 int 就是长整型,且没有大小限制,受限于内存区域的大小。

    • float : 有整数部分和小数部分组成。支持十进制和科学计数法表示。只有双精度型。

    • complex : 有实数和虚数部分组成,实数和虚数部分都是浮点数,3+4.2 J

    • bool: int 的子类,仅有2个实例 True、False 对应 1 和 0 ,可以和整数直接运算。

  • 类型转换(built-in 指的是内建),强语言通过函数强制类型转换

    • int(x)返回一个整数

    • float(x)返回一个浮点数

    • complex(x)、complex(x,y)返回一个复数

    • bool(x) 返回布尔值,前面讲过 False 等价的对象

3.1.1.1 数字的处理函数 – 1

  • round(),四舍五入? 不是。而是四舍六入,五取偶

  • math模块中有两个函数,floor()地板 向下取值、天花板ceil() 向上取

  • int() 只做取整数部分

  • // 取整向下取

3.1.1.1.1 举例说明

floor()地板、ceil()天花板,举例

import math

math.floor(2.5),math.floor(-2.5)
(2, -3)

math.ceil(2.5),math.ceil(-2.5)
(3, -2)                         # 在工作中向天花板跑比较多

int() 整数类型,取整数部分举例:

int(2.5),int(3.5)
(2, 3)

int(-2.9),int(-3.9)
(-2, -3)

//不管在正数范围还是在负数范围都是在往下跑举例:

3//2
1

3//-2
-2

round()函数使用范例

round(0.1),round(1.3),round(1.6),round(2.45),round(2.71)    # 典型的四舍五入
(0, 1, 2, 2, 3)

round(0.5),round(1.5),round(2.5),round(3.5),round(4.5) # 在 python3 中有个特点 round 函数在遇见 .5 最中间这个值有个原则,就是哪个偶数理他最近的它就往这个偶数靠
(0, 2, 2, 4, 4)

round(0.500001),round(1.5002),round(2.51),round(3.501),round(4.5001) # 只要超过 0.5 就变为四舍五入
(1, 2, 3, 4, 5)

3.1.1.2 数字的处理函数 – 2

  • min() 取最大值

  • max() 取最小值

  • pow(x,y)等于 x**y ,谁的几次方。

  • math.sqrt() 开平方

  • 进制函数,返回值是字符串

  • bin()

  • oct()

  • hex()

  • math.pi 兀 math.e自如常数

3.1.1.2.1 举例说明
  • min() 取最大值

  • max() 取最小值

    范例:

    不同类型之间默认情况是不能直接比较。

    min(1,2)                     # 取出最小值 1
    (1)
    
    max(1,2,3,4,5,),max('1','2') # 取出最大值 5 、和'2'
    (5, '2')
    
    max('1','2',3)      # 不同类型直接比较没有意义,但是可以通过 key=func 的函数来进行自定义转换比较
    
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-62-dcfaffa36328> in <module>
    ----> 1 max('1','2',3)
    
    TypeError: '>' not supported between instances of 'int' and 'str

  • pow(x,y)等于 x**y ,谁的几次方。

    范例:

    pow(2,2)        # 2 的 2 次方
    4
    
    2**2            # 也可以通过这种方式写出 2 的 2 次方,一般用这种范式
    4

  • math.sqrt() 开平方

    范例:

    math.sqrt(2)        # 开平方通过 math 模块的 sqrt 函数来执行
    1.4142135623730951
        
    2**0.5              # 也可以通过几次方的方式来执行,但是要是开 3 次方还得使用 math.sqrt 来执行
    1.4142135623730951

  • 进制函数,返回值是字符串,字符串和数字是两回事,所以在计算的时候要将字符串转换为数字才能够使用

    • bin()

    • oct()

    • hex()

      范例:

      bin(2)
      '0b10'
      
      oct(8)
      '0o10'
      
      hex(16)
      '0x10'

  • math.pi 兀值

    math.pi
    3.141592653589793

  • math.e自如常数

3.1.2 类型判断

  • type(obj),返回类型,而不是字符串

  • isinstance(obj, class_or_tuple),返回布尔值

  • 举例:

    • type(a)

    • type(‘abc’)

    • type(123)

    • isinstance(6,str)

    • isinstance(6, (str, bool,int))

type(1+True)

type(1+True+2.0) #是什么?隐式转换

3.1.2.1 举例说明

  • type(obj),返回类型,而不是字符串

    范例:

    type(hex(16))       # 通过 type 函数来求 hex(16) 的类型
    str                 # 返回时 str 字符串类型
    
    type(type(hex(16))) # 再次通过 type 来问 str 是什么东西
    type                # 返回的是 str 是一个类型

  • isinstance(obj, class_or_tuple),返回布尔值,我们一旦要判断是什么类型一般情况下都会写isinstance,这是我们的内建函数,是我们经常使用的东西

    范例:

    isinstance(16,int)      # 这是在问 16 是否为 int 类型
    True                    # 返回为 true 就是是
    
    isinstance('16',int)    # 这个地方我写出字符串 16 问他是不是 int 整数类型
    False                   # 返回为 false 
    
    isinstance('16',(int,str)) # 这是问 16 是不是 (int,str) 中的某一个类型,先拿 16 和 int 整数类型对比,然后再拿 16 和字符类型相比较。
    True                       # 因为 16 是字符类型所以返回为 true
    
    isinstance('16',(int,str,bool)) # 这是问 16 是不是 (int,str) 中的某一个类型,先拿 16 和 int 整数类型对比,然后再拿 16 和字符类型相比较。直接不用看 16 是否为 bool 类型
    True                        # 因为 16 是字符类型所以返回为 true
    
    isinstance('16',(int,float,bool)) # 因为 16 和小括号中的所有类都不能匹配所以 false 报错
    False
    
    
    isinstance('16',int,float,bool) # 因为我们这里将小括号去掉了就会报错,因为它的语法要求在写多个的时候必须要使用小括号将他们组起来形成一个元组。因为刚才括号括起来是传的一个参数,现在将括号去掉就等于一次性传了 4 个参数进去。
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-91-56be86a9fa32> in <module>
    ----> 1 isinstance('16',int,float,bool)
    
    TypeError: isinstance expected 2 arguments, got 4

  • 举例:

    • type(a)

    • type(‘abc’)

    • type(123)

    • isinstance(6,str)

    • isinstance(6, (str, bool,int))

    type(16)
    int
    
    type(16 + True) # 因为 16 的布尔值位 true 所以 true+true 为正也就是 int 类型
    int

type(1+True)

type(1+True+2.0) #是什么?隐式转换,python 是强类型语言

type(16 + True + 2.0) # 往 float 浮点数转,因为往整型转就丢东西了,因为 float 的精度要比整型要高 float 在 python 中是一个双精度,所以要向高精度类型转换,所以这个时候就向 float 转。
float

但是能不能进行字符串和数字、数值 之间的相加,这个要看语言是强类型还是弱类型。但是数值类型之间是可以混加的,因为这里面都可以做隐式类型转换向高精度类型转。

3.1.3 列表 list 必须掌握的数据结构

列表特点:有顺序、有索引、可迭代、可变

  • 一个队列,—个排列整齐有顺序的队伍

  • 列表内的个体称作元素,由若干元素组成列表

    • 在逻辑上所有的元素都是一个挨着一个排列好的,在内存中也是要求连续的,并且给它进行编号这种东西我们称为索引

    • 就是由 0 个或任意个,可能有也可能没有,有呢可能有一大堆,也可能有一个,也就是说这个列表里面这个队伍但是没有人来所以这个叫做空对,如果来一个元素叫一个元素队伍,但是来多个元素就叫做多个元素队伍排好队就行了,所以列表是一个有顺序的排好队的序列 ,列表类的元素是有顺序的只要定义了列表在这个列表中放的所有数据都是有序的,有序就可以给他编号。所以有序序列最大的好处就是排好队了我就可以给他们编号,然后我想叫几号就叫几号。

    • 在内存中列表也是有顺序的,如果在内存中需要连续的有顺序的开辟内存空间。像我们这种列表的话如果碎片太多,就开辟不出来,总容量是够的剩余容量也是够的,但是我们要开辟一个大的连续空间的话,比如说开个列表需要一万个元素,这时候内存的总容量可以给列表开,但是内存此时有碎片好多空洞并且没有垃圾回收又移不动,这时候就会创建失败。所以内存垃圾回收就需要规整规整,这样才能给一个连续的列表空间。但是在 python 中不能做到要一个大列表除非一次填满,否则 python 做不到。python 的意思是我在创建列表的时候并不会让你写入一个数字进来。而是写一个元素就给你一个位置,但是在 python 中它会给你预估一个然后再给一块内存空间。

  • 元素可以是任意对象(数字、字符串、对象、列表等)

  • 列表内元素有顺序,可以使用索引,也就是说有序即可索引,有顺序就可以编号。

  • 线性的数据结构,就是一个挨着一个。

  • 使用 [ ] 表示,一个列表使用中括号来表示。

  • 列表是可变的

  • 列表list、链表、queue(队列)、stack(栈)的差异。

    • 列表:我们可以认为列表是一个排好的队伍,有顺序可索引。

    • 链表:是一种松散结构,也就是大家不需要站成一排,可以手拉手随便站。在内存中就是不连续的但是链表是有顺序,它知道下一个在哪。但是链表的顺序在内存种感觉是散落的,但是也可以通过索引由上一个找到下一个。

    • queue 队列:和列表比较像,先进先出的队列,就如同过安检一样从前面的开始依次出去,先进去的元素必须先出来,假如需要用后面的几个元素,也得将需要的这几个元素的前面几个元素拿出来才可以使用,没有插队这一说,队列就是用来做先进先出的。队列就是元素排个队一个一个走,不是用来寻找和增减的。

    • stack(栈):后进先出,在生活中其实就相当于碟盘子,最先放的盘子放到最下面,而最后一张盘子最后进去但是我们要使用的时候却是最先拿出来,也相当于后进去的数据必须先拿出来,否则其他数据拿不出来,其实 stack 就是一种 queue。

      在我们逻辑上的概念会发现 queue(队列)和 stack(栈)都是有序的队列,但是在内存中就是连续的吗?假如 queue 通过列表来实现就是连续的,如果是由链表来实现的就不是连续的队列了

3.1.3.1 列表和链表的差异

列表和链表之间最大的问题是他们在做一些事物的时候效率不同,比如说我现在要找 3 号,在列表中找到开头之后直接偏移几个单位就可以找到。但是在链表中手拉手在内存中这样散落着我们只能从 1 号找到 2 直到找到 3 号。

列表:

假如现在有个问题列表是可变的,如下图:

这是列表在一个连续的空间中有 1~4 个序号,现在有个问题是有人想在 1 和 2 之间插个队这时候就会出现 2、3、4 统统向后移动一位,但是假如我们想在第一个位置加一个元素,而且我们的列表序号已经到了 10000 了就会出现原有的 1~10000 序号全部往后移动一位。假如这时候我们将 10000 个列表中的 2 号取出,我们是不是依次将 3 移到 2 、4 移动到 3 后面一次向前移动一位,但是这种操作非常慢。好处内存中连续找的非常快偏移几个单位即可找到(在内存中偏移几个字节为一个单位),这是一个乘法的过程固定来找,这就相当于是一个货架,货架里面有好多格子,并且这些格子之间的编号是连续的,所以我们就能够很快的来找到。虽然这些格子编号是连续的但是这些格子长的长短的短,比如 1 号很短大概 5 cm 长、二号呢就快 1 千米长,找起来就没有这方便,所以说偏移的时候我们要求每个单位长度一致这样偏移才有用。

我们解决了寻找和偏移的问题,但是我们发现在该列表中增加值或者拿掉值还是有问题,如果我要把在尾部实现对该列表的增减效率依旧很高对该列表没有什么影响,因为不会影响该列表的序号问题。但是在该列表中间进行增减的就会有影响真个列表序号都需要进行调整,到底影响多大就要看我们从该列表的那个位置进行增减了。

列表的优点:查询表速度很快

列表的缺点:在中间进行增减这个时候队伍就需要进行调整,所以修改队伍就慢。

链表:

链表就像一群小朋友手拉手,这时候我叫三号小朋友出来。这时候 2 号小朋友就会和 4 号小朋友互相牵手,或这说 2 号和 3 号手断开我加一个新的小朋友进来,手互相牵手就实现了增加,相对来说比列表快。

但是在链表中找一个序号就很慢,因为链表需要从头或者说是从尾巴开始找。

链表优点:增减修改速度快、代价小。

链表缺点:查询比列表慢。

3.1.3.2 列表 list 定义初始化

  • list() -> new empty list

  • list(iterable)->new list initialized from iterable's items

  • 列表不能一开始就定义大小

list?

Init signature: list(self, /, *args, **kwargs)
Docstring:     
list() -> new empty list
list(iterable) -> new list initialized from iterable's items
Type:           type
Subclasses:     _HashedSeq, StackSummary, SList, List, List, List, List, List, List, _ImmutableLineList, ...

list 使用范例:

l = list(range(5)) # range 是一个可迭代对象 ,将这个 range 5 的可迭代对象交给 list 之后,list 会把可迭代对象里面的元素一个一个拿出来然后放入到 list 里面去,这就相当于 for 循环的 range ,然后把 range 的东西一个一个添加到 list 列表里面去去作为它的元素。这类东西我们经常使用
l       # 打印刚才 l 赋值的 list(range(5))
[0, 1, 2, 3, 4]



l = [range(5)] # range 虽然是可迭代对象,但是在这里没有添加 list 列表,所以就是一个对象,在这里只能够算一个。
l               # 打印 l 赋值的 range(5)
[range(0, 5)]

list 迭代使用范例:

可以迭代的东西未必可以索引。

l = list([1,3,5]) # 列表不但是有顺序有索引的也是可迭代对象。
l

## 执行结果
[1, 3, 5]

for i in [1,3,5]: # for 可以迭代未必可索引
    print(i)

## 执行结果
1
3
5

3.1.3.3 列表的索引访问

  • 索引,也叫下标

  • 正索引:从左至右,从 0 开始,为列表中每一个元素编号

  • 负索引:从右至左,从 -1 开始

  • 正负索引不可以超界比如定义了 0、1、2 这三个索引但是要去索引 3 就会报错,否则引发异常Index Error

  • 为了理解方便,可以认为列表是从左至右排列的,左边是头部,右边是尾部,左边是下界,右边是上界

  • 列表通过索引访问

    • list[index 编号],index就是索引,使用中括号访问

3.1.3.4 列表查询

  • index(value,[start,[stop]])

    • 通过值value,从指定区间查找列表内的元素是否匹配

    • 匹配第一个就立即返回索引

    • 匹配不到,抛出异常ValueError

  • count(value)

    • 返回列表中匹配value的次数

  • 时间复杂度

    • indexcount方法都是O(n)

    • 随着列表数据规模的增大,而效率下降

  • 如何返回列表元素的个数?如何遍历?如何设计高效?

    • len()

3.1.3.4.1 index 索引的使用范例

列表查询范例:

# 给 l 赋值为 [[1,2],3,['a']] 这个列表
l = [[1,2],3,['a']]

# 通过查看 l 已经实现赋值
l
[[1, 2], 3, ['a']]

# 通过 index 在 l 这个列表中查询 3 ,并将将 3 所在的列表位置打印出来
l.index(3) # 通过 l 这个列表来找 3 这个索引,在索引中是由 0 开始计算索引 3 在第二个列表位置中也就是 1
1

列表查询错误范例:

# 先给 l 这个列表赋值为 [1,2],3,['a']
l = [[1,2],3,['a']]

# 输出 l 列表的赋值结果
l
[[1, 2], 3, ['a']]

# 通过 index 索引查询在 l 列表中的 2 列表由于没有报错
l.index(2)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-113-d275bb726dcb> in <module>
----> 1 l.index(2) # 通过 l 这个列表来找 2 这个索引

ValueError: 2 is not in list        # 直接报错 value error ,2 不在这个列表中

列表查询中多个同数据的列表范例:

从左至右来找如果能够找到就返回这个值所对应的索引也就是列表序号,找到第一个就直接返回并不再查找,如果在当前列表中找不到的就直接报value error

# 先给 l 这个列表赋值为 [1,2],3,['a'],2,0,2
l = [[1,2],3,['a'],2,0,2]

# 输出 l 列表的赋值结果
l
[[1, 2], 3, ['a'], 2, 0, 2]

# 通过 l 这个列表来找 2 这个索引,因为第一个 2 的列表排在第 4 个直接将二进制的 4 打印出来
l.index(2) 
3

利用索引号找元素和利用元素值来找索引号范例:

# 给 l 这个列表赋值 [1,2],3,['a'],2,0,2
l = [[1,2],3,['a'],2,0,2]

# 打印 l 这个列表的赋值结果
l
[[1, 2], 3, ['a'], 2, 0, 2]

# 利用索引号来找元素
l[0] 
[1, 2]

# 利用元素值来找索引号
l.index([1,2])
0

用索引号来找元素速度较快,反过来用元素值来找索引号就比较慢,因为它会去整个列表中遍历一遍,然后才能够找出来。

元素值来找索引号,这种行为不能常做,甚至可以不做,能不做就不做,因为太慢了。

3.1.3.4.2 count 求总数使用范例:

求总数就是要所有元素过一遍才知道该列表中有几个,count 函数会将 list 中有多个元素他就要全部找一遍,这种全部浏览一遍叫做遍历

count 求总数范例:

# 给 l 这个列表赋值 [1,2],3,['a'],2,0,2
l = [[1,2],3,['a'],2,0,2]

# 打印 l 这个列表的赋值结果
l
[[1, 2], 3, ['a'], 2, 0, 2]

# 使用 count 求出 2 在 l 列表中有几个
l.count(2)
2

count 求列表中没有出现过的元素范例

l = [[1,2],3,['a'],2,0,2]

l
[[1, 2], 3, ['a'], 2, 0, 2]

l.count(5)
0           # 没有 5 这个元素所有打印 0 

3.1.3.4.3 index 和 count 总结

用索引号来找元素速度较快,反过来用元素值来找索引号就比较慢,因为它会去整个列表中遍历一遍,然后才能够找出来。

元素值来找索引号,这种行为不能常做,甚至可以不做,能不做就不做,因为太慢了。

但凡是遍历的操作,能少做就少做或者不做

3.1.3.4.4 时间复杂度
  • indexcount方法都是 O(n)

    • O(n):n 表示列表中元素的个数,O:表示遍历,合起来就是 O(n) 将整个列表的所有元素遍历了一遍。

    • count:需要一定要知道某个元素的总数,就得将该列表中所有的元素全部遍历一遍,因为的全部遍历一遍后才知道某元素的总数是多少。

    • index:对于 index 而言就是看概率了,如果我们要找的值在第一个就直接返回,但是如果我们要找的元素在该列表中的最后一个,他就会去遍历一遍这就是典型的 O(n) 。

  • 随着列表数据规模的增大,而效率下降。

    • 因为我们在工作中要找的元素在那个位置我们都不知道,而且随着项目的规模越来越大我们寻找的效率就会逐渐下降。

    • 随着 n 的变化,寻找效率越来越低,因为 n 会越来越大。

对我们来说凡是需要大 O(n) 找到的都是遍历的问题。

3.1.3.4.4 如何返回列表元素的个数?如何遍历?如何设计高效?(能少做就少做)
  • 可以通过 len() 函数查找

    • 可以认为所有的列表最开始都是空的,然后我们加一个元素的时候 +1 这时候我要做一个东西就是为列表来计算的,我们加一个元素就在这个东西里面 +1,我们在写一个元素就会理解为 1+1 。假如我们减一个元素,我不管他从列表中的那个位置减。只要将元素拿走一个我们就在原有基础上 -1 。因为在 python 中一切皆对象,也就是说列表是一个对象这时候我们在这个列表对象上专门给它附加一个值,就表示为当下的元素个数。只要一增加元素我们就在当前基础上+1 ,一减元素我们就在当前基础上-1

    • len () 函数就等于说是在依次 +1 在当前元素计算的基础上依次累加,直到没有元素给 len 函数相加为止就会将该列表的元素长度反馈。

len () 使用范例:

# 给 l 这个列表赋值 [1,2],3,['a'],2,0,2
l = [[1,2],3,['a'],2,0,2]

# 打印 l 这个列表的赋值结果
l
[[1, 2], 3, ['a'], 2, 0, 2]

# 查看 l 列表的元素长度
len(l) # 通过 len 函数然后再小括号中写我们的列表名 l
6
点赞

发表评论