函数、迭代器、生成器、装饰器

被刻印的时光 ゝ 提交于 2019-12-27 09:53:23

函数

函数基础

一、为什么要使用函数

把重复的代码提取出来,节省代码量

 

二、函数分类

内置函数,如len(),sun()

自定义函数

 

三、如何定义函数

def 函数名(参数1,参数2,参数3,...):
    '''注释'''
    函数体
    return 返回的值

 

四、函数使用的原则:先定义,后使用

 

五、调用函数

1、如何调用函数:函数名加括号

2、函数的返回值

函数的返回值通常都是一个,如果出现 return a,b 这种情况,其实是表示一个元组

 

 六、函数的参数

#1、位置参数:按照从左到右的顺序定义的参数

位置形参:必选参数

位置实参:按照位置给形参传值,顺序必须一一对应

 

#2、关键字参数:按照key=value的形式定义的实参

无需按照位置为形参传值

注意的问题:

1. 关键字实参必须在位置实参右面

2. 对同一个形参不能重复传值

 

#3、默认参数:形参在定义时就已经为其赋值

可以传值也可以不传值,经常需要变得参数定义成位置形参,变化较小的参数定义成默认参数(形参)

注意的问题:

1. 只在定义时赋值一次

2. 默认参数的定义应该在位置形参右面

3. 默认参数通常应该定义成不可变类型,举例

def add_end(L=[]):
    print(id(L))
    L.append('END')
    return L

print(add_end())
print(add_end())

结果:

2822355733768
['END']
2822355733768
['END', 'END']

 

解释:python函数在定义好时,形参、内部参数等变量就已经有了固定的内存空间(变量始终指向那一块内存,不会更改)。默认参数形参变量如果是可变的,下一次调用该函数时,默认参数就已经发生了变化。

 

#4、可变长参数: 可变长指的是实参值的个数不固定

而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*args,**kwargs

===========*args===========

def foo(x,y,*args):   print(x,y)  print(args) foo(1,2,3,4,5) def foo(x,y,*args):   print(x,y)   print(args) foo(1,2,*[3,4,5]) def foo(x,y,z):   print(x,y,z) foo(*[1,2,3])当形参为*args,实参可以用 *()或者 *[] 的方式将若干个参数打包发送给形参

===========**kwargs===========

def foo(x,y,**kwargs):   print(x,y)   print(kwargs) foo(1,y=2,a=1,b=2,c=3) def foo(x,y,**kwargs):   print(x,y)   print(kwargs) foo(1,y=2,**{'a':1,'b':2,'c':3}) def foo(x,y,z):   print(x,y,z) foo(**{'z':1,'x':2,'y':3}) 当形参为**kwargs时,实参可以通过**{}的方式将若干参数打包发送给形参

 

===========*args+**kwargs===========

def foo(x,y):   print(x,y) def wrapper(*args,**kwargs):   print('====>') foo(*args,**kwargs)

 

#5、命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递,可以保证,传入的参数中一定包含某些关键字

和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数(如果参数中已经有了可变参数*args,则不需要 * 来特别标明)

def foo(x,y,*args,a=1,b,**kwargs):
  print(x,y)
  print(args)
  print(a)
  print(b)
  print(kwargs)
foo(1,2,3,4,5,b=3,c=4,d=5)
结果: 1 2 (3, 4, 5) 1 3 {'c': 4, 'd': 5}

 

闭包

一、函数是对象

函数可以当做参数传递,也可以返回一个函数

利用该特性,可以写成一个类似C语言switch case语法的功能,取代多分支if

def foo():
    print('foo')

def bar():
    print('bar')

dic={
    'foo':foo,
    'bar':bar,
}
while True:
    choice=input('>>: ').strip()
    if choice in dic:
        dic[choice]()

 

二、函数嵌套、名称空间、作用域的概念

1、函数嵌套调用

函数嵌套定义

 

2、名称空间

存放名字的地方,x=1,1存3、放于内存中,那名字x存放在哪里呢?名称空间是存放名字x与1绑定关系的地方,L = [1,2,3],L存放在名称空间里,真正的列表在其他地方。

3、作用域 

#1、作用域即范围

-全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效  

-局部范围(局部名称空间属于该范围):临时存活,局部有效

内部的函数可以访问外部的变量,但是外面的函数访问不了内部函数

 

#2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关

 

#3、查看作用域:globals(),locals()

python中globals和nonlocals的用法

 

LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__

locals 是函数内的名字空间,包括局部变量和形参

enclosing 外部嵌套函数的名字空间(闭包中常见)

globals 全局变量,函数定义所在模块的名字空间

