函数进阶(1)

1 闭包函数

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

闭包函数:传参的另外一种方式,参数+函数包在一起传出去

1.1 函数传参方式

# 函数传参的方式一:使用参数
def inner(x):
    print(x)

inner(1)
inner(1)
inner(1)

# 函数的传参方式二:闭包函数,把变量和函数一起包起来,下次要用直接调用
def outter(x):
    def inner():
        print(x)
    return inner
inner = outter(2) # inner # f = inner
inner()
inner()
inner()

1.2 闭包函数的应用

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

import requests

def get_res(url):
    res = requests.get(url)
    print(res.txt)

get_res('https://www.baidu.com/')
get_res('https://www.baidu.com/')
get_res('https://www.baidu.com/')

get_res('https://www.taobao.com')
get_res('https://www.taobao.com')
get_res('https://www.taobao.com')

get_res('https://www.taobao.com/markets/3c/tbdc?spm=a21bo.2017.201867-links-3.20.5af911d9iW0QkW')
get_res('https://www.taobao.com/markets/3c/tbdc?spm=a21bo.2017.201867-links-3.20.5af911d9iW0QkW')
get_res('https://www.taobao.com/markets/3c/tbdc?spm=a21bo.2017.201867-links-3.20.5af911d9iW0QkW')

上面的方式极其复杂,我们如果使用默认参数也只能解决一个网址,因此我们可以考虑使用闭包的方式。
import requests
def func(url):
    def get_res():
        res = requests.get(url)
        print(res.text)
    return get_res
baidu_spider = func('https://www.baidu.com/')
baidu_spider()
baidu_spider()

taobao_spider = func('https://www.taobao.com')
taobao_spider()
taobao_spider()

2 装饰器

2.1 装饰器的定义

装饰器:装饰的工具(函数),这个函数有装饰的作用

装饰器本质就是一个函数A,装饰的对象也是一个函数B,用一个函数A去装饰一个函数B

装饰器的实现必须遵循两大原则:

​ 1.不改变函数B的调用方式

​ 2.不改变函数B的源代码

def A():
    """装饰器"""
    pass

def B():
    """被装饰的对象"""
    pass

B()

2.2 装饰器的使用

def index():
    """被装饰的函数"""
    print('hello,index')

index()

# 打印函数运行的时间

# 1.改变函数体代码,没改变调用方式
import time

def index():
    start = time.time()
    print('hello,index')
    time.sleep(1)
    end = time.time()
    print(end - start)

index()

# 2没改变调用方式,也没改变源码,但是不通用
import time
def index():
    """被装饰的函数"""
    print('hello,index')

def index1():
    print('hello,index')

start = time.time()
index1()
time.sleep(1)
end = time.time()
print(end - start)

# 装饰器
# 检测index的运行的时间,但是不能改变index的调用方式,以及index的源码
import time

def deco(func): # func = index
    """装饰器函数"""
    def inner():
        start = time.time()
        func() # index
        time.sleep(1)
        end = time.time()
        print(end - start)
    return inner

def index():
    """被装饰的函数"""
    print('hello,index')

def index1():
    print('hello,index1')

index = deco(index) # index = inner
index()

2.3 装饰器语法糖

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

import time

def deco(func):
    def f1(*args,**kwargs):
        print('args:',args) # (10,)
        print('kwargs:',kwargs)
        start = time.time()
        # *args = *(10,)10
        res = func(*args,**kwargs) # 真正的index()
        end = time.time()
        print(end - start)
        
        return res
    return f1

@deco  # 语法糖(更精简的代码) index = deco(index)
def index(x,a=1):
    print('x',x)
    print('a',a)
    print('hello index')
    time.sleep(1)
    return 123

# 重新创建的index = deco(index真正的index)
index = deco(index) # index = f1
res = index(10) # f1(1)

3 三层装饰器

三层装饰器:给双层装饰器加参数

# 判断账号密码来自于哪个地方
def auth(engine):
    def login(func):
        def inner(*args, **kwargs):
            # 登录功能
            if engine == 'file':
                username = input('username:')
                pwd = input('pwd:')
                if username == 'nick' and pwd == '123':
                    print('登录成功')
                    res = func(*args, **kwargs)  # shopping()
                    return res
                else:
                    print('登录失败')
            elif engine == 'db':
                print('账号密码来自于数据库,非法请求')

        return inner
    return login

@auth('db')
def shopping():
    print('shopping')

# login = auth('db') # login = login
# shopping = login(shopping) # shopping = inner
shopping() # inner()

4 装饰器模板

4.1 双层装饰器

def outter(func):
    def wrapper(*args,**kwargs): # wrapper 是未来要运行的函数
        # 加功能
        res = func(*args,**kwargs) # func是被装饰的函数
        return res
    return wrapper

