第二模块:03python学习之函数进阶

1.名称空间

定义:相比上一节的作用域,名称空间更能解释。名称空间又名name space, 顾名思义就是存放名字的地方,存什么名字呢?举例说明,若变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方。

名称空间分以下三种:

  • locals:是函数内的名称空间,包括局部变量和形参,当前函数空间内
  • globals:全局变量,函数定义所在模块的空间
  • bulildings:内置模块的名称空间

针对作用域的查找顺序,遵循LEGB的规则

  • L:LOCALS 本地的
  • E:ENCLOSING 相邻的
  • G:GLOBLS 全局的
  • B:BUILTINGS 内置函数的

函数在调用变量时,遵循从上到下,逐层向下获取变量,LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__

看以下代码

level = ‘L0‘
n = 22

def func():
    level = ‘L1‘
    n = 33
    print(locals())

    def outer():
        n = 44
        level = ‘L2‘
        print(locals(),n)

        def inner():
            level = ‘L3‘
            print(locals(),n) #此外打印的n是多少?
        inner()
    outer()

func()

2.闭包

关于闭包可以这么定义:即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数)。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。也就是说,内部函数会在外部函数返回后被执行。而当这个内部函数执行时,它仍然必需访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明(最初时)的值是外部函数返回时的值,但也会受到内部函数的影响。

是不是觉得很麻烦,绕来绕去,先看代码

def outer():
    name = ‘alex‘

    def inner():
        print("在inner里打印外层函数的变量",name)

    return inner

f = outer() 

f()

你会发现在与普通的嵌套函数不同的是子函数返回的是函数自己,而不是其他变量什么的。这个时候可以这么定义,在外部可以调用内部函数,同时使用内部的值这个就可以称之为闭包。虽然函数内部的子函数返回子函数自身,在返回的同时,不止是一个函数对象,同时在外面包裹了一层作用域,使得函数在调用的时候优先使用外部的作用域。

3.装饰器***

对于程序开发来说,我们一般需要遵守‘开放封闭原则’,所谓开放就是说程序有较好的拓展性以及延展性,因为对于一个程序的功能不可能永远不变,会各种各样的拓展功能,例如说我们登录一个程序,以前只有账户密码验证,后面铸件出现了QQ验证,微信认证,微博验证,这就要求我们的程序可以较好的拓展。而所谓的封闭就是说已经完成的程序代码最好不动,有可能的是这个操作会发生巨大的工作量,涉及到不同的部门或者不同的流程这样就会有巨大的变更动作,也有可能是代码时间已经很久远,不好重新构建。下面用几个例子来说明这个事情。

你所在的部门属于基础支撑部门,产生核心代码,其他部门代码从你这边调取

你的核心代码如下

############### 基础平台提供的功能如下 ###############

def f1():
    print ‘f1‘

def f2():
    print ‘f2‘

def f3():
    print ‘f3‘

def f4():
    print ‘f4‘

############### 业务部门A 调用基础平台提供的功能 ###############

f1()
f2()
f3()
f4()

############### 业务部门B 调用基础平台提供的功能 ###############

f1()
f2()
f3()
f4()

这个时候老板要求,核心代码随意调用有可能泄密,要求调用的时候需要账户密码验证。对于初学者我们有可能会想到这样,对每一个被调用的函数进行重编

代码如下

############### 基础平台提供的功能如下 ############### 

def f1():
    # 验证1
    # 验证2
    # 验证3
    print ‘f1‘

def f2():
    # 验证1
    # 验证2
    # 验证3
    print ‘f2‘

def f3():
    # 验证1
    # 验证2
    # 验证3
    print ‘f3‘

def f4():
    # 验证1
    # 验证2
    # 验证3
    print ‘f4‘

############### 业务部门不变 ###############
### 业务部门A 调用基础平台提供的功能### 

f1()
f2()
f3()
f4()

### 业务部门B 调用基础平台提供的功能 ### 

f1()
f2()
f3()
f4()

但是这种情况,我们就发现重复会很多很多,这只是三四个模块,如果多呢?这样就太多重复代码了,有可能说我这样功能实现了呀,但是作为一个程序员应该是想着用最简洁的语句完成最多的事情,不过我们有可能在学了一点,想到以下的这种方式。

############### 基础平台提供的功能如下 ############### 

def check_login():
    # 验证1
    # 验证2
    # 验证3
    pass

def f1():

    check_login()

    print ‘f1‘

def f2():

    check_login()

    print ‘f2‘

def f3():

    check_login()

    print ‘f3‘

def f4():

    check_login()

    print ‘f4‘

专门做一个认证函数,每个基础模块做修改调用一次认证函数,认证通过就可以继续下去,但是我们这样还是违背了开放封闭原则,对源代码做了修改,最好的办法就是认证一次后不需要在此认证。比较高级的代码就是下面这个,后面会对这个代码说明。

