Blueherb In solitude, where we are least alone

8.12 day13

闭包函数
一、什么是闭包?
闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。

def outter():
x = 1

def inner():
    print(x)
return inner

f = outter()

def f2():
x = 2
f()

f2()

1

1.1 两种为函数传参的方式
为函数传参的方式一:使用参数的形式

def func(x):
print(x)

func(1)
func(1)
func(1)

1

1

1

为函数传参的方式二:包给函数

def outter(x):
x = 1

def inner():
    print(x)
return inner

f = outter(1)
f()
f()
f()

查看闭包的元素

print(F"f.closure[0].cell_contents: {f.closure[0].cell_contents}")

1

1

1

f.closure[0].cell_contents: 1

举个栗子

def f2(x):
def f1():
print(x)
return f1

f1_1 = f2(1) #返回f1 + 1
f1_1() #调用f1,传参x = 1

1

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

应用一(复杂):

import requests

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

get('https://www.baidu.com')
get('https://www.baidu.com')
get('https://www.baidu.com')
get('https://www.cnblogs.com/linhaifeng')
get('https://www.cnblogs.com/linhaifeng')
get('https://www.cnblogs.com/linhaifeng')

https://www.baidu.com

https://www.baidu.com

https://www.baidu.com

https://www.cnblogs.com/linhaifeng

https://www.cnblogs.com/linhaifeng

https://www.cnblogs.com/linhaifeng

应用二(闭包):

爬取

import requests

def func(url):
def get():
res = requests.get(url)
print(res.text)
return get

baidu_spider = func('http://www.iqiyi.com/')
baidu_spider()
无参装饰器
装饰器指的是为被装饰器对象添加额外功能,就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能。

一、装饰器使用原则
装饰器的实现必须遵循两大原则:

不修改被装饰对象的源代码
不修改被装饰对象的调用方式
二、怎么用装饰器?
改变源代码(index内部代码块被改变):

import time

def index():
start = time.time()
print('welcome to index')
time.sleep(1)
end = time.time()
print(F"index run time is {start-end}")

index()

welcome to index

index run time is -1.0008180141448975

编写重复代码(index( )和f2( )都调用,而且time代码重复):

import time

def index():
print('welcome to index')
time.sleep(1)

def f2():
print('welcome to index')
time.sleep(1)

start = time.time()
index()
end = time.time()
print(F"index run time is {start-end}")

start = time.time()
f2()
end = time.time()
print(F"f2 run time is {start-end}")

welcome to index

index run time is -1.0046868324279785

welcome to index

f2 run time is -1.000690221786499

三、两种传参方式
第一种传参方式:改变调用方式

import time

def index():
print('welcome to index')
time.sleep(1)

def time_count(func):
start = time.time()
func() #func()=index(),打印'welcome to index'
end = time.time()
print(f"{func} time is {start-end}") #打印

time_count(index) #运行time_count(),同时传参给func

welcome to index

<function index at 0x102977378> time is -1.000748872756958

第二种传参方式:包给函数-外包

import time

def index():
print('welcome to index')
time.sleep(1)

def time_count(func):
# func = 最原始的index
def wrapper():
start = time.time()
func()
end = time.time()
print(f"{func} time is {start-end}")
return wrapper

f = time_count(index)

f()

index = time_count(index) # index被重新定义,原来的index被覆盖,即index = wrapper,func = index
index() # wrapper()

依旧是以index()方式调用

welcome to index

<function index at 0x102977730> time is -1.0038220882415771

四、完善装饰器
上述的装饰器,最后调用index()的时候,其实是在调用wrapper(),因此如果原始的index()有返回值的时候,wrapper()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和wrapper()方法的返回值。

import time

def index():
print('welcome to index')
time.sleep(1)

return 123

def time_count(func):
# func = 最原始的index
def wrapper():
start = time.time()
res = func()
end = time.time()
print(f"{func} time is {start-end}")

    return res
return wrapper

index = time_count(index) #index = wapper, func = index
res = index() #wapper(index)
print(f"res: {res}")

welcome to index

<function index at 0x102977620> time is -1.0050289630889893

res: 123

如果原始的index()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于有wrapper()=index(),所以给wrapper()方法传参即可。

import time

def index():
print('welcome to index')
time.sleep(1)

return 123

def home(name):
print(f"welcome {name} to home page")
time.sleep(1)

return name

def time_count(func):
# func = 最原始的index
def wrapper(args, **kwargs):
start = time.time()
res = func(
args, **kwargs)
end = time.time()
print(f"{func} time is {start-end}")

    return res
return wrapper

home = time_count(home) #func = home, home = wrapper
res = home('egon') #wrapper('egon') = fun('egon') = home('egon') = 'welcome egon to home page' = 'egon'
print(f"res: {res}") # res = func('egon') = home('egon') = 'egon'

welcome egon to home page

<function home at 0x102977378> time is -1.0039079189300537

res: egon

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

import time

def time_count(func):
# func = 最原始的index
def wrapper(args, **kwargs):
start = time.time()
res = func(
args, **kwargs)
end = time.time()
print(f"{func} time is {start-end}")

    return res
return wrapper

@time_count # home = time_count(home)
def home(name):
print(f"welcome {name} to home page")
time.sleep(1)

return name

@time_count # index = time_count(index)
def index():
print('welcome to index')
time.sleep(1)

return 123