@outter
def shopping():
    print('shopping')

4.2 三层装饰器

def sanceng(engine):
    def outter(func):
        def wrapper(*args, **kwargs):  # wrapper 是未来要运行的函数
            # 加功能
            print(engine)
            res = func(*args, **kwargs)  # func是被装饰的函数
            return res

        return wrapper
    return outter

@sanceng('file')
def shopping():
    print('shopping')

4.3 叠加装饰器

def outter1(func): # func = wrapper2
    def wrapper1(*args,**kwargs): # wrapper 是未来要运行的函数
        print('---------')
        res = func(*args,**kwargs) # func是被装饰的函数 # wrapper2
        print('---------')
        return res
    return wrapper1

def outter2(func): # func = index
    def wrapper2(*args,**kwargs): # wrapper 是未来要运行的函数
        print('1111111111')
        res = func(*args,**kwargs) # func是被装饰的函数 # index()
        print('1111111111')
        return res
    return wrapper2

# @outter1  # index = outter1(index)
# @outter2  # index = outter2(index)  # 先运行最下面的装饰器
# # index
def index():
    print('index')

# index重新定义的index = outter2(index 真正的index)
index = outter2(index)   #  index = wrapper2
# index再一次重新定义的index = outter1(index重新定义的index,即wrapper2)
index = outter1(index)  # index  = wrapper1
index()  # wrapper1()

'''
---------
1111111111
index
1111111111
---------
'''

5 迭代器

5.1 可迭代对象

具有__iter__方法的对象就是可迭代对象

x = 1 # 不可迭代对象
s = 'nick' # 可迭代对象
s.__iter__()
lt = [1,2,3] # 可迭代对象
dic = {'a':1,'b':2} # 可迭代对象
tup = (1,) # 元组只有一个元素必须得加逗号 # 可迭代对象
se = {1,2,3}  # 可迭代对象
f = open('test.py') # 可迭代对象
def func(): # 不可迭代对象
    pass

# 有__iter__()方法的对象就是可迭代对象,然后除了数字类型和函数之外都是可迭代对象

s = 'nick'
s_iter = s.__iter__()
print(s_iter.__next__()) # 基于索引(基于上一次结果)通过__next__进行迭代
print(s_iter.__next__())
print(s_iter.__next__())
print(s_iter.__next__())

dic = {'a':1,'b':2} # 可迭代对象
dic['a']
dic_iter = dic.__iter__() # 不依赖索引取值 # 迭代器对象
print(dic_iter.__next__()) # a
print(dic_iter.__next__()) # b
# print(dic_iter.__next__()) # 报错

5.2 迭代器对象

具有__iter__以及__next__方法的叫做迭代器对象

缺点:

  1. 取值麻烦,只能一个一个取,并且只能往后取,值取了就没了
  2. 无法使用len()方法获取长度
s = 'nick' # 可迭代对象,不属于迭代器对象
s.__iter__()
lt = [1,2,3] # 可迭代对象,不属于迭代器对象
dic = {'a':1,'b':2} # 可迭代对象,不属于迭代器对象
tup = (1,) # 元组只有一个元素必须得加逗号 # 可迭代对象,不属于迭代器对象
se = {1,2,3}  # 可迭代对象,不属于迭代器对象
f = open('test.py') # 可迭代对象,迭代器对象

# 只有文件是迭代器对象

5.3 for循环原理

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

lt = [1,2,3]
lt_iter = lt.__iter__()
while 1:
    try:
        print(lt_iter.__next__())
    except StopIteration:
        break

for i in lt:  # 可迭代对象;迭代器对象 # 不依赖索引取值,而是迭代取值
    print(i)
    
# 1,首先使用iter把lt变成迭代器对象;对于文件也要使用iter方法把文件再一次iter下
# 2. 然后使用next方法进行迭代取值
# 3. 判断StopIteration异常,遇到异常终止

dic ={'a':1,'b':2}
for i in dic:
    print(i)

# 1. 把lt(可迭代对象/迭代器对象)用__iter__方法转换成迭代器对象
# 2. 使用__next__取出迭代器里的所有值
# 3. 使用__next__方法取尽迭代器中的所有值,一定会报错,通过异常捕捉退出while循环

# 解决了不依赖索引取值

f = open('time.py') # 可迭代对象,迭代器对象
f_iter = f.__iter__()
print(id(f_iter))
f_iter_iter = f_iter.__iter__()
print(id(f_iter_iter)) # 两次打印的id值是一样的

dic ={'a':1,'b':2}
dic_iter = iter(dic)
print(next(dic_iter))
print(next(dic_iter))  # 相当于内置方法
posted @ 2019-08-12 15:53  PLPLPL  阅读(139)  评论(0编辑  收藏  举报
// 目录