第三章 Python基础——文件操作&函数 续

3.6函数进阶

名称空间:name space

例:若变量X=1,1存放于内存中,那存放X与1绑定关系的地方就叫做名称空间。

名称空间共三种,分别如下:

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

不同变量的作用域不同就是由于这个变量所在的命名空间决定的。

作用域即范围:

  • 全局范围:全局存活,全局有效
  • 局部范围:临时存活,局部有效

 注:查看作用域方法 globals(),locals()

作用域查找顺序:

level='L0'
n=22

def func():
      leval='L1'
      n=33
      print(locals())

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

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

func()

问题:在inner()里的打印n的值是多少?

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

  • locals是函数内的名字空间,包括局部变量和形参
  • enclosing外部嵌套函数的名字区间
  • globals全局变量,函数定义所在模块的名字空间
  • builtins内置模块的名字空间

闭包

关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数)。

详解:而且,这些内部函数可以访问他们所在外部函数中声明的所有局部变量、参数。当其中一个这样的内部函数在包含他们的外部函数被调用时,就会形成闭包。也就是说,内部函数会在外部函数返回后被执行。而当这个内部函数执行时,它仍然必须访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明(最初时)的值是外部函数返回时的值,但也会会受到内部函数的影响。

def outer():
      name='alex'

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

        return inner

f=outer()
f()

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

装饰器

例1:

#_*_coding:utf-8_*_

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

def login():
    _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!")
    else:
        print("用户已登录,验证通过...")

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

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

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

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

home()
america()
henan()

注:在软件开发中有一个原则“开放-封闭”原则,简单来说,它规定已经实现的代码不允许被修改,但可以被扩展,即:

封闭:已实现的功能代码不应该被修改

开放:对现有功能的扩展开放

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=login(america)#你在这里相当于把america这个函数替换了

最终简化为:

#_*_coding:utf-8_*_

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

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

    def inner(*args,**kwargs):#再定义一层函数
        _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(*args,**kwargs) # 看这里看这里,只要验证通过了,就调用相应功能

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


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

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

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

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

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

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

henan("3p")

带参数装饰器:

#_*_coding:utf-8_*_
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")

3.6生成器,迭代器

列表生成式:

例:把列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],要求把列表里的每个值都加1

>>> a = [i+1 for i in range(10)]
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10列表生成式]

这种写法就叫做生成式

生成式

在python中一边循环一边计算的机制称为生成器:generator

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

>>> 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>

Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,想要打印书generator的每一个元素有哦两种方法:

第一种用next() 

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

第二种for循环,generator也是可迭送的对象

>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
...
0
1
4
9
16
25
36
49
64
81

迭送器

可以直接作用于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

Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。