壹拾叁

函数进阶

一、闭包函数

1.1 什么是闭包

​ 闭包:闭是封闭(函数内部函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用)。闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。

1.2 两种为函数传参的方式

​ 1.使用参数的形式:

def func(x)
	print(x)
func(1)
func(1)
func(1)

1
1
1

​ 2.包给函数:

def outter(x)
	x = 1
    
    def inner()
    print(x)
    return inner
f = otter(1)
f()
f()
f()

1
1
1

1.3 闭包函数的应用

​ 闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。

​ 应用领域:延迟计算(原来我们是传参,现在我们是包起来)、爬虫领域。

import request

def get(url):
    def get():
        respose = requests.get(url)
        print(f'done:{url}')
    return get

baidu=outter('https://www.baidu.com')
python = outter('https://www.python.org')

baidu()
baidu()
python()
python()

done: https://www.baidu.com
done: https://www.baidu.com
done: https://www.python.org
done: https://www.python.org

二、装饰器

2.1 无参装饰器

2.1.1 什么是装饰器

​ 装饰器指的是为被装饰器对象添加额外功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能。

​ 需要注意的是:

  • 装饰器本身其实是可以任意可调用的对象
  • 被装饰的对象也可以是任意可调用的对象

2.1.2 为什么使用装饰器

​ 装饰器有两条原则:

  1. 不修改被装饰对象的源代码

  2. 不修改被装饰对象的调用方式

    ​ 装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。即不修改代码运行方式的情况下为其增加新功能。

2.1.3 装饰器模板

def deco(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper

2.1.4 装饰器名

​ 在被装饰函数正上方,并且是单独一行写上@装饰器名

二、有参装饰器

​ 三层闭包装饰器。

​ 由于两层的装饰器,参数必须得固定位func,但是三层的装饰器解除了这个限制。我们不仅仅可以使用上述单个参数的三层装饰器,多个参数的只需要在三层装饰器中多加入几个参数即可。也就是说装饰器三层即可,多加一层反倒无用。

三、迭代器

​ 迭代器,即迭代工具。

3.1可迭代对象

​ 在python中一切皆是对象,如数字、字符串、列表、字典等等,对于这些对象,凡是可以使用__iter__方法的对象,都是可迭代对象。

# x = 1.__iter__  # SyntaxError: invalid syntax

# 以下都是可迭代的对象
name = 'nick'.__iter__
lis = [1, 2].__iter__
tup = (1, 2).__iter__
dic = {'name': 'nick'}.__iter__
s1 = {'a', 'b'}.__iter__
f = open('49w.txt', 'w', encoding='utf-8')
f.__iter__
f.close()

​ Python内置str、list、tuple、dict、set、file都是可迭代对象。

3.2 迭代器对象

​ 只有字符串和列表都是依赖索引取值的,而其他的可迭代对象都是无法依赖索引取值的。因此我们得找到一个方法能让其他的可迭代对象不依赖索引取值。

​ 在找到该方法前,首先我们给出迭代器对象的概念:可迭代的对象执行__iter__方法得到的返回值。并且可迭代对象会有一个__next__方法。

# 不依赖索引的数据类型迭代取值
dic = {'a': 1, 'b': 2, 'c': 3}
iter_dic = dic.__iter__()
print(iter_dic.__next__())
print(iter_dic.__next__())
print(iter_dic.__next__())
# print(iter_dic.__next__())  # StopIteration:

a
b
c
# 依赖索引的数据类型迭代取值
lis = [1, 2, 3]
iter_lis = lis.__iter__()
print(iter_lis.__next__())
print(iter_lis.__next__())
print(iter_lis.__next__())
# print(iter_lis.__next__())  # StopIteration:
1
2
3

3.3 总结

​ 迭代器对象:执行可迭代对象的__iter__方法,拿到的返回值就是迭代器对象。

​ 特点:

  1. 内置__next__方法,执行该方法会拿到迭代器对象中的一个值

  2. 内置有__iter__方法,执行该方法会拿到迭代器本身

  3. 文件本身就是迭代器对象。

    ​ 缺点:

  4. 取值麻烦,只能一个一个取,并且只能往后取,值取了就没了

  5. 无法使用len()方法获取长度。

3.4 for循环原理

​ for循环称为迭代器循环,in后必须是可迭代的对象。

lis = [1, 2, 3]
for i in lis:
    print(i)
1
2
3

因为迭代器使用__iter__后还是迭代器本身,因此for循环不用考虑in后的对象是可迭代对象还是迭代器对象。

由于对可迭代对象使用__iter__方法后变成一个迭代器对象,这个迭代器对象只是占用了一小块内存空间,他只有使用__next__后才会吐出一个一个值。如lis = [1,2,3,4,5,...]相当于一个一个鸡蛋,而lis = [1,2,3,4,5,...].__iter__相当于一只老母鸡,如果你需要蛋,只需要__next__即可。

posted @ 2019-08-12 21:14  唐ceng  阅读(275)  评论(0编辑  收藏  举报