jupyther_python基础系列 09 第九章 有益的探索

Table of Contents

第九章 有益的探索

数据类型的底层实现

奇怪的列表

1.错综复杂的复制

list_1 = [1,[22,33,44],(5,6,7),{"name":"peter","sex":"male"}]
  • 浅拷贝
list_2 = list_1.copy() #浅拷贝
list_2[1].append(55)

print("list_1: ", list_1)
print("list_2: ", list_2)
list_1:  [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'peter', 'sex': 'male'}]
list_2:  [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'peter', 'sex': 'male'}]

2.列表的底层实现

引用数组的概念
引用数组的概念
列表内的元素可以分散的存储在内存中
列表存储的,实际上是这些元素的地址!!!——地址的存储在内存中是连续的

list_1 = [1,[22,33,44],(5,6,7),{"name":"peter","sex":"male"}]
list_2 = list(list_1)  #浅拷贝 与list_1.copy()功能一样

(1) 新增元素

list_1.append(100)
list_2.append("n")

print("list_1: ", list_1)
print("list_2: ", list_2)
list_1:  [1, [22, 33, 44], (5, 6, 7), {'name': 'peter', 'sex': 'male'}, 100]
list_2:  [1, [22, 33, 44], (5, 6, 7), {'name': 'peter', 'sex': 'male'}, 'n']
list_1.append(100)
list_2.append("n")

print("list_1: ", list_1)
print("list_2: ", list_2)
list_1:  [1, [22, 33, 44], (5, 6, 7), {'name': 'peter', 'sex': 'male'}, 100, 100]
list_2:  [1, [22, 33, 44], (5, 6, 7), {'name': 'peter', 'sex': 'male'}, 'n', 'n']

list_1 和list_2

后面增加不同的元素

列表浅拷贝示意图

(2) 修改元素

list_1[0] = 111
list_2[0]= 222

print("list_1: ", list_1)
print("list_2: ", list_2)
list_1:  [111, [22, 33, 44], (5, 6, 7), {'name': 'peter', 'sex': 'male'}, 100, 100]
list_2:  [222, [22, 33, 44], (5, 6, 7), {'name': 'peter', 'sex': 'male'}, 'n', 'n']

(3) 对列表型元素进行操作

list_1[1] .remove(44)
list_2[1] += [55,66]

print("list_1: ", list_1)
print("list_2: ", list_2)
list_1:  [111, [22, 33, 55, 66], (5, 6, 7), {'name': 'peter', 'sex': 'male'}, 100, 100]
list_2:  [222, [22, 33, 55, 66], (5, 6, 7), {'name': 'peter', 'sex': 'male'}, 'n', 'n']

(4) 对元组型元素进行操作
元组是不可变的 当执行+操作 元组是一个新的元组

list_2[2] += (8,9)

print("list_1: ", list_1)
print("list_2: ", list_2)
list_1:  [111, [22, 33, 55, 66], (5, 6, 7), {'name': 'peter', 'sex': 'male'}, 100, 100]
list_2:  [222, [22, 33, 55, 66], (5, 6, 7, 8, 9), {'name': 'peter', 'sex': 'male'}, 'n', 'n']

(5) 对字典型元素进行操作

list_1[-3]["age"] = 20

print("list_1: ", list_1)
print("list_2: ", list_2)
list_1:  [111, [22, 33, 55, 66], (5, 6, 7), {'name': 'peter', 'sex': 'male', 'age': 20}, 100, 100]
list_2:  [222, [22, 33, 55, 66], (5, 6, 7, 8, 9), {'name': 'peter', 'sex': 'male', 'age': 20}, 'n', 'n']

  • 针对不可变元素(数字、字符串、元组)的操作,都各自生效了
  • 针对不可变元素(列表、集合)的操作,发生了一些混淆

3. 引入深拷贝

  • 深拷贝将所有层级的相关元素全部复制,完全分开,泾渭分明,避免了上述问题
import copy
list_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "peter", "sex": "male"}]
list_2 = copy.deepcopy(list_1)
list_1[-1]["age"] = 18
list_2[1].append(55)

print("list_1: ", list_1)
print("list_2: ", list_2)
list_1:  [1, [22, 33, 44], (5, 6, 7), {'name': 'peter', 'sex': 'male', 'age': 18}]
list_2:  [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'peter', 'sex': 'male'}]

神秘的字典

1.快速的查找

import time

ls_1 = list(range(1000000))
ls_2 = list(range(500)) + [-10] * 500

