第四篇: python函数续 1、函数的嵌套 2、名称空间和作用域 3、闭包函数 4、装饰器  5、生成器 6、迭代器  

第四篇: python函数续
1、函数的嵌套
2、名称空间和作用域
3、闭包函数
4、装饰器 
5、生成器
6、迭代器  

函数的嵌套调用:在调用一个函数的过程中,又调用了其它函数

示例1:

def bar():
    print('from nbar')

def foo():
    print('from foo')
    bar()
foo()
#执行结果为
C:UsersAdministratorAppDataLocalProgramsPythonPython36python.exe C:/Users/Administrator/PycharmProjects/python18/day4/函数嵌套.py
from foo
from nbar

示例2:求函数所带四个参数的最大值

def max2(x,y):
    if x > y:
        return x
    else:
        return y

def max4(a,b,c,d):
    res1=max2(a,b)
    res2=max2(res1,c)
    res3=max2(res2,d)
    return res3
print(max(1,2,3,-1))

#执行结果如下
C:UsersAdministratorAppDataLocalProgramsPythonPython36python.exe C:/Users/Administrator/PycharmProjects/python18/day4/函数嵌套.py
3

函数的嵌套定义:在一个函数的内部,又定义另外一个函数

2、名称空间和作用域

名称空间:存放名字的地方,准确的说名称空间是存放名字与变量值绑定关系的地方
名称空间共有三种名称空间既:
  1、内置名称空间:在python解释器启动时产生,存放一些python内置的名字
  2、全局名称空间:在执行文件时产生,存放文件级别定义的名字
  3、局部名称空间:在执行文件的过程中,如果调用了函数,则会产生该函数的局部名称空间,用来存放该函数内定义的名字,该名字在函数调用时生效,在函数调用结束后失效

加载顺序:内置===》全局===》局部


优先掌握一:名字的查找顺序是:局部==》全局===》内置


作用域:作用的范围
全局作用域:全局存货,全局有效:globals()


局部作用域:临时存活,局部生效:locals()
x=11111111111111111111111111111111111111111111

# def f1():
#     x=1
#     y=2
#     def f2():pass
#     # print(locals())
#     print(globals())
#
# f1()
# print(locals() is globals())
# print(locals())
#
# print(dir(globals()['__builtins__']))

#global nonlocal掌握
# x=1
# def f1():
#     global x
#     x=2
#
# f1()
# print(x)


# l=[]
# def f2():
#     l.append('f2')
#
# f2()
# print(l)


# x=0
# def f1():
#     # x=1
#     def f2():
#         # x=2
#         def f3():
#            # global x
#            nonlocal x
#            x=3
#         f3()
#         # print(x)
#     f2()
#     print(x)
# f1()
# print(x)

 

优先掌握二:作用域关系,在函数定义时就已经固定,于调用位置无关,在调用函数时,必须必须必须回到函数原来定义的位置去找作用域关系

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


# func=f1()
# print(func)
# x=10000000
# func()
# x=10000000


def foo(func):
    x=300000000
    func() #f2()
x=10000000000000000000000


foo(f1())
# x=10000000000000000000000
# foo(f1())

  

3、闭包函数

1. 定义在函数内部的函数
2. 包含对外部作用域名字的引用,而不是对全局作用域名字的引用,那么该内部函数就称为闭包函数

# x=1
# def  f1():
#     x=11111111111
#     def f2():
#         print(x)
#     return f2
#
# func=f1()


# x=1000
# func()

# def foo():
#     x=12312312312312312312312312312312312313123
#     func()
#
#
# foo()


# def deco():
#     x=123123123123
#     def wrapper():
#         print(x)
#     return wrapper
#
# func=deco()


# func()

闭包函数的应用:惰性计算

import requests #pip3 install requests

# def get(url):
#     return requests.get(url).text
#
# print(get('https://www.python.org'))
# print(get('https://www.python.org'))
# print(get('https://www.python.org'))
# print(get('https://www.python.org'))

# def index(url):
#     # url='https://www.python.org'
#     def get():
#         # return requests.get(url).text
#         print(requests.get(url).text)
#
#     return get
#
# python_web=index('https://www.python.org')
# baidu_web=index('https://www.baidu.com')

# python_web()
# baidu_web()