builtins 内置模块的名字空间

 

闭包函数

内部函数包含对外部函数而非全局作用域的引用

def counter():
    n = 0
    def incr():
        nonlocal n
        x = n
        n += 1
        return x
    return incr

c = counter()
print(c())
print(c())
print(c())
print(c.__closure__[0].cell_contents)  # 查看闭包的元素

 

结果:

0
1
2
3

 

闭包函数的意义:

返回的函数对象,不仅仅是一个函数,在该函数外还包裹了一个外层作用域(一般含是变量),这使得,该函数无论在何处调用,都优先使用自己外层包裹的作用域,相对于更外层的比如全局作用域。

实现延迟计算的功能:

普通函数调用后立即函数执行,闭包则是给函数包裹上了一层外层作用域,传了参数的功能。

 

装饰器

1、装饰器是闭包函数的一种应用

2、装饰器可以在不修改原函数功能的前提下,添加额外功能。开放封闭原则

 

#不带参数的装饰器

import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper

@timmer   #相当于foo = timmer(foo)
def foo():
    time.sleep(3)
    print('from foo')
foo()#利用闭包的特点:1、具有延迟计算的功能,函数不是立即执行;2、闭包相当于包裹了一个外层作用域的函数。当timmer函数执行后,形参 func,原函数作为一个对象传到了形参,传给了内层函数 wrapper,即使timmer执行完毕了,形参会一直存在着。

 

#带参数的装饰器

def auth(driver='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            name=input("user: ")
            pwd=input("pwd: ")

            if driver == 'file':
                if name == 'egon' and pwd == '123':
                    print('login successful')
                    res=func(*args,**kwargs)  #原函数其实就一个调用,其他都是额外功能,装饰作用
                    return res
            elif driver == 'ldap':
                print('ldap')
        return wrapper
    return auth2

@auth(driver='file')
def foo(name):
    print(name)

foo('egon')

 

迭代器

1、为什么要有迭代器:

有序序列,如列表,字符串可以通过索引的方式取出其中的元素,而对于字典,文件,集合等类型,是没用索引的,所以需要通过不依靠索引的迭代方式,就是迭代器。

2、什么是可迭代对象:

可迭代对象指的是实现了__iter__方法的对象

3、什么是迭代器(对象):

可迭代对象执行obj.__iter__()得到的结果就是迭代器对象,同时,迭代器内部还有__next__方法

文件类型是可迭代器对象
open('a.txt').__iter__()
open('a.txt').__next__()

注意:

迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

 

迭代器对象的使用

dic = {'a':1, 'b':2, 'c':3}

iter_dic = dic.__iter__()   #得到一个迭代器

注意:迭代器.__iter__() 得到仍然是迭代器本身

print(iter_dic.__next__()) #等同于next(iter_dic)

print(iter_dic.__next__()) #等同于next(iter_dic)

print(iter_dic.__next__()) #等同于next(iter_dic)

print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志

iter_dic=dic.__iter__()
while 1:
    try:
        k=next(iter_dic)
        print(dic[k])
    except StopIteration:
        break
通过next方法从迭代器取数据,需要手动捕获异常

 

 

for循环执行next方法,并且自动捕获异常

dic={'a':1,'b':2,'c':3}
for k in dic:
    print(dic[k])

for循环的工作原理
1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
3: 重复过程2,直到捕捉到异常StopIteration,结束循环

 

生成器

概念:

只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,加括号不会执行函数内部的代码

注意:生成器是一种特殊的迭代器,生成器内置__next__,__iter__方法

 

send方法的使用:

def range2(n):
    count =0
    while count < n:
        print('count',count)
        count += 1
        sign = yield count
        print('-------sign',sign)
    return 333

new_range = range2(3)
n1 = next(new_range)

print('do sth else')
new_range.send('stop!')

 

 

send与next的区别:

  两者都可以唤醒生成器,来继续执行

  send的作用:外界可以发送一个信号给生成器内部,表达式 sign = yield count, 而next是默认发送为None

  

只使用send的情况:

#yield关键字的另外一种使用形式:表达式形式的yield
def eater(name):
    print('%s 准备开始吃饭啦' %name)
    food_list=[]
    while True:
        food=yield food_list
        print('%s 吃了 %s' % (name,food))
        food_list.append(food)

g=eater('egon')
g.send(None) #对于表达式形式的yield,在使用时,第一次必须传None,g.send(None)等同于next(g)
g.send('蒸羊羔')
g.send('蒸鹿茸')
g.send('蒸熊掌')
g.send('烧素鸭')
g.close()
g.send('烧素鹅')
g.send('烧鹿尾')

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!