start = time.time()
count = 0
for n in ls_2:
    if n in ls_1:
        count += 1
end = time.time()

print("查找{}个元素,在ls_1中的有{}个,共用时{}秒".\
      format(len(ls_2),count,round(end-start),2))
查找1000个元素,在ls_1中的有500个,共用时6秒
import time

d = {i: i for i in range(1000000)}
ls_2 = list(range(500)) + [-10] * 500

start = time.time()
count = 0
for n in ls_2:
    try:
        d[n]
    except:
        pass
    else:
        count+=1
end = time.time()

print("查找{}个元素,在ls_1中的有{}个,共用时{}秒".\
      format(len(ls_2),count,round(end-start),2))
查找1000个元素,在ls_1中的有500个,共用时0秒

字典的实现

通过稀疏数组来实现值的存储与访问

字典的创建过程

  • 第一步: 创建一个散列表(稀疏数组 N>>n)
d = {}
  • 第二步: 通过hash()计算键的散列值
print(hash("python"))
print(hash(1012))
print(hash((1,2)))
7824177075760137180
1012
3713081631934410656
d["age"] = 18  # 增加键值对的操作,首先会计算键的散列值hash("age")
print(hash("age"))
-7854890815213985751
  • 第三步:根据计算的散列值确定其在散列表中的位置

极个别时候,散列值会发生冲突,则内部有相应的解决冲突的办法

  • 第四步:在该位置上存入值

键值对的访问过程

d["age"]
18
  • 第一步:计算要访问的键的散列值

  • 第二步:根据计算的散列值,通过一定的规则,确定其在散列表中的位置

  • 第三步:读取该位置上存储的值

      如果存在,则返回该值  
      如果不存在,则报错 Key Error
    

3.小结

(1)宇典数据类型,通过空间換时间,实现了快速的数据查找

·也就注定了字典的空间利用效率低下

(2)因为散列值对应位置的顺序与键在字典中显示的顺序可能不同,因此表现出来字典是无序的

紧凑的字符串

通过紧凑数组实现字符串的存储

  • 数据在内存中是连续存放的,效率更高,节省空间

  • 思考一下,同为序列类型,为什么列表采用引用数组,而字符串采用紧凑数组

是否可变

1、不可变类型:数字、字符串、元组

在生命周期中保持内容不变

换句话说,改变了就不是它自己了(id变了)

  • 不可变对象的+=操作实际上创建了一个新的对象
x = 1
y = "python"

print("x id: ", id(x))
print("y id: ", id(y))
x id:  140722067513744
y id:  2488682840624
x += 2
y += "123"

print("x id: ", id(x))
print("y id: ", id(y))
x id:  140722067513808
y id:  2487398868016

元组并不是总是不可变的

t = (1,[2])
print(t)
print(id(t))
t[1].append(3)

print(t)
print(id(t))
(1, [2])
2488716027720
(1, [2, 3])
2488716027720

2、可变类型:列表、字典、集合

  • id保持不变,但是里面的内容可以变

  • 可变对象的+=操作实际在原对象的基础上就地修改

ls = [1, 2, 3]
d = {"name": "peter", "age": 18}

print("ls id: ", id(ls))
print("d id: ", id(d))
ls id:  2487399022984
d id:  2487025142264
ls += [4,5]
d_2 = {"sex": "male"}
d.update(d_2)

print("ls id: ", id(ls))
print("d id: ", id(d))
ls id:  2487399022984
d id:  2487025142264

列表操作的几个小例子

[例1] 删除列表内的特定元素

  • 方法1 存在运算删除法

确定 每次运算都要从头进行变量查找 效率低下

alist = ["d","d","d","0","0","d","d","1"]

s = "d"
while True:
    if s in alist:
        alist.remove(s)
    else:
        break
print(alist)
['2', '2', '2', '4']
  • 方法2 一次性遍历元素执行删除
alist = ["d","d","d","0","0","d","d","1"]
count = 0
for s in alist:
    if s == "d":
        alist.remove(s)
        count+=1
print(alist,count)
['0', '0', 'd', 'd', '1'] 3

解决方法: 使用负向索引

alist = ["d","d","d","0","0","d","d","1"]

for i in range(-len(alist),0):
    if alist[i] == "d":
        alist.remove(alist[i])
print(alist)
['0', '0', '1']

[例2] 多维列表的创建

[[0]*10]
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

更简洁的语法

解析语法

ls = [[i]*10 for i in range(4)]
ls
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
 [2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
 [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]]