def w1(func):
    def inner():
        # 验证1
        # 验证2
        # 验证3
        return func()
    return inner

@w1
def f1():
    print ‘f1‘
@w1
def f2():
    print ‘f2‘
@w1
def f3():
    print ‘f3‘
@w1
def f4():
    print ‘f4‘

解释上面的代码,对于python程序是由上至下执行,首先返回俩个值

return fi()直接执行f1函数

return inner返回inner函数,并没有执行

没错,从表面上看解释器仅仅会解释这两句代码,因为函数在没有被调用之前其内部代码不会被执行。

从表面上看解释器着实会执行这两句,但是 @w1 这一句代码里却有大文章,@函数名 是python的一种语法糖。

如上例@w1内部会执行一下操作:

    • 执行w1函数,并将 @w1 下面的 函数 作为w1函数的参数,即:@w1 等价于 w1(f1)
      所以,内部就会去执行:
          def inner:
              #验证
              return f1()   # func是参数,此时 func 等于 f1
          return inner     # 返回的 inner,inner代表的是函数,非执行函数
      其实就是将原来的 f1 函数塞进另外一个函数中
    • 将执行完的 w1 函数返回值赋值给@w1下面的函数的函数名
      w1函数的返回值是:
         def inner:
              #验证
              return 原来f1()  # 此处的 f1 表示原来的f1函数
      然后,将此返回值再重新赋值给 f1,即:
      新f1 = def inner:
                  #验证
                  return 原来f1() 
      所以,以后业务部门想要执行 f1 函数时,就会执行 新f1 函数,在 新f1 函数内部先执行验证,再执行原来的f1函数,然后将 原来f1 函数的返回值 返回给了业务调用者。
      如此一来, 即执行了验证的功能,又执行了原来f1函数的内容,并将原f1函数返回值 返回给业务调用着

所以我们可以这么理解,装饰器只是作为一个语法糖包含在函数外面,将函数作为参数传递给认证函数执行,对于调用者而言其实是执行,调用的f1(),其实是调用的inner函数,然后做认证,执行真正f1()函数

对于装饰器还有其他

带参数

#单参数
def w1(func):
    def inner(arg):
        # 验证1
        # 验证2
        # 验证3
        return func(arg)
    return inner

@w1
def f1(arg):
    print ‘f1‘

一个参数
#俩个参数
def w1(func):
    def inner(arg1,arg2):
        # 验证1
        # 验证2
        # 验证3
        return func(arg1,arg2)
    return inner

@w1
def f1(arg1,arg2):
    print ‘f1‘

两个参数

#多个参数
def w1(func):
    def inner(*args,**kwargs):
        # 验证1
        # 验证2
        # 验证3
        return func(*args,**kwargs)
    return inner

@w1
def f1(arg1,arg2,arg3):
    print ‘f1‘

双装饰器

def w1(func):
    def inner(*args,**kwargs):
        # 验证1
        # 验证2
        # 验证3
        return func(*args,**kwargs)
    return inner

def w2(func):
    def inner(*args,**kwargs):
        # 验证1
        # 验证2
        # 验证3
        return func(*args,**kwargs)
    return inner

@w1
@w2
def f1(arg1,arg2,arg3):
    print ‘f1‘

其他类型的需要后面再添加

4.列表生成器

现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],要求你把列表里的每个值加1,你怎么实现?你可能会想到2种方式

第一种

a = [1,3,4,6,7,7,8,9,11]
b = []
for i in a:b.append(i+1)
    a = b

第二种

b = []
>>> for i in a:b.append(i+1)
>>> a = b

稍微装逼一点的

