python----函数进阶

名称空间

又名name space, 顾名思义就是存放名字的地方,存什么名字呢?举例说明,若变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方

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

1.locals: 是函数内的名称空间,包括局部变量和形参

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

3.builtins: 内置模块的名字空间

有名称空间才有作用域

例子:

def outer_function():
    b = 20
    def inner_func():
        c = 30

a = 10

在这个例子中,名称a在全局名称空间中,名称b在函数outer_function的局部名称空间,名称c则在函数inner_func的局部名称空间。
当我们在函数inner_func时,c是个局部的名称,b是个非局部的名称,而a则是个全局的名称。在函数inner_func中,我们可以对c进行读取操作和赋值操作,而只能对b和a进行读取操作。当对b进行赋值时,一个新的名称将会被创建,这个新的名称处于inner_func函数局部名称空间中。对a进行赋值时也会在局部名称空间中创建一个新的名称。

def outer_function():
    a = 20

    def inner_function():
        a = 30
        print('a = %s' % a)

    inner_function()
    print('a = %s' % a)


a = 10
outer_function()
print('a = %s' % a)
输出:
a = 30
a = 20
a = 10

在函数inner_function中,我们对a进行了赋值操作,但函数outer_function中的a仍然为20,全局名称空间中的a则仍然为10

为了在函数作用域中对全局的名称进行读取或者赋值操作,需要将这个名称声明为global

def outer_function():
    global a
    a = 20

    def inner_function():
        global a
        a = 30
        print('a = %s' % a)

    inner_function()
    print('a = %s' % a)


a = 10
outer_function()
print('a = %s' % a)
输出:
a = 30 
a = 30 
a = 30

可以看到,通过global,我们在不同的作用域对全局名称a进行了赋值操作,最后在函数inner_function中对a的赋值也就是全局名称a的值。

作用域查找顺序:

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

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

闭包:

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

闭包函数必须满足两个条件:

1.函数内部定义的函数

2.包含对外部作用域而非全局作用域的引用

例1:以下仅仅在函数内部定义了一个函数,但并非闭包函数.

def outer():
    def inner():
        print("inner func excuted")
    inner()  # 调用执行inner()函数
    print("outer func excuted")
outer()  # 调用执行outer函数

####输出结果为##########
inner func excuted
outer func excuted

例2:以下在函数内部定义了一个函数,而且还引用了一个外部变量x,那么这个是闭包函数么?答案:不是

x = 1
def outer():

    def inner():
        print("x=%s" %x)  # 引用了一个非inner函数内部的变量
        print("inner func excuted")
    inner()  # 执行inner函数
    print("outer func excuted")

outer()
#####输出结果########
x=1
inner func excuted
outer func excuted

例3:显然,下面实例满足闭包函数的条件。现在,你应该清楚,作为一个闭包函数,必须得满足上述的两个条件,缺一不可。

def outer():
    name = 'alex'

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

    return inner


f = outer() 

f()

装饰器:

  装饰器:外部函数传入被装饰函数名,内部函数返回装饰函数名。

  特点:1.不修改被装饰函数的调用方式 2.不修改被装饰函数的源代码

不带参数装饰器:

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


def login(func):

    def inner():
        _username = "alex"  # 假装这是DB里存的用户信息
        _password = "abc123"  # 假装这是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    # henan()


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


def america():
    print("----欧美专区----")


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


@login  # henan=login(henan)
def henan():
    print("----河南专区----")


henan()
japan()

带参数的装饰器:

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


def login(auth_type):
    def outer(func):

        def inner(*args, **kwargs):
            _username = "alex"  # 假装这是DB里存的用户信息
            _password = "abc123"  # 假装这是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    # henan()
    return outer

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


def america():
    print("----欧美专区----")


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


@login('wx')  # henan=login(henan)
def henan(style):
    print("----河南专区----", style)

# xx = login('qq')
# print(xx)
# henan = xx(henan)
# print(henan)
henan('oo')

列表生成式:

例子:

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

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

这样的写法就叫做列表生成式.

生成器:

在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
如果要一个一个打印出来,可以通过next()函数获得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(1))
>>>next(g)
>>>1
>>> next(g)
<generator object <genexpr> at 0x1022ef630

创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator.

所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print('before')
        yield b  # 把函数的执行过程冻结着这一步并且把b的值返回给外面
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'Done'


f = fib(15)  # turn function into generators

for i in f:
    print(i)

这里,最难理解的就是generator和函数的执行流程不一样。
函数是顺序执行,遇到return语句或者最后一行函数语句就返回。
而变成generator的函数,在每次调用next()的时候执行,
遇到yield语句返回,再次被next()调用时从上次返回的yield语句处继续执行

yield VS return:

  • return返回并中止function
  • yield返回数据,并冻结当前的执行过程
  • next唤醒yield并继续执行,直到下一次遇到yield

 

posted on 2019-04-06 17:48  cn_ike  阅读(284)  评论(0编辑  收藏  举报

导航