目录

一、概述

二、数据类型底层实现

1. 从奇怪的列表说起

1.1 错综复杂的复制

1.2 列表的底层实现​

1.3 引入深拷贝

2. 神秘的字典

2.1 快速的查找

2.2 字典的底层实现

2.3 小结

3. 紧凑的字符串

4. 是否可变

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

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

5. 列表操作的几个小例子

三、更简洁的语法

1. 解析语法

1.1 解析语法的基本结构——以列表解析为例(也称为列表推导)

1.2 其他解析语法的例子

2. 条件表达式

四、三大神器

1. 生成器

1.1 生成器表达式

1.2 生成器函数——yield

2. 迭代器

2.1 可迭代对象

2.2 迭代器

3. 装饰器

3.1 需求的提出

3.2 函数对象

3.3 高阶函数

3.4 嵌套函数

3.5 闭包

3.6 一个简单的装饰器

3.7 装饰有参函数

3.8 带参数的装饰器

3.9 何时执行装饰器

3.10 回归本源

五、重难点回顾

1. 数据类型底层实现

2. 更简洁的语法

3. 三大神器


一、概述

尝试着潜入水中,往冰山的深处扎一个小小的猛子

二、数据类型底层实现

1. 从奇怪的列表说起

1.1 错综复杂的复制

list_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "Sarah"}]
  • 浅拷贝
# list_3 = list_1          # 错误!!!
list_2 = list_1.copy()     # 或者list_1[:] \ list(list_1) 均可实习浅拷贝
  • 对浅拷贝前后两列表分别进行操作

输入:

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': 'Sarah'}]
list_2:   [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'Sarah'}]

1.2 列表的底层实现

引用数组的概念

列表内的元素可以分散的存储在内存中

列表存储的,实际上是这些元素的地址!!!——地址的存储在内存中是连续的

list_1浅拷贝后:

list_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "Sarah"}]
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': 'Sarah'}, 100]
list_2:   [1, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 'n']

(2)修改元素

输入:

list_1[0] = 10
list_2[0] = 20print("list_1:  ", list_1)
print("list_2:  ", list_2)

输出:

list_1:   [10, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 100]
list_2:   [20, [22, 33, 44], (5, 6, 7), {'name': 'Sarah'}, 'n']

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

输入:

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

输出:

list_1:   [10, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah'}, 100]
list_2:   [20, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah'}, 'n']

(4)对元组型元素进行操作

元组是不可变的

输入:

list_2[2] += (8,9)print("list_1:  ", list_1)
print("list_2:  ", list_2)

输出:

list_1:   [10, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah'}, 100]
list_2:   [20, [22, 33, 55, 66], (5, 6, 7, 8, 9), {'name': 'Sarah'}, 'n']

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

输入:

list_1[-2]["age"] = 18print("list_1:  ", list_1)
print("list_2:  ", list_2)

输出:

list_1:   [10, [22, 33, 55, 66], (5, 6, 7), {'name': 'Sarah', 'age': 18}, 100]
list_2:   [20, [22, 33, 55, 66], (5, 6, 7, 8, 9), {'name': 'Sarah', 'age': 18}, 'n']

1.3 引入深拷贝

浅拷贝之后

  • 针对不可变元素(数字、字符串、元组)的操作,都各自生效了

  • 针对可变元素(列表、集合)的操作,发生了一些混淆

引入深拷贝

  • 深拷贝将所有层级的相关元素全部复制,完全分开,泾渭分明,避免了上述问题

输入:

import copylist_1 = [1, [22, 33, 44], (5, 6, 7), {"name": "Sarah"}]
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': 'Sarah', 'age': 18}]
list_2:   [1, [22, 33, 44, 55], (5, 6, 7), {'name': 'Sarah'}]

2. 神秘的字典

2.1 快速的查找

输入:

import timels_1 = list(range(1000000))
ls_2 = list(range(500))+[-10]*5000start = 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)))

输出:

查找5500个元素,在ls_1列表中的有500个,共用时39.54秒

输入:

import timed = {i:i for i in range(100000)}
# print(d)
ls_2 = list(range(500))+[-10]*5000start = time.time()
count = 0
for n in ls_2:try:d[n]except: passelse:count += 1
end = time.time()
print("查找{}个元素,在ls_1列表中的有{}个,共用时{}秒".format(len(ls_2), count,round(end-start)))

输出:

查找5500个元素,在ls_1列表中的有500个,共用时0秒

