函数进阶2.0

  • 迭代器
  • 生成器
  • 装饰器

迭代器

可迭代对象

可迭代(iter)是指支持iter的一个对象
通俗地说可以循环的对象就是可循环的对象。

可以用isinstance()判断一个对象是否为可迭代对象

可迭代对象包括:字符串,列表,字典,元组等可以循环的对象

>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>>

迭代器对象

迭代器是指iter所返回的一个支持next(I)的对象

可以被next()函数并不断返回下一个值直到抛出StopIteration异常的对象称为迭代器:Iteraor

python中的迭代对象器:生成器,map、zip、filter

for循环本质

# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
    try:
        # 获得下一个值:
        x = next(it)
    except StopIteration:
        # 遇到StopIteration就退出循环
        break

生成器

Python使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果么不是立即产生结果。这也是生成器的主要好处

定义生成器的两种方法:

1、生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果。每个结果中间,挂起函数的状态,下次从他离开的地方继续执行。

2、生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是构建一个结果列表

比如把列表生成式中的[]改成()。

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

斐波那契数列

著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

fib(5) 

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

def fib(max):
    n,a,b = 0,0,1

    while n < max:
        #print(b)
        yield  b
        a,b = b,a+b

        n += 1

    return 'done'
data = fib(10)
print(data)

print(data.__next__())
print(data.__next__())
print("干点别的事")
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())

注意:生成器只能遍历一次

总结:

生成器和迭代器的本质区别是什么?什么场景用生成器?什么场景下用迭代器?

生成器本质是一个迭代器。生成器不会一次性在内存中返回全部结果的列表,一次只返回一个结果。因此,生成器更加节省内存空间,比如处理一个100万个元素是列表,使用生成器并不会把内存撑爆。

举个生活的例子,吃火锅的时候,使用生成器就相当于把吃火锅的时候刷肥牛。刷一块吃一块,使用迭代器,就相当于整盘肥牛倒进锅里,在捞起来吃。

装饰器

定义
本质是函数:(装饰其他函数)就是为了其他函数添加附加功能

原则

1.不能修改被装饰的函数的源代码

2.不能修改被装饰的函数 的调用方式

例子:

你是一个视频网站的后端工程师,网站有以下几个模块

def home():
    print("---首页----")
 
def america():
    print("----欧美专区----")
 
def japan():
    print("----日韩专区----")
 
def henan():
    print("----河南专区----")

新需求:“欧美”和“河南”板块需要付费,不能改变原代码和调用方式。

user_status=False
def login(func):  # 把要执行的模块从这里传进来

    def inner():  # 再定义一层函数
        _username = "alex"  # 假装这是DB里存的用户信息
        _password = "abc!23"  # 假装这是DB里存的用户信息
        global user_status

        if user_status == False:
            username = input("user:")
            password = input("pasword:")

            if username == _username and password == _password:
                print("welcome login....")
                user_status = True
            else:
                print("wrong username or password!")

        if user_status == True:
            func()  # 看这里看这里,只要验证通过了,就调用相应功能

    return inner  # 用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数
                   #america=inner

@login  #america=login(america);login(america),america=inner  
def america():
    # login() #执行前加上验证
    print("----欧美专区----")


def japan():
    print("----日韩专区----")


@login
def henan():
    # login() #执行前加上验证
    print("----河南专区----")

def home():
    print('--首页--')

home()
america()
henan()

往河南板块传个参数,结果出错了。因为....

调用henan时,其实是相当于调用的login。henan第一次调用时henan = login(henan), login就返回了inner的内存地址,第2次用户自己调用henan("3p"),实际上相当于调用的时inner,但inner定义时并没有设置参数,所以报错。

升级版:

user_status = False #用户登录了就把这个改成True

def login(auth_type): #把要执行的模块从这里传进来
    def auth(func):
        def inner(*args,**kwargs):#再定义一层函数
            if auth_type == "qq":
                _username = "alex" #假装这是DB里存的用户信息
                _password = "abc!23" #假装这是DB里存的用户信息
                global user_status

                if user_status == False:
                    username = input("user:")
                    password = input("pasword:")

                    if username == _username and password == _password:
                        print("welcome login....")
                        user_status = True
                    else:
                        print("wrong username or password!")

                if user_status == True:
                    return func(*args,**kwargs) # 看这里看这里,只要验证通过了,就调用相应功能
            else:
                print("only support qq ")
        return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数

    return auth

def home():
    print("---首页----")

@login('qq')
def america():
    #login() #执行前加上验证
    print("----欧美专区----")

def japan():
    print("----日韩专区----")

@login('weibo')
def henan(style):
    '''
    :param style: 喜欢看什么类型的,就传进来
    :return:
    '''
    #login() #执行前加上验证
    print("----河南专区----")

home()
# america = login(america) #你在这里相当于把america这个函数替换了
#henan = login(henan)

# #那用户调用时依然写
america()

# henan("3p")

练习

1、用装饰器实现计算函数运行时间

import time

def cal(func):
    def wraps():
        start_time = time.time()
        res = func()
        end_time = time.time()
        print("time:", end_time - start_time)
        return res
    return wraps
    
 
@cal
def f1():
    print("in the f1")
    time.sleep(1)
   
   
@cal
def f2():
    print("in the f2")
    time.sleep(2)


@cal
def f3():
    print("in the f3")
    time.sleep(3)
    
f1()
f2()
f3()

2、打印10以下斐波那契数列

姿势1

def fib(max):
	a, b, n = 0,1,0
	while b < max:	
		print(b)
		a,b = b, a+b
		n += 1

	return


fib(10)

姿势2

fibs = [0,1]
for i in range(10):
	fibs.append(fibs[-2]+fibs[-1])

for i in fibs:
	if i <= 10:
		print(i)
原文地址:https://www.cnblogs.com/Jason-lin/p/8326210.html