a =[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a = map(lambda x:x+1, a)
for i in a:print(i)

最装逼的就是下面这种了

a = [i+1 for i in range(10)]

直接一行代码搞定,减少代码量,这种就叫做列表生成式

5.生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法。

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

第二种方法,yield  把函数变成生成器

5.1看以下代码

list1 = [i for i in range(100)]
list1 =[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27
, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

我们很容易就用列表生成式生成一个100元素的列表,那么如果要生成一个一个出现的额生成式,只需要将[]变为()就可以了,看以下代码

>>> li = (i for i in range(100))
>>> li
<generator object <genexpr> at 0x0220C6E8>

对于一个生成式获取他下一个元素就有俩种方式next(li) 和 li.__next__(),就可以轻松获取到下一个元素

不过需要注意的是,在调用next()的函数的时候,在最后一个位置会抛出一个错误,必须记住生成器只能往前,不能后退,也不可以切片,走到最后直接报错

>>> next(li)
8
>>> next(li)
9
>>> next(li)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

#每次调用next(g)就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

range对于python2与3的区别

在python3中,range和文件的打开也是生成器

5.2用yield语法生成一个生成器

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个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‘

>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>

yield与return的区别

return返回并中止function

yield返回数据,并冻结当前进程

next 唤醒冻结的函数执行过程,继续执行直到下一个yield

return在生成器中,代表生成器的中止,会直接报错

5.3生成器send

例子:执行到yield时,gen函数作用暂时保存,返回i的值;temp接收下次c.send("python"),send发送过来的值,c.next()等价c.send(None)

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

f = fib(10)
print(next(f))
f.send(‘寄个快递‘)
print(next(f))

f.send(None)    #第一次相当于f.__next__()

总结:

生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。

生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。

生成器的特点:

  1. 节约内存
  2. 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的

6.迭代器

我们已经知道,可以直接作用于for循环的数据类型有以下几种:

  • 一类是集合数据类型,如listtupledictsetstr等;
  • 一类是generator,包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

可以使用isinstance()判断一个对象是否是Iterable对象:

>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance(‘abc‘, Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

可以使用isinstance()判断一个对象是否是Iterator对象:

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance(‘abc‘, Iterator)
False

生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

listdictstrIterable变成Iterator可以使用iter()函数:

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter(‘abc‘), Iterator)
True

总结

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

Python3的for循环本质上就是通过不断调用next()函数实现的,例如:

for x in [1, 2, 3, 4, 5]:
    pass

实际上完全等价于:

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

原文地址:https://www.cnblogs.com/gbq-dog/p/8932010.html

时间: 2024-10-14 00:53:38

第二模块:03python学习之函数进阶的相关文章

python学习总结(函数进阶)

-------------------程序运行原理------------------- 1.模块的内建__name__属性,主模块其值为__main__,导入模块其值为模块名 1.创建时间,py文件比pyc文件新,则从新生成pyc. 2.magic num,做运行前版本测试,版本不同重新生成pyc. 3.PyCodeObject对象,源代码中的字符串,常量值,字节码指令,原始代码行号的对应关系. 2.LEGB规则 1.Local :本地 当前所在命名空间(如函数,模块),函数的参数也属于命名空

python学习之函数进阶三

一.模块介绍 os模块常用方法 os.getcwd()    #获取当前工作目录 os.listdir("F:\\")    #获取指定目录下的所有文件和目录 os.remove('b.json')    #删除指定文件 os.stat('a.json')    #获取文件属性 os.chmod()        #修改文件属性 os.mkdir('hyh')    #创建目录 os.rmdir('hyh')    #删除目录 os.system('dir')    #运行shell命

Python学习之函数进阶

函数的命名空间 著名的python之禅 Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't

python学习之---函数进阶

一,递归函数: 做程序应该都知道,在一个函数的内部还可以调用其它函数,这叫函数的调用,但是有一种特殊的情况,在一个函数内部对自身函数的调用,我们成这为函数的递归调用. 在此,使用一个家喻户晓的例子来演示一下函数的递归调用------求阶乘: 1 >>> func(1) 2 1 3 >>> func(10) 4 3628800 5 >>> func(100) 6 9332621544394415268169923885626670049071596826

pyrhon学习_day11___函数进阶

1.函数嵌套的调用 #定义函数 def max2(x,y): m = x if x>y else y #三元运算:结果 = if条件成立的结果 if 条件 else if条件不成立的结果 return m #函数嵌套 def max4(a,b,c,d): res1 = max2(a,b) res2 = max2(res1,c) res3 = max2(res2,d) return res3 # print(max4(23,-7,31,11)) 2.函数嵌套的定义 def func(): prin

Python学习:函数进阶

本节要点:命名空间及作用域,函数嵌套,函数名本质,闭包 命名空间: 命名空间的本质:存放名字与值的绑定关系. 三种命名空间: 全局命名空间 局部命名空间 内置命名空间 三种命名空间之间的加载与取值顺序: 加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载) 取值: 在局部调用:局部命名空间->全局命名空间->内置命名空间 在全局调用:全局命名空间->内置命名空间 作用域: 作用域就是作用范围,按照生效

Python第二模块(文件和函数)

1.集合操作 集合的特点:无序,不重复的数据组合 集合的作用: 去重,将列表变为集合,就会自动去重 关系测试,测试两组数据之间的交集.差集.并集关系 常用操作:

PYTHON学习第二模块 python内置模块介绍

1 >>> import time 2 >>> time.time() 3 1491064723.808669 4 >>> # time.time()返回当前时间的时间戳timestamp(定义为从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数)的方法,无参数 5 >>> time.asctime() 6 'Sun Apr 2 00:39:32 2017' 7 >>> # time.asctim

《lua程序设计 第二版》 学习笔记6 -------- 深入函数

-- 第6章 深入函数 -- 函数是一种"第一类值",他们具有特定的"词法域" -- "第一类值":与其他传统类型的值具有相同的权利.例如:存储到变量/table中,作为实参传递给函数或者作为其他函数的返回值 -- "词法域":一个函数可以嵌套在另一个函数中,内部函数可以反问外部函数的变量. local function testFun(x) return 2 * x end -- 函数定义方式1 local testFun