前言:本博客主要复习下Python进阶知识,迭代器、生成器、装饰器的应用和原理,常用的高阶函数以及赋值、深浅拷贝。
目录
常见数据结构
先来复习下python中常见的数据结构:字典、列表、字符串、元组、集合,以及它们常见的用法和操作。
在处理数据时,经常会用到遍历,那什么是遍历?
从列表、元组、字符串中取出数据,用for i in ... 语句,依次拿到数据,这个过程就是遍历,可以用for循环的对象是可迭代对象,即Iterable。
可迭代对象&迭代器
interable本质是什么?如何自己去实现一个可迭代对象?
示例如下:
from collections.abc import Iterable
class Mylist():
def __init__(self):
self.container = []
def add(self, other):
self.container.append(other)
def __iter__(self):
pass
mylist = Mylist()
print(isinstance(mylist, Iterable))
print(isinstance([], Iterable))
print(isinstance("abcdef", Iterable))
print(isinstance({}, Iterable))
print(isinstance(range(10), Iterable))
可以看到,内部实现这个方法def __iter__(self): ,就是可迭代对象
可迭代对象不是迭代器,iter()可将可迭代对象转为迭代器
迭代器Iterator,实现了__iter__和__next__()。
其中__iter__()方法返回迭代器对象本身 __next__()方法返回容器的下一个元素,直到结尾抛出StopIteration异常。
实现一个迭代器
class Mylist():
def __init__(self,data):
self.__data = data
self.__count = 0
def __iter__(self):
return self
def __next__(self):
if self.__count < len(self.__data):
val = self.__data[self.__count]
self.__count += 1
return val
else:
raise StopIteration
mylist = Mylist([1,2,3,4,5,6])
print(mylist)
for i in mylist:
print(i)
生成器
创建列表可以用列表生成式:[i for i in range(5)]
生成器表达式:(i for i in range(5))
生成器函数使用yield语句而不是return语句返回结果
生成器只能遍历一次
def hanshu(n):
for i in range(n):
yield i
a = hanshu(5)
for i in a:
print(i)
关于生成器的更多内容可以查看下面的这篇博客。
python生成器的原理和业务场景下的使用_colourmind的博客-CSDN博客
装饰器
python里很多@的用法,这个@是什么意思?@就是装饰器的意思。
接下来学习装饰器的应用和原理。
现在有一个需求,将调用接口的每个函数的耗时取出来。
思考:在每个函数内部开始执行前打印时间,调用接口执行结束后打印时间,取两者的差值,每个函数都需要这样取做,非常麻烦,有没有什么更简便的方式呢?
答:装饰器可以实现,运用到了闭包的原理。
示例代码如下:
import time
#定义一个新功能 外部函数
def time_test(func):
#内部函数 调用了被修饰的函数,同时在调用前后计算了时间,可以得到函数的耗时
def inner(*args,**kwargs):
start_time = time.time()
func(*args,**kwargs)
end_time = time.time()
print(end_time - start_time)
return func(*args,**kwargs)
return inner
@time_test #装饰器名称,就是新功能的外部的函数名
def now(a,b,c):
time.sleep(10)
return a+b+c
now(1,2,4)
高阶函数
map函数
语法:map(func,iterable……)
计算列表中每个元素的平方
def square(x):
return x ** 2
list_a = [1,2,4,5,6,7,8,9]
list_new = list(map(square,list_a))
print(list_new)
lambda表达式
两个列表相同位置的元素相加
list_a = [1,2,3,4,5,6,7,8,9]
list_b = [9,8,7,6,5,4,3,2,1]
list_new = list(map(lambda x,y:x+y,list_a,list_b))
print(list_new)
以list_a元素的值作为key,list_b元素的值作为value
list_a = [1,2,3,4,5,6,7,8,9]
list_b = ['a','b','c','d','e','f','g','h','i','j']
dict_new = dict(map(lambda x,y:[x,y],list_a,list_b))
print(dict_new)
reduce函数
reduce(function,sequence[,initial])
最多接收3个参数,第三个参数可传可不传
from functools import reduce
def f(x,y):
return x+y
a = reduce(f,[1,2,3,4,5],80)
print(a)
filter
filter(function or None,iterable)
第一个是函数,第二个是序列,序列的每个元素作为参数传递给函数进行判断,将返回True的元素加到新的列表中。
过滤出奇数
def odd_num(n):
return n % 2 == 1
odd_num = filter(odd_num,[1,2,3,4,5,6,7,8,9])
print(list(odd_num))
过滤出首字母大写的字符串,可以看到如果不用这个函数,需要新建一个空列表,然后遍历调用upper函数再判断,为True加到列表里,非常繁琐。
def upper(n):
return n[0].isupper()
upper_str = filter(upper, ["Python", "javascript", "Learn", "Java", "Good","test"])
print(list(upper_str))
//如果不用filter这个函数
list_upper_str = []
for i in ["Python", "javascript", "Learn", "Java", "Good","test"]:
res = upper(i)
if res == True:
list_upper_str.append(i)
print(list_upper_str)
sorted
对列表中的元素进行排序
nums = [1,35,523,34,342,345,3342,32,54,52,78]
print(sorted(nums,reverse=True))
enumerate
enumerate这个函数,是枚举的意思,enumerate(列表)
for x,y in enumerate(列表) 其中,x--是索引,y--是具体的值
enumerate(list,start=x) start表示下标从x开始
enumerate()函数将可迭代对象组合成一个索引序列,一般用在for循环中
在列表里找某个元素所在的索引,一般要用到enumerate这个函数
如下面的代码,在遍历列表时,可以取出元素所在的下标,enumerate函数的应用很常见
for index,value in enumerate(["python","Java","JavaScript","PHP","C++"]):
print(index,value)
看下面应用的例子:一个数组,已知需要剔除数组的下标,给出剔除后的数组
不用遍历,要用enumerate函数,[n for i, n in enumerate(list_a) if i not in index_list]
list_a = ['香蕉','橘子','火龙果','梨','苹果','柚子','csdn']
index_list = [1,2,6]
lis = [n for i, n in enumerate(list_a) if i not in index_list]
print(list_a)
例子:取列表里第3、4、5位都为0的值所在的索引
[x for x, y in list(enumerate(list_a) if y[2:5] == ['0','0','0'])]
当然上面只是列出了常见的高级函数,除此之外,还有很多高级的函数,需要平时在应用时多加学习和练习。
赋值、深浅拷贝
在比较相等时,可以用==、is,那两者有什么区别?
is 比较的是两个引用是否指向了同一个对象
== 比较的是两个对象是否相等
可以看到下面的例子很形象,a is c返回的是False,两者的id也不同
而a is b返回的是True,两者的id是相同的
import copy
a = [1, 2, 3, 4, [9, 8, 7]]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
print("a的原id地址为:", id(a))
print("给b赋值后,其id地址为:", id(b))
print("浅拷贝a后,其id地址为:", id(c))
print("深拷贝a后,其id地址为:", id(d))
print("修改a的值后")
a.append(5)
a[4].append(10)
print("a的值变为:", a)
print("经过赋值的b的值是:", b)
print("经过浅拷贝后c的值是:", c)
print("经过深拷贝后d的值是:", d)
a = [1,2,3,4]
c = copy.deepcopy(a)
print(a is c) //False
print(a == c) //True
根据上面的代码,用=进行赋值时,b的id没有发生变化,而用到copy.deepcopy()时,c的id发生了变化,那这是为什么呢?
赋值,其本质是复制了新对象的引用,不会开辟新的内存空间,如果a的值改了,b的值当然也随之发生改动。
浅拷贝,创建新的对象,新对象是对原对象的引用。
而深拷贝,是对于一个对象所有层次的拷贝,而不是引用。也就是,把对象复制一遍,并且该对象中引用的其他对象也复制了一遍。
可以看下面的例子,理解下赋值、深浅拷贝。
浅拷贝只拷贝了一层,所以在改变[9,8,7]里元素的值时,也跟着改变了。
此外,具体可参考下面的这篇文章。