name='egon'
def index(url):
    x=1
    y=2
    def wrapper():
        # x
        # y
        # return requests.get(url).text
        print(name)
    return wrapper

python_web=index('https://www.python.org')

# print(python_web.__closure__[0].cell_contents)
print(python_web.__closure__)
# print(python_web.__closure__[0].cell_contents)
# print(python_web.__closure__[1].cell_contents)
# print(python_web.__closure__[2].cell_contents)

4、装饰器 

1、 开放封闭原则:对扩展是开放的,对修改是封闭

2、 装饰器:装饰它人的工具,装饰器本身可以是任意可调用对象,被装饰的对象本身也可以是任意可调用对象
  2.1 装饰器的遵循的原则:

    2.1.1、 不修改被装饰对象的源代码

    2.1.2、 不修改被调用对象的调用方式

装饰器名,必须写在被装饰对象的正上方,并且是单独一行

import time

def timmer(func):
    # func=index
    def wrapper():
        start=time.time()
        func()
        stop=time.time()
        print('run time is %s' %(stop-start))
    return wrapper


@timmer # index=timmer(index)
def index():
    time.sleep(3)
    print('welcome to index')
@timmer # home=timmer(home)
def home():
    time.sleep(2)
    print('welcome to home page')

index()
home()  

装饰器补充

#补充一:wraps

# import time
# from functools import wraps
#
# def timmer(func):
#     @wraps(func)
#     def wrapper(*args,**kwargs):
#         start=time.time()
#         res=func(*args,**kwargs)
#         stop=time.time()
#         print('run time is %s' %(stop-start))
#         return res
#     return wrapper
#
#
# @timmer # index=timmer(index)
# def index():
#     '''这是index函数'''
#     time.sleep(3)
#     print('welcome to index')
#     return 123
#
# print(index.__doc__)
# # print(help(index))

#补充二:一个函数头顶上可以多个装饰器 import time from functools import wraps current_user={'user':None} def timmer(func): @wraps(func) def wrapper(*args,**kwargs): start=time.time() res=func(*args,**kwargs) stop=time.time() print('run time is %s' %(stop-start)) return res return wrapper def auth(auth_type='file'): def deco(func): def wrapper(*args, **kwargs): if auth_type == 'file': if current_user['user']: return func(*args, **kwargs) name = input('name: ').strip() password = input('password: ').strip() with open('db.txt', encoding='utf-8') as f: user_dic = eval(f.read()) if name in user_dic and password == user_dic[name]: res = func(*args, **kwargs) current_user['user'] = name return res else: print('user or password error') elif auth_type == 'mysql': print('mysql') elif auth_type == 'ldap': print('ldap') else: print('not valid auth_type') return wrapper return deco @timmer #index=timmer(wrapper) @auth() # @deco #index=deco(index) #wrapper def index(): '''这是index函数''' time.sleep(3) print('welcome to index') return 123 # print(index.__doc__) # print(help(index)) index()

  

5、生成器

 学习生成器之前,我们先来看看什么是列表生成式

#列表生成式
b = [ i*2 for i in range(10)]
print(b)
 
###########打印输出###########
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,还需要花费很长时间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种调用时才会生成相应数据的机制,称为生成器:generator

要创建一个generator,有很多种方法,第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个生成器

#生成器
l = [ i*2 for i in range(10)]
print(l)
 
g = (i*2 for i in range(10))
print(g)
 
###########打印输出###########
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
#<generator object <genexpr> at 0x0064AAE0>

print(g) 打印出来的信息显示g是一个生成器,创建lg的区别仅在于最外层的[]()l是一个list,而g是一个generator;我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值

#生成器next打印
print(next(g))
#.........                 不断next 打印10次
#..........
print(next(g))
 
###########打印输出###########
#0
#........
#18
#Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
#StopIteration

我们讲过,generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象,所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误

#生成器for调用
g = (i*2 for i in range(10))        #不用担心出现StopIteration错误
for i in g:
    print(i)
 
###########打印输出###########
# 0
# 2
# 4
# 6
# 8
# 10
# 12
# 14
# 16
# 18

generator非常强大。如果推算的算法比较复杂,用列表生成式转换的生成器无法去实现时,我们还可以用函数来实现。比如,著名的斐波拉契数列(Fibonacci)  

#函数表示斐波拉契数列
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n += 1
    return 'done'
 