ls[0][0] = 1
ls
[[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

1. 解析语法的基本操作— 以列表解析为例(也称列表推导)

[expression for value in iterable if conditihon]

  • 三要素: 表达式,可迭代对象, if条件(可选)
ls = [[i]*10 for i in range(10) if i % 2 == 0]
ls
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
 [4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
 [6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
 [8, 8, 8, 8, 8, 8, 8, 8, 8, 8]]

执行过程

(1) 从可迭代对象中拿出一个元素

(2) 通过 if条件(有的话),对元素进行筛选

 通过筛选;传递给表达式  
 未通过筛选;进入(1) 进行下一次迭代

(3) 将传递给表达式的元素,带入表达式进行处理,产生一个结果

(4) 将(3)步产生的结果作为列表的一个元素进行存储

(5) 重复(1)-(4),知道迭代对象迭代结束,返回新创建的列表

等价与以下代码

result = []
for value in iterale:
    if condition:
        result.append(expression)
# 例 求20以内奇数的平方
square = [i**2 for i in range(1,21) if i % 2 == 1]
print(square)
[1, 9, 25, 49, 81, 121, 169, 225, 289, 361]

支持多变量

x = [1,2,3]
y = [1,2,3]

res = [i*j for i,j in zip(x,y)]

print(res)
[1, 4, 9]

支持循环嵌套

colors = ["black","white"]
sizes = ["S","M","L"]

tshirts = ["{} {}".format(color,size) for color in colors for size in sizes]

print(tshirts)
['black S', 'black M', 'black L', 'white S', 'white M', 'white L']

2.其他解析语法的例子

  • 解析语法构造字典(字典推导)
squares = {i: i**2 for i in range(10)}
for k, v in squares.items():
    print(k,":",v)
0 : 0
1 : 1
2 : 4
3 : 9
4 : 16
5 : 25
6 : 36
7 : 49
8 : 64
9 : 81
  • 解析语法构造集合(集合推导)
squares = {i**2 for i in range(10)}
squares
{0, 1, 4, 9, 16, 25, 36, 49, 64, 81}
  • 生成器表达式
squares = (i**2 for i in range(10))
squares
<generator object <genexpr> at 0x0000024301DD7148>
colors = ["black","white"]
sizes = ["S","M","L"]
tshirts = ["{} {}".format(color,size) for color in colors for size in sizes]
for tshirt in tshirts:
    print(tshirt)
black S
black M
black L
white S
white M
white L

条件表达式

expr_1 if condition else expr_2

# 将变量n 的绝对值赋值给x

n = -10
x = n if n >=0 else -n
print(x)
10

条件表达式和解析语法简单使用,运行速度相对更快一些

三大神器

生成器

ls = [i**2 for i in range(1000000)]
for i in ls:
    pass

缺点: 占用大量内存

生成器
(1)采用惰性计算的方式

(2)无需一次性存储海量数据

(3)一边执行一边计算,只计算每次需要的值

(4)实际上一直在执行next操作,直到无值可取

1. 生成器表达式

  • 海量数据,不需存储
squares = (i**2 for i in range(1000000))

for i in squares:
    pass
  • 求0-100的和

无需显示存储全部数据,节省内存

sum(i for i in range(101))
5050

2.生成器函数–yidld

  • 生成斐波那契数列
def fib(max):
    ls = []
    n, a, b = 0, 1, 1
    while n < max:
        ls.append(a)
        a, b = b, a + b
        n = n + 1
    return ls


fib(10)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

中间尝试

def fib(max):
    n,a,b = 0,1,1
    while n < max:
        print(a)
        a,b=b,a+b
        n=n+1
        
fib(10)
1
1
2
3
5
8
13
21
34
55
def fib(max):
    n,a,b = 0,1,1
    while n < max:
        yield a
        a,b=b,a+b
        n=n+1
    

构造生成器函数

每次调用next()的时候执行,遇到yield语句返回,再次执行上次返回的yield语句处继续执行

for i in fib(10):
    print(i)
1
1
2
3
5
8
13
21
34
55

迭代器

1.可迭代对象

课直接用域for循环的对象统称为可迭代对象:Iterable

(1) 列表,元组,字符串,字典,集合,文件

可以使用isistance()判断一个对象是否是Iterable对象

from collections import Iterable

print(isinstance([1,2,3],Iterable))
True
print(isinstance({"name":"peter"},Iterable))
True
print(isinstance("Python",Iterable))
True

(2)生成器

squares = (i**2 for i in range(5))
print(isinstance(squares,Iterable))
True
print(next(squares))
print(next(squares))
print(next(squares))
print(next(squares))
print(next(squares))

0
1
4
9
16

直到没有数据可取,抛出StopIteration

print(next(squares))
---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-139-f5163ac9e49b> in <module>
----> 1 print(next(squares))


StopIteration: 

可以被next(函数调用并不断返回下一个值,直至没有数据可取的对象称为迭代器: Iterator

2.迭代器

可以使用 instance()判断一个对象是否是 Iterate对象

(1) 生成器都是迭代器

from collections import Iterator

squares = (i**2 for i in range(10) if i%2==0)
print(isinstance(squares,Iterator))
True


E:\Anaconda\lib\site-packages\ipykernel_launcher.py:1: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3,and in 3.9 it will stop working
  """Entry point for launching an IPython kernel.

(2) 列表,元组,字符串,字典,集合,文件不是迭代器

print(isinstance([1,2,3],Iterator))
False

可以通过iter(iterable)创建迭代器

isinstance(iter([1,2,3]),Iterator)
True

for item in Iterable等价于:

先通过 iter函数获取可迭代对象 Iterable的迭代器  

然后对获取到的迭代器不断调用 next(方法来获取下一个值并将其赋值给item  

当遇到 StopIteration的异常后循环结束  

(3) zip enumerate等 itertools里的函数都是迭代器

x = [1, 2]
y = ['a', 'b']
zip(x, y)
<zip at 0x243053336c8>
for i in zip(x, y):
    print(i)
isinstance(zip(x, y), Iterator)
(1, 'a')
(2, 'b')





True
numbers = [1,2,3,4,5]

enumerate(numbers)
<enumerate at 0x24306dab0e8>
for i in enumerate(numbers):
    print(i)
isinstance(enumerate(numbers), Iterator)
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)





True

(4) 文件是迭代器

with open("测试文件.txt","r",encoding = "utf-8") as f:
    print(isinstance(f, Iterator))
True

(5)迭代器是可以耗尽的

squares = (i**2 for i in range(5))
for square in squares:
    print(square)
0
1
4
9
16
for square in squares:
    print(square)

(6)range()不是迭代器

numbers = range(10)
isinstance(numbers, Iterator)
False
print(len(numbers)) #有长度
print(numbers[0])  #可索引
print(9 in numbers) #可存在运算
# 不能被next进行遍历
10
0
True

range() 可称为懒序列

装饰器

比如 要统计每个函数的运行时间

def f1():
    pass
def f2():
    pass
def f3():
    pass
f1()
f2()
f3()

2. 函数对象

函数是Python中的第一类对象

(1) 可以吧函数赋值给变量  

(2) 对改变了进行调用,可实现原函数的功能
def square(x):
    return x**2

print(type(square))
<class 'function'>
pow_2 = square

print(pow_2(5))
print(square(5))
25
25

可以将函数作为参数进行传递

3.高阶函数

(1)接收函数作为参数

(2)或者返回一个函数

满足上述条件之一的函数称之为高阶函数

def square(x):
    return x**2
def pow_2(fun):
    return fun

f = pow_2(square)

f(8)
64
print(f == square)
True

4. 嵌套函数

def outer():
    print("outer fun is running")

    def inner():
        print("inner fun is running")

    inner()


outer()
outer fun is running
inner fun is running

5.闭包

def outer():
    x = 1
    z = 10

    def inner():
        y = x + 100
        return y, z

    return inner


f = outer()  # 实际上f包含了 inner函数本身+ outer函数的环境

print(f)
<function outer.<locals>.inner at 0x0000027AF41728B8>
print(f.__closure__)  # closure属性中包含了来自外部函数的信息

for i in f.__closure__:
    print(i.cell_contents)
(<cell at 0x0000027AF41DB378: int object at 0x00007FFC549EA190>, <cell at 0x0000027AF41DB2E8: int object at 0x00007FFC549EA2B0>)
1
10
res = f()
print(res)
(101, 10)

闭包:延伸了作用域的函数

如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包

闭包是由函数及其相关的引用环境组合而成的实体即:闭包=函数+引用环境)

  • 一旦在内层函数重新定义了相同名字的变量,则变量成为局部变量
def outer():
    x = 1
    
    def inner():
        nonlocal x # 使用nonlocal 表示x不是一个局部变量
        x = x + 100
        return x

    return inner

f = outer()

f()
101

6.一个简单的装饰器

嵌套函数实现

import time

def timer(func):
    def inner():
        print("inner run")
        start = time.time()
        func()
        end = time.time()
        print("{}函数运行用时{:.2f}秒".format(func.__name__,(end-start)))
        
    return inner

def f1():
    print("f1 run")
    time.sleep(1)
    
f1 = timer(f1)  # 包含inner() 和timer的环境,如传递过来的参数func
f1()
inner run
f1 run
f1函数运行用时1.00秒

语法糖

import time

def timer(func):
    def inner():
        print("inner run")
        start = time.time()
        func()
        end = time.time()
        print("{}函数运行用时{:.2f}秒".format(func.__name__,(end-start)))
        
    return inner

@timer   #相当于实现了 f1 = timer(f1) 
def f1():
    print("f1 run")
    time.sleep(1)

f1()
inner run
f1 run
f1函数运行用时1.00秒

7.装饰有参函数

import time


def timer(func):
    
    
    def inner(*args, **kwargs):
        print("inner run")
        start = time.time()
        
        func(*args, **kwargs)
        
        end = time.time()
        print("{}函数运行用时{:.2f}秒".format(func.__name__, (end - start)))

    return inner


@timer  #相当于实现了 f1 = timer(f1)
def f1(n):
    print("f1 run")
    time.sleep(n)


f1(2)
inner run
f1 run
f1函数运行用时2.00秒

被装饰函数有返回值的情况

import time


def timer(func):
    
    
    def inner(*args, **kwargs):
        print("inner run")
        start = time.time()
        
        res = func(*args, **kwargs)
        
        end = time.time()
        print("{}函数运行用时{:.2f}秒".format(func.__name__, (end - start)))
        return res
    
    return inner


@timer  #相当于实现了 f1 = timer(f1)
def f1(n):
    print("f1 run")
    time.sleep(n)
    return "wake up"


res = f1(2)
print(res)
inner run
f1 run
f1函数运行用时2.00秒
wake up

8.带参数的装饰器

def timer(method):
    
    def outer(func):
        
        def inner(*args, **kwargs):
            print("inner run")
            if method == "origin":
                print("origin inner run")
                start = time.time()
                res = func(*args, **kwargs)
                end = time.time()
                print("{}函数运行用时{:.2f}秒".format(func.__name__, (end - start)))

            elif method == "double":
                print("double inner run")
                start = time.time()
                res = func(*args, **kwargs)
                end = time.time()
                print("{}函数运行双倍用时{:.2f}秒".format(func.__name__, 2*(end - start)))
            return res
            
        return inner
    
    return outer


@timer(method="origin")  # 相当于 timer = timer(method = "origin")  f1 = timer(f1)
def f1():
    print("f1 run")
    time.sleep(2)


@timer(method="double")  # 相当于实现了 f1 = timer(f1)
def f2():
    print("f2 run")
    time.sleep(2)


f1()
f2()
inner run
origin inner run
f1 run
f1函数运行用时2.00秒
inner run
double inner run
f2 run
f2函数运行双倍用时4.00秒

9.何时执行装饰器

  • 一装饰就执行,不必等调用
func_name = []


def find_function(func):
    print("run")
    func_name.append(func)
    return func


@find_function
def f1():
    print("f1 run")


@find_function
def f2():
    print("f2 run")


@find_function
def f3():
    print("f3 run")
    
f1()
f2()
f3()

print(func_name)
run
run
run
f1 run
f2 run
f3 run
[<function f1 at 0x0000027AF4270288>, <function f2 at 0x0000027AF4270828>, <function f3 at 0x0000027AF4270168>]
for func in func_name:
    print(func.__name__)
    func()
    print()
f1
f1 run

f2
f2 run

f3
f3 run

10. 回归本源

import time

def timer(func):
    def inner():
        print("inner run")
        start = time.time()
        func()
        end = time.time()
        print("{}函数运行用时{:.2f}秒".format(func.__name__,(end-start)))
        
    return inner

@timer   #相当于实现了 f1 = timer(f1) 
def f1():
    print("f1 run")
    time.sleep(1)

print(f1.__name__)
inner
  • 回来
import time
from functools import wraps

def timer(func):
    
    @wraps(func)
    def inner():
        
        print("inner run")
        start = time.time()
        func()
        end = time.time()
        print("{}函数运行用时{:.2f}秒".format(func.__name__,(end-start)))
        
    return inner

@timer   #相当于实现了 f1 = timer(f1) 
def f1():
    print("f1 run")
    time.sleep(1)

print(f1.__name__)
f1()
f1
inner run
f1 run
f1函数运行用时1.00秒

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值