res = home('egon') #func = home name = 'egon' --> welcome egon to home page --> print(f"{func} time is {start-end}")
print(f"res: {res}") # egon

welcome egon to home page

<function home at 0x102977620> time is -1.0005171298980713

res: egon

六、装饰器模板(重要)
def deco(func):
def wrapper(args,**kwargs):
res = func(
args,**kwargs)
return res
return wrapper
有参装饰器
无参装饰器套了两层,有参装饰器套了三层。

import time

current_user = {'username': None}

def login(func):
# func = 最原始的index
def wrapper(args, **kwargs):
if current_user['username']:
res = func(
args, **kwargs)

        return res

    user = input('username: ').strip()
    pwd = input('password: ').strip()

    if user == 'nick' and pwd == '123':
        print('login successful')
        current_uesr['usre'] = user
        res = func(*args, **kwargs)

        return res
    else:
        print('user or password error')

return wrapper

@login #home = login(home)
def home(name):
print(f"welcome {name} to home page")
time.sleep(1)

return name

@login #index = login(index)
def index():
print('welcome to index')
time.sleep(1)

return 123

res = index() #运行index之前运行糖果 wrapper(index) 继续运行 返回res = func 继续运行 即真正的index()

username: nick

password: 123

login successful

welcome to index

对于上面的登录注册,我们把用户登录成功的信息写入内存当中。但是在工业上,用户信息可以存在文本中、mysql中、mongodb当中,但是我们只让用户信息来自于file的用户可以认证。因此我们可以改写上述的装饰器。

import time

current_user = {'username': None}

def login(func):
# func = 最原始的index
def wrapper(*args, **kwargs):

    if current_user['username']:
        res = func(*args, **kwargs)

        return res

    user = input('username: ').strip()
    pwd = input('password: ').strip()
    
    engine = 'file'

    if engine == 'file':
        print('base of file')
        if user == 'nick' and pwd == '123':
            print('login successful')
            current_uesr['usre'] = user
            res = func(*args, **kwargs)

            return res
        else:
            print('user or password error')
    elif engine == 'mysql':
        print('base of mysql')
    elif engine == 'mongodb':
        print('base of mongodb')
    else:
        print('default')

return wrapper

@login
def home(name):
print(f"welcome {name} to home page")
time.sleep(1)

@login
def index():
print('welcome to index')
time.sleep(1)

res = index()

username: nick

password: 123

base of file

login successful

welcome to index

一、三层闭包
def f1(y):

def f2():
    x = 1

    def f3():
        print(f"x: {x}")
        print(f"y: {y}")
        return f3
    return f2

f2 = f1(2) #f2(y = 2)
f3 = f2() #x = 1
f3() #到外部找x, y

倒着运行--->先找x, y--->找到x = 1--->y = 2--->结果输出

x: 1

y: 2

现在需求改了,我们需要判断用户动态的获取用户密码的方式,如果是file类型的,我们则让用户进行认证。因此我们可以使用有参装饰器。

import time

current_uesr = {'username': None}

def auth(engine='file'):

def login(func):
    # func = 最原始的index
    def wrapper(*args, **kwargs):

        if current_user['username']:
            res = func(*args, **kwargs)

            return res

        user = input('username: ').strip()
        pwd = input('password: ').strip()

        if engine == 'file':
            print('base of file')
            if user == 'nick' and pwd == '123':
                print('login successful')
                current_uesr['usre'] = user
                res = func(*args, **kwargs)

                return res
            else:
                print('user or password error')
        elif engine == 'mysql':
            print('base of mysql, please base of file')
        elif engine == 'mongodb':
            print('base of mongodb, please base of file')
        else:
            print('please base of file')

        return wrapper

    return login

@auth(engine='mysql')
def home(name):
print(f"welcome {name} to home page")
time.sleep(1)

@auth(engine='file')
def index():
print('welcome to index')
time.sleep(1)

res = index()

username: nick

password: 123

base of file

login successful

welcome to index

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

迭代器
迭代器:迭代的工具。

迭代是更新换代,比如你子孙繁衍;迭代也可以说成是重复,但每一次的重复都是基于上一次的结果来的,例如计算机中的迭代开发,就是基于软件的上一个版本更新。

以下代码就不是迭代,它只是单纯的重复

while True:
print(''10)

一、可迭代对象
定义:具有__iter__方法的对象就是可迭代对象,除了数字类型和函数类型。

注意:tuple(1)与tuple(1,)类型有区别

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()
二、迭代器对象
只有字符串和列表都是依赖索引取值的,而其他的可迭代对象都是无法依赖索引取值的。因此我们得找到一个方法能让其他的可迭代对象不依赖索引取值。

定义:具有__iter__和__next__方法的都是迭代器对象,只有文件。

缺点:

取值麻烦,只能一个一个取,并且只能往后取,值取了就没了
无法使用len()方法获取长度

不依赖索引的数据类型迭代取值

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

上述的方法是非常繁琐的,我们可以使用while循环精简下。其中使用的try...except...为异常处理模块

for循环原理

lt = [1,2,3]

lt_iter = lt.iter()
while 1:
try:
print(lt_iter.next())
except StopIteration:
break
注意一:迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象

注意二:for循环 == 迭代循环

注意三:迭代器对象使用__iter__()还是迭代器对象

posted @ 2019-08-12 20:18  奏乐乐章  阅读(109)  评论(0编辑  收藏  举报