fib(5)
###########打印输出###########
# 1
# 1
# 2
# 3
# 5

仔细观察,可以看出,fib函数实际上是定义了斐波那契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator;也就是说,上面的函数和generator仅一步之遥,那我们能不能把上面的函数变成一个生成器呢?  

#斐波拉契数列转换为generator
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'
 
print(type(fib(5)))     #打印fib(5)的类型
for i in fib(5):        #for循环去调用
    print(i)
###########打印输出###########
# <class 'generator'>
# 1
# 1
# 2
# 3
# 5

要把fib函数变成generator,只需要把print(b)改为yield b就可以了,这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

但是用for循环调用generator时,会发现拿不到generator的return语句的返回值,也就是return的值没有打印出来,现在我们来看看怎么去打印generator的返回值

#获取generator的返回值
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'
 
g = fib(5)
while True:
    try:
        x = next(g)
        print( x)
    except StopIteration as e:
        print(e.value)
        break
###########打印输出###########
# 1
# 1
# 2
# 3
# 5
# done

如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIterationvalue中,关于如何捕获错误,后面的错误处理还会详细讲解。 

还可通过yield实现在单线程的情况下实现并发运算的效果

import time
def consumer(name):
    print("%s 准备吃包子啦!" %name)
    while True:
       baozi = yield
 
       print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
 
 
def producer(name):
    c = consumer('A')
    c2 = consumer('B')
    c.__next__()        #c.__next__()等同于next(c)
    c2.__next__()
    print("老子开始准备做包子啦!")
    for i in range(10):
        time.sleep(1)
        print("%s做了2个包子!"%(name))
        c.send(i)
        c2.send(i)
 
producer("lzl")

  

6、迭代器  

迭代:是一个重复的过程,每一次重复,都是基于上一次的结果而来  

# while True: #单纯的重复
#     print('你瞅啥')

# l=['a','b','c','d']
# count=0
# while count < len(l):
#     print(l[count])
#     count+=1

dic={'name':'egon','sex':'m',"age":18} #上述按照索引的取值方式,不适于没有索引的数据类型

迭代器:
可迭代对象iterable:凡是对象下有__iter__方法:对象.__iter__,该对象就是可迭代对象

# s='hello'
# l=['a','b','c','d']
# t=('a','b','c','d')
# dic={'name':'egon','sex':'m',"age":18}
# set1={1,2,3}
# f=open('db.txt')

# s.__iter__()
# l.__iter__()
# t.__iter__()
# dic.__iter__()
# set1.__iter__()
# f.__iter__()

迭代器对象:可迭代对象执行内置的__iter__方法,得到的结果就是迭代器对象  

# dic={'name':'egon','sex':'m',"age":18}
#
# i=dic.__iter__()
# # print(i) #iterator迭代器
#
# # i.__next__() #next(i)
# print(next(i))
# print(next(i))
# print(next(i))
# print(next(i)) #StopIteration
#
# l=['a','b','c','d']
#
# i=l.__iter__()
# print(next(i))
# print(next(i))
# print(next(i))
# print(next(i))
# print(next(i)) #StopIteration

不依赖于索引的取值方式  

# l=['a','b','c','d']
# dic={'name':'egon','sex':'m',"age":18}
# iter_l=iter(l)
# iter_dic=iter(dic)
# while True:
#     try:
#         # print(next(iter_l))
#         k=next(iter_dic)
#         print(k,dic[k])
#     except StopIteration:
#         break

什么是迭代器对象:
1 有__iter__,执行得到仍然是迭代本身
2 有__next__

  

迭代器对象的优点
1:提供了一种统一的(不依赖于索引的)迭代方式
2:迭代器本身,比起其他数据类型更省内存

# l=['a','b','c','d']
# i=iter(l)

# dic={'a':1,'b':2}
# x=dic.keys()
# print(x)
# i=x.__iter__()
#
# with open('a.txt') as f:
#     # print(next(f))
#     # print(next(f))
#     # print(next(f))
#     f.read()

迭代器对象的缺点
1:一次性,只能往后走,不能回退,不如索引取值灵活
2:无法预知什么时候取值结束,即无法预知长度

# l=['a','b','c','d']
# i=iter(l)
# print(next(i))
# print(next(i))
# print(next(i))