一、切片
    在对数据进行操作时,需要对list,tuple或str取出部分元素

    1. l1 = ['Shanghai','Beijing','Shenzheng','Guangzhou','Hangzhou']
    2. '''在l1中取出前4个城市'''
    3. l2 = [l1[0],l1[1],l2[2],l3[3]]

    对于一个长列表,需要取出100个元素

    1. l3 = [1,2,3,4......n] # n > 1000
    2. '''从l3中取出前100个元素'''
    3. l4 = l3[0:100] # l4=[1,2,3,4,....100]

    上述代码中的 l3[0:100]即是切片,从l3索引0开始,一直取到索引100,但不包含索引100,即取索引0,1,2,3……99对应的元素(前包含后不包含)
    当第一个索引为0的时候可简写

    1. l4 = l3[:100] # 第一个索引为0时可省略实现简写

    从元素第2个开始,取出3个元素

    1. l5 = l3[1:4] # l5=[l3[1],l3[2],l3[3]]

    注意索引的前包含后不包含,所以取l3[1], l3[2], l3[3]
    当然Python也支持倒数取元素

    1. l5 = ['a','b','c','d','e']
    2. l6 = l5[-1] # 取l5倒数第一个元素,即'e'
    3. l7 = l5[-3:-1] # 取l5倒数第3个,倒数第2个元素,即l5[-3],l5[-2]

    注:
    通过切片倒数取元素时,同样遵循前包含后不包含;
    倒数第一个元素索引从 -1 开始
    对于一个长数列,通过切片可轻松取出任意一段数列

    1. l8 = list(range(100)) # l8 = [0,1,2,3.....98,99]
    2. l9 = l8[:10] # 取前10个数 [0,1,2,3,4,5,6,7,8,9]
    3. l10 = l8[-10:] # 取后10个数 [90,91,92....99]
    4. l11 = l8[5:10] # 取索引为5-9的元素

    通过切片间隔取元素

    1. l12 = [1,2,3,4,5,6,7,8,9,10]
    2. l13 = l12[0:10:2]
    3. # 对列表l12按索引间隔为2取元素,即l13=[l12[0],l12[2],l12[4],l12[6],l12[8]]
    4. # 1,3,5,7,9

    任务:l8 = list(range(100)) ,通过切片形式,取出l8中所有奇数

    1. l = list(range(100))
    2. l2 = l[1::2]
    3. print(l2)

    任务:尝试一下l14 = l8[ : ]
    tuple也是一种特殊的list,唯一区别就是tuple不可变,因此tuple也可以进行切片,切片结果仍是tuple

    1. t1 = (1,2,3,4,5)
    2. t2 = t1[0:3] # tuple也可进行切片

    字符串进行切片

    1. s1 = 'abcdefg'
    2. s2 = s1[0:4] # str也能进行切片

    字符串第一个元素就是一个字符,切片结果仍是字符串
    任务:给定一个字符串,该字符串首尾都包含空格,通过切片的形式,去除该字符串首尾空格

    1. s3 = ' 测试字符串.....n '
    2. '''s3是一个字符串,首发包含空格,通过切片的形式,生成一个新字符串s4,去除s3的首尾空格'''

    二、迭代(Iteration)
    通过for循环来遍历list或tuple等可迭代对象,Python中通过 for…in 来完成迭代

    1. dic = {'k_1':'v_1','k_2':'v_2','k_3':'v_3'}
    2. for i in dic: # 字典也可迭代,但输出的是key
    3. print (i) # 输出:k_1 k_2 k_3

    迭代字典对象时,默认输出的是字典的所有 key
    如果要迭代字典中的value,可通过 for i in dic.values( ),如果要同时迭代key和value,可以用for k, v in dic.items( )

    1. for i in dic.values(): # 迭代输出字典的value
    2. print(i) # v_1 v_2 v_3
    3. for k, v in d.items(): # 同时迭代key,value
    4. print(f'{k}:{v}') # k_1:v_1 k_2:v_2 k_3:v_3

    迭代字符串

    1. s = "测试字符串"
    2. for i in s: # 迭代字符串
    3. print(i)

    任务:通过迭代,查找list [1,8,4,7,5,3,2]的最大数

    三、列表生成式
    L = [表达式 for 变量 in 可迭代对象 ]
    即:变量从可迭代对象中获取元素作用到表达式中
    遍历列表 [1,2,3,4,5,6,7] 根据公式 2x+1,生成一个新的列表

    1. l1 = [1,2,3,4,5,6,7]
    2. l2 = []
    3. for x in l1:
    4. s = 2*x+1
    5. l2.append(s)
    6. # l2列表[3, 5, 7, 9, 11, 13, 15]

    运用列表生成式简化上述代码

    1. l2 = [2*x+1 for x in l1] #l2列表[3, 5, 7, 9, 11, 13, 15]

    列表生成式中for循环后面可以加if判断进行筛选

    1. l3 = [2*x+1 for x in l1 if x % 2 ==0] # 结果[5, 9, 13]

    上述代码只有满足if 条件后的元素才会参加 2*x+1 运算,注意:列表生成式中if作为筛选条件,后面不能跟else
    列表生成式中for循环后面可以再嵌套for循环

    1. l4 = [x+y for x in '123' for y in 'abc']
    2. # ['1a','1b','1c','2a','2b','2c','3a','3b','3c']

    任务:根据l4列表生成式逻辑,请定义一个函数,使用嵌套的for循环实现上述代码功能
    任务:根据下面代码,改写成列表生成式

    1. l5 = [1,2,3,4,5,6]
    2. l6 = []
    3. for i in l5:
    4. if i >= 3:
    5. s = i*i + 1
    6. l6.append(s)

    四、生成器(generator)
    列表对象对内存的占用,决定于其列表中元素个数,如果上百万个元素的列表不仅会占用很大的内部空间,而且其元素利用率不高将会造成很大的资源浪费,所以生成器,就是按照某种算法推算列表元素,通过循环能不断推断下一个元素,而不必创建整个list,来节省空间,即边循环边计算。
    创建一个生成器
    1.在列表生成式的基础上,把[ ]改成( ),就生成了一个generator

    1. l = [x for x in range(10)]
    2. print(type(l)) # <class 'list'>
    3. l_g = (x for x in range(10))
    4. print(type(l_g)) # <class 'generator'>

    生成器可通过next( )函数来一个一个实现打印元素

    1. next(l_g) # 0
    2. next(l_g) # 1
    3. next(l_g) # 2
    4. next(l_g) # 3

    当next(l_g) 没有更多元素时会抛出StopIteration
    因为 generator 是可迭代对象,可使用for循环迭代

    1. for i in l_g: # l_g是生成器也是迭代器
    2. print(i)

    2.在函数中通过 yield 来定义一个 generator
    在Python程序中函数一般是顺序执行,遇到 return 语句返回结果并结束或执行到函数最后一句后结束运行,但在作为 generator 的函数,每次调用 next( ) 执行时,遇到 yield 就返回,当再次执行时是从上次返回的 yield 语句处继续执行

    1. def new_generator():
    2. print('执行第一次调用')
    3. yield '第一次返回1'
    4. print('执行第二次调用')
    5. yield '第二次返回2'
    6. print('执行第三次调用')
    7. yield '第三次返回3’
    8. n = new_generator()
    9. print(next(n)) # 执行第一次调用
    10. 第一次返回1
    11. print(next(n)) # 执行第二次调用
    12. 第二次返回2
    13. print(next(n)) # 执行第三次调用
    14. 第三次返回3
    15. print(type(n)) # <class 'generator'>
    16. for i in n: # 通过 for 循环迭代 new_generator
    17. print(i)
    18. '''
    19. 执行第一次调用
    20. 第一次返回1
    21. 执行第二次调用
    22. 第二次返回2
    23. 执行第三次调用
    24. 第三次返回3
    25. '''

    五、迭代器(Iterator)
    可直接通过for循环的数据类型有:
    1.集合类数据类型:list, tuple, dict, set, str
    2.generator 生成器
    所有可通过for循环迭代的可称为可迭代对象(Iterable)
    Python基础之高级特性介绍 - 图1
    迭代器:可以被next( )函数不断调用并返回下一个值的对象(Iterator)
    问题:
    1、迭代对象都是迭代器(True or False)?F
    2、迭代器都是迭代对象(Ture or False)?T
    3、生成器属于迭代器 ( Ture or False)?T
    4、list, dict, str 是迭代器(True or False) ?F
    生成器都是Iterator,但list、dict、str虽然是Iterable,却不是Iterator
    Iteration/Iterable/Iterator?
    iter( )函数可把Iterable 变成 Iterator

    1. l = [1,2,3,4,5]
    2. next(l) # TypeError: 'list' object is not an iterator
    3. l_t = iter(l) # 通过iter()函数把list变成一个Iterator
    4. print(next(l_t))
    5. print(next(l_t))
    6. print(next(l_t))

    image.png
    总结:
    1、凡是可用for循环的都是可迭代对象Iterable;
    2、凡是可用next( )函数返回下一值的都是迭代器Iterator;
    3、生成器是通过某种算法推算,边循环边计算的机制,可以通过next( )函数不断返回下一值
    因此,生成器也是一种迭代器;

    六、装饰器
    前提概念一:
    闭包:在函数中再嵌套一个函数,并且引用外部函数的变量,这就是一个闭包了

    1. def outer(x):
    2. def inner(y):
    3. return x + y
    4. return inner
    5. print(outer(6)(5))

    函数中可定义函数

    1. def func_1():
    2. print("正在调用func_1")
    3. def func_1_1():
    4. return "正在调用func_1_1"
    5. def func_1_2():
    6. return "正在调用func_1_2"
    7. print(func_1_1())
    8. print(func_1_2())
    9. print("函数func_1调用结束")
    10. #
    11. 正在调用func_1
    12. 正在调用func_1_1
    13. 正在调用func_1_2
    14. 函数func_1调用结束

    函数中定义的函数,只能在func_1函数内调用
    前提概念二:
    函数中返回函数

    1. def func_1():
    2. print("正在调用func_1")
    3. def func_1_1():
    4. return "正在调用func_1_1"
    5. print("函数func_1调用结束")
    6. return func_1_1
    7. a = func_1() #正在调用func_1
    8. 函数func_1调用结束
    9. print(a) # <function func_1.<locals>.func_1_1 at 0x000001B92BE3DAF0>
    10. print(a()) # 正在调用func_1_1

    问题:为什么 print(a) 与 print(a( ))不一样?
    前提概念三:
    赋值与调用

    1. def func_2():
    2. print("函数func_2被调用")
    3. a = func_2
    4. print(a) # <function func_2 at 0x00000219AAD8F040>
    5. b = func_2() # 函数func_2被调用
    6. c = a() # 函数func_2被调用

    从上述代码可知,当 a = func_2 时,是把func_2函数对象内存地址赋值给变量a,并不会执行func_2这个函数中的代码;当 b = func_2( )时,函数func_2( )会被调用,加上()会运行这个函数中的代码
    装饰器:
    在Python中,可以把函数A作为参数传入另一个函数B,从而实现在不改变函数A原有代码的基础上对函数A增加新功能

    1. def func_A():
    2. print("A函数本身的功能")
    3. def func_B(func):
    4. print("执行func_B开始")
    5. def func_b():
    6. print("func_b执行传入函数前做一些事情")
    7. func()
    8. print("func_b执行传入函数后做一些事情")
    9. print("准备返回func_b")
    10. return func_b
    11. b = func_B(func_A) # func_B()执行返回一个 func_b 内嵌函数对象给func_A调用
    12. '''
    13. 执行func_B开始
    14. 准备返回func_b
    15. func_b执行传入函数前做一些事情
    16. A函数本身的功能
    17. func_b执行传入函数后做一些事情
    18. '''

    上述代码func_B函数就是一个装饰器函数,但需要调用func_B函数来实现对func_A函数的功能拓展,
    通过@符号装饰func_A函数,可实现直接调用func_A函数来达成装饰器的目的

    1. @func_B
    2. def func_A():
    3. print("A函数本身的功能")
    4. func_A()
    5. '''
    6. 执行结果:
    7. 执行func_B开始
    8. 准备返回func_b
    9. func_b执行传入函数前做一些事情
    10. A函数本身的功能
    11. func_b执行传入函数后做一些事情
    12. '''

    被装饰的A函数有单个参数

    1. def func_B(func):
    2. def func_b(x):
    3. return 2*func(x)
    4. return func_b
    5. @func_B
    6. def func_A(x):
    7. return int(x)+1
    8. if __name__ == "__main__":
    9. a = func_A(2)
    10. print(a)
    11. print(func_A.__name__) # 结果为func_b,即func_A被装饰后指向了func_b

    被装饰的A函数有多个参数

    1. def func_B(func):
    2. def func_b(*args,**kwargs):
    3. return 2*func(*args,**kwargs)
    4. return func_b
    5. @func_B
    6. def func_A(x,y):
    7. return int(x)+int(y)
    8. if __name__ == "__main__":
    9. a = func_A(2,3)
    10. print(a)