2.2 字典的底层实现

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

字典的创建过程

  • 第一步:创建一个散列表(稀疏数组 N >> n)
d = {}
  • 第一步:通过hash()计算键的散列值

输入:

print(hash("python"))
print(hash(1024))
print(hash((1,2)))

输出:

-1700689346824694673
1024
-3550055125485641917

输入:

d["age"] = 18    # 增加键值对的操作,首先会计算键的散列值hash("age")
print(hash("age")) 

输出:

3061355840583939350
  • 第二步:根据计算的散列值确定其在散列表中的位置

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

  • 第三步:在该位置上存入值
for i in range(2, 2):print(i)

键值对的访问过程

d["age"]
  • 第一步:计算要访问的键的散列值
  • 第二步:根据计算的散列值,通过一定的规则,确定其在散列表中的位置
  • 第三步:读取该位置上存储的值

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

2.3 小结

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

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

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

  • 回顾一下 N >> n
    如果N = n,会产生很多位置冲突
  • 思考一下开头的小例子,为什么字典实现了比列表更快速的查找

3. 紧凑的字符串

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

  • 数据在内存中是连续存放的,效率更高,节省空间
  • 思考一下,同为序列类型,为什么列表采用引用数组,而字符串采用紧凑数组

4. 是否可变

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

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

  • 换句话说,改变了就不是它自己了(id变了)
  • 不可变对象的 += 操作 实际上创建了一个新的对象

输入:

x = 1
y = "Python"print("x id:", id(x))
print("y id:", id(y))

输出:

x id: 140718440616768
y id: 2040939892664

输入:

x += 2
y += "3.7"print("x id:", id(x))
print("y id:", id(y))

输出:

x id: 140718440616832
y id: 2040992707056

元组并不是总是不可变的

当原组中是不可变类型的元素时,原组才是不可变的

输入:

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

输出:

(1, [2, 3])

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

  • id 保持不变,但是里面的内容可以变
  • 可变对象的 += 操作 实际在原对象的基础上就地修改

输入:

ls = [1, 2, 3]
d = {"Name": "Sarah", "Age": 18}print("ls id:", id(ls))
print("d id:", id(d))

输出:

ls id: 2040991750856
d id: 2040992761608

输入:

ls += [4, 5]
d_2 = {"Sex": "female"}
d.update(d_2)            # 把d_2 中的元素更新到d中print("ls id:", id(ls))
print("d id:", id(d))

输出:

ls id: 2040991750856
d id: 2040992761608

5. 列表操作的几个小例子

【例1】 删除列表内的特定元素

  • 方法1 存在运算删除法

缺点:每次存在运算,都要从头对列表进行遍历、查找、效率低

输入:

alist = ["d", "d", "d", "2", "2", "d" ,"d", "4"]
s = "d"
while True:if s in alist:alist.remove(s)else:break
print(alist)

输出:

['2', '2', '4']
  • 方法2 一次性遍历元素执行删除

输入:

alist = ["d", "d", "d", "2", "2", "d" ,"d", "4"]
for s in alist:if s == "d":alist.remove(s)      # remove(s) 删除列表中第一次出现的该元素
print(alist)

输出:

['2', '2', 'd', 'd', '4']

解决方法:使用负向索引

输入:

alist = ["d", "d", "d", "2", "2", "d" ,"d", "4"]
for i in range(-len(alist), 0):if alist[i] == "d":alist.remove(alist[i])      # remove(s) 删除列表中第一次出现的该元素
print(alist)

输出:

['2', '2', '4']

【例2】 多维列表的创建

输入:

ls = [[0]*10]*5
ls

输出:

[[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, 0],[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

输入:

ls[0][0] = 1
ls

输出:

[[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

三、更简洁的语法

1. 解析语法

输入:

ls = [[0]*10 for i in range(5)]
ls

输出:

[[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, 0],[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

输入:

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],[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

1.1 解析语法的基本结构——以列表解析为例(也称为列表推导)

[expression for value in iterable if conditihon]

  • 三要素:表达式、可迭代对象、if条件(可选)

执行过程

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

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

若通过筛选:则把元素传递给表达式

若未通过:  则进入(1)步骤,进入下一次迭代

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

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

(5)重复(1)~(4)步,直至迭代对象迭代结束,返回新创建的列表

# 等价于如下代码
result = []
for value in iterale:if condition:result.append(expression)

【例】求20以内奇数的平方

输入:

squares = []
for i in range(1,21):if i%2 == 1:squares.append(i**2)
print(squares)   

输出:

[1, 9, 25, 49, 81, 121, 169, 225, 289, 361]

输入:

squares = [i**2 for i in range(1,21) if i%2 == 1]
print(squares) 

输出:

[1, 9, 25, 49, 81, 121, 169, 225, 289, 361]

支持多变量

输入:

x = [1, 2, 3]
y = [1, 2, 3]results = [i*j for i,j in zip(x, y)]
results

输出:

[1, 4, 9]

支持循环嵌套

输入:

colors = ["black", "white"]
sizes = ["S", "M", "L"]
tshirts = ["{} {}".format(color, size) for color in colors for size in sizes]
tshirts

输出:

['black S', 'black M', 'black L', 'white S', 'white M', 'white L']

1.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 0x000001DB37A58390>

输入:

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

2. 条件表达式

expr1 if condition else expr2

如果满足condition则返回expr1,否则返回expr2

【例】将变量n的绝对值赋值给变量x

输入:

n = -10
if n >= 0:x = n
else:x = -n
x

输出:

10

输入:

n = -10
x = n if n>= 0 else -n
x

输出:

10

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

四、三大神器

1. 生成器

ls = [i**2 for i in range(1, 1000001)]
for i in ls:pass

缺点:占用大量内存

生成器

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

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

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

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

1.1 生成器表达式

  • 海量数据,不需存储
squares = (i**2 for i in range(1000000))
for i in squares:pass
  • 求0~100的和

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

输入:

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

输出:

5050

1.2 生成器函数——yield

  • 生产斐波那契数列

数列前两个元素为1,1 之后的元素为其前两个元素之和

输入:

def fib(max):ls = []n, a, b = 0, 1, 1while n < max:ls.append(a)a, b = b, a + bn = n + 1return lsfib(10)

输出:

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

中间尝试

输入:

def fib(max):n, a, b = 0, 1, 1while n < max:print(a)a, b = b, a + bn = n + 1fib(10)

输出:

1
1
2
3
5
8
13
21
34
55

构造生成器函数

在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行;如果不使用next(),直接调用含有yield的函数,yield只相当于return

yield用法详解

输入:

def fib(max):n, a, b = 0, 1, 1while n < max:yield aa, b = b, a + bn = n + 1fib(10)

输出:

<generator object fib at 0x000001BE11B19048>

输入:

for i in fib(10):print(i)

输出:

1
1
2
3
5
8
13
21
34
55

2. 迭代器

2.1 可迭代对象

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

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

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

输入:

from collections import Iterableisinstance([1, 2, 3], Iterable)

输出:

True

输入:

isinstance({"name": "Sarah"}, Iterable)

输出:

True

输入:

isinstance('Python', Iterable)

输出:

True

(2)生成器

输入:

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

输出:

True

生成器不但可以用于for循环,还可以被next()函数调用

输入:

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-66-f5163ac9e49b> in <module>
----> 1 print(next(squares))StopIteration: 

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

2.2 迭代器

可以使用isinstance()判断一个对象是否是Iterator对象

(1) 生成器都是迭代器

输入:

from collections import Iteratorsquares = (i**2 for i in range(5))
isinstance(squares, Iterator)

输出:

True

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

输入:

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 0x1be11b13c48>

输入:

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 0x1be11b39990>

输入:

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(numbers)         # 不可被next()调用,所以不是迭代器

输出:

10
0
True
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-76-7c59bf859258> in <module>2 print(numbers[0])     # 可索引3 print(9 in numbers)   # 可存在计算
----> 4 next(numbers)         # 不可被next()调用TypeError: 'range' object is not an iterator

输入:

for number in numbers:print(number)

输出:

0
1
2
3
4
5
6
7
8
9

不会被耗尽

输入:

for number in numbers:print(number)

输出:

0
1
2
3
4
5
6
7
8
9

可以称range()为懒序列

它是一种序列但并不包含任何内存中的内容而是通过计算来回答问题

3. 装饰器

3.1 需求的提出

(1)需要对已开发上线的程序添加某些功能

(2)不能对程序中函数的源代码进行修改

(3)不能改变程序中函数的调用方式

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

def f1():passdef f2():passdef f3():passf1()
f2()
f3()

没问题,我们有装饰器!!!

3.2 函数对象

函数是Python中的第一类对象

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

(2)对该变量进行调用,可实现原函数的功能

输入:

def square(x):return x**2print(type(square))      # square 是function类的一个实例

输出:

<class 'function'>

输入:

pow_2 = square          # 可以理解成给这个函数起了个别名pow_2
print(pow_2(5))
print(square(5))

输出:

25
25

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

3.3 高阶函数

(1)接收函数作为参数

(2)或者返回一个函数

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

输入:

def square(x):return x**2def pow_2(fun):return funf = pow_2(square)
f(8)

输出:

64

输入:

print(f == square)

输出:

True

3.4 嵌套函数

在函数内部定义一个函数

输入:

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

输出:

outer is running
inner is running

3.5 闭包

输入:

def outer():x = 1z = 10def inner():y = x+100return y, zreturn innerf = outer()                # 实际上f包含了inner函数本身+outer函数的环境
print(f)

输出:

<function outer.<locals>.inner at 0x000001BE11B1D730>

输入:

print(f.__closure__)         # __closure__属性中包含了来自外部函数的信息
for i in f.__closure__:print(i.cell_contents)

输出:

(<cell at 0x000001BE0FDE06D8: int object at 0x00007FF910D59340>, <cell at 0x000001BE0FDE0A98: int object at 0x00007FF910D59460>)
1
10

输入:

res = f()
print(res)

输出:

(101, 10)

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

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

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

  • 一旦在内层函数重新定义了相同名字的变量,则变量成为局部变量

输入:

def outer():x = 1def inner():x = x+100return xreturn innerf = outer()
f()

输出:

---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-87-d2da1048af8b> in <module>10 11 f = outer()
---> 12 f()<ipython-input-87-d2da1048af8b> in inner()3 4     def inner():
----> 5         x = x+1006         return x7 UnboundLocalError: local variable 'x' referenced before assignment

nonlocal允许内嵌的函数来修改闭包变量

输入:

def outer():x = 1def inner():nonlocal xx = x+100return x  return innerf = outer()
f()

输出:

1
101

3.6 一个简单的装饰器

嵌套函数实现

输入:

import timedef timer(func):def inner():print("inner run")start = time.time()func()end = time.time()print("{} 函数运行用时{:.2f}秒".format(func.__name__, (end-start)))return innerdef f1():print("f1 run")time.sleep(1)f1 = timer(f1)             # 包含inner()和timer的环境,如传递过来的参数func
f1()

输出:

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

语法糖

输入:

import timedef 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秒

3.7 装饰有参函数

输入:

import timedef 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 timedef 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 resreturn 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

3.8 带参数的装饰器

装饰器本身要传递一些额外参数

  • 需求:有时需要统计绝对时间,有时需要统计绝对时间的2倍

输入:

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 resreturn innerreturn outer@timer(method="origin")  # 相当于timer = timer(method = "origin")   f1 = timer(f1)
def f1():print("f1 run")time.sleep(1)@timer(method="double")
def f2():print("f2 run")time.sleep(1)f1()
print()
f2()

输出:

inner run
origin_inner run
f1 run
f1 函数运行用时1.00秒inner run
double_inner run
f2 run
f2 函数运行双倍用时2.00秒

理解闭包是关键!!!

3.9 何时执行装饰器

  • 一装饰就执行,不必等调用

输入:

func_names=[]
def find_function(func):print("run")func_names.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")

输出:

run
run
run

输入:

for func in func_names:print(func.__name__)func()print()

输出:

f1
f1 runf2
f2 runf3
f3 run

3.10 回归本源

  • 原函数的属性被掩盖了

输入:

import timedef 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():time.sleep(1)print("f1 run")print(f1.__name__)    

输出:

inner
  • 回来

输入:

import time
from functools import wrapsdef 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():time.sleep(1)print("f1 run")print(f1.__name__)
f1()

输出:

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

五、重难点回顾

1. 数据类型底层实现

2. 更简洁的语法

3. 三大神器

【Python】8.有益的探索相关推荐

  1. 【深度之眼五——(1)】Python:有益的探索

    目录 一,数据类型的底层实现 1错综复杂的复制 1.1列表的底层实现 1.1.1新增元素 1.1.2修改元素 1.2.3对列表型元素进行操作 1.1.3对元组型元素进行操作 1.1.4对字典型元素进行 ...

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

    Table of Contents 1  第九章 有益的探索 1.1  数据类型的底层实现 1.1.1  奇怪的列表 1.1.2  神秘的字典 1.1.3  紧凑的字符串 1.1.4  是否可变 1. ...

  3. Kaggle 房价预测竞赛优胜方案:用 Python 进行全面数据探索

    [导读]Kaggle 的房价预测竞赛从 2016 年 8 月开始,到 2017 年 2 月结束.这段时间内,超过 2000 多人参与比赛,选手采用高级回归技术,基于我们给出的 79 个特征,对房屋的售 ...

  4. 房价预测python_详解 Kaggle 房价预测竞赛优胜方案:用 Python 进行全面数据探索...

    [导读]Kaggle 的房价预测竞赛从 2016 年 8 月开始,到 2017 年 2 月结束.这段时间内,超过 2000 多人参与比赛,选手采用高级回归技术,基于我们给出的 79 个特征,对房屋的售 ...

  5. python和建筑工程项目的结合_工程实践项目驱动的Python课程教学改革探索

    工程实践项目驱动的 Python 课程教学改革探索 文欣秀,王占全,范贵生,赵 敏,杨泽平 [摘 要] 为提升高校非计算机专业学生运用计算机分析和解决专业问题的能力, 结合新工科建设要求,基于工程实践 ...

  6. python做运动控制_ROS探索总结-61.MoveIt!编程驾驭机械臂运动控制

    ROS探索总结-61.MoveIt!编程驾驭机械臂运动控制 说明: 介绍MoveIt!编程驾驭机械臂运动控制 正文 本讲我们将从以下四个部分进行讲解. 首先来回顾下MoveIt!编程接口的框架. Mo ...

  7. python设置路径_Python探索之修改Python搜索路径

    当Python执行import语句时,它会在一些路径中搜索Python模块和扩展模块.可以通过sys.path查看这些路径,比如: >>> import sys >>&g ...

  8. python matplotlib数据可视化教程_matplotlib的Python数据可视化和探索——入门指南

    matplotlib--最受欢迎的Python库,用于数据可视化和探索 我喜欢在Python中使用matplotlib.这是我学会掌握的第一个可视化库,此后一直存在.matplotlib是最受欢迎的用 ...

  9. 我与Python | 从Hacker到探索Deep Learning

    北京 | 高性能计算之GPU CUDA课程11月24-26日3天密集学习 快速带你晋级阅读全文> 作者,李良,中国海洋大学.SEI实验室B519NLP小组长组长. 为什么是Python 人生苦短 ...

最新文章

  1. 【百度之星2017】资格赛-题3-度度熊与邪恶大魔王
  2. 将iPad, iPhone直投屏幕用于TEASOFT课程录制
  3. 读书 - Delphi下深入Windows核心编程 第二天
  4. MATLAB从入门到精通-matlab计算多重复杂多边形重叠面积
  5. Servlet笔记2-文件上传
  6. hdu3033---加限制条件的0-1背包
  7. Linux Shell中有三种引号的用法
  8. data layui table 排序_使用pandas的pivot_table方法统计2019年各省份GDP最大最小值及其对应的所在城市...
  9. python文件加锁
  10. python清空画布_关于python:如何清除Tkinter画布?
  11. linux 运行 ccs,ccs_linux
  12. HWSD土壤数据库介绍
  13. 非线性动力学_利用非线性动力学系统研究混沌现象
  14. 华盛顿大学计算机硕士什么时候录取,2020年华盛顿大学排名TFE Times美国最佳计算机科学硕士专业排名第5...
  15. 写一段情人节耳钉礼物文案,要求100字左右,以小红书的形式展示,带emoji表情包,带分段,吸引眼球,情绪化,最后加一些tag...
  16. css实现旋转的小箭头
  17. Rouge的安装与使用
  18. Sigmoid函数解析
  19. Softer-NMS
  20. 细说中国各省省名的由来(zt)

热门文章

  1. Android 通知栏图标
  2. 大一期末项目:学生管理系统
  3. Spark jars依赖问题
  4. 【Flink】Flink 消费 kafka retries和retry.backoff.ms 引起问题
  5. 移动端APP测试总结
  6. [NOIP模拟测试30]题解
  7. 瀑布模型、迭代模型和敏捷开发
  8. 关于canvas的学习心得(三)(计算错误)
  9. java安卓软件开发_9大安卓app开发软件盘点,0基础自己也能开发开发安卓app
  10. 使用 PD Recover 恢复 PD 集群