08>>>函数名称空间与作用域、匿名函数、列表生成式、三元表达式

08.函数名称空间与作用域、匿名函数、列表生成式、三元表达式

 

函数名称空间与作用域

 

名称空间

  名称空间是存放变量与值绑定关系的地方。一共有三层名称空间。

1.内置名称空间

  随着python解释器启动立刻创建,随着python解释器结束立刻销毁。

2.全局名称空间

  随着python文件的开始执行而产生,随着执行完毕而回收。

3.局部名称空间

  在函数体代码运行时产生,在函数体代码结束时销毁。

  这三层名称空间的加载顺序按的也是它们的大小顺序。

  内置名称空间 > 全局名称空间 > 局部名称空间

作用域

  全局作用域包含了内置名称空间和全局名称空间。

  局部作用域则只包含局部名称空间。

思考题1

name = 'joe'def index():
    name = 'simon'
    print(name)
​
print(name)

  程序运行后,打印出来的结果是什么?

  答案是:打印出joe。

  为什么会这样呢?

  首先我们来看一下这道题中程序所对应的名称空间想象图。

  可以看到,name = 'joe'保存在全局名称空间里;name = 'simon'是函数体代码中的变量,所以保存在局部名称空间中。

  然后来看一下程序的逻辑流程。

  程序只是定义了函数但没有执行,最后的print自然只是找函数之外的代码,所以打印是name = 'joe'。

作用域的查找顺序

  首先,一定要先明确你当前在哪。

  如果当前在局部名称空间:

局部名称空间→全局名称空间→内置名称空间(最小至最大)

  如果当前在全局名称空间:

  全局名称空间→内置名称空间(中间至最大)

  如果当前在内置名称空间:

  内置名称空间

  诀窍:顺序只能从左往右(从小到大),不能反向!

思考题2

name = 'joe'def index():
    print(name)
​
index()

  程序运行后,打印出来的结果是什么?

  答案是:打印出joe。

  我们还是先画图。

  全局名称空间里有变量name,局部名称空间里没有。

  程序运行到函数时发觉局部名称空间里没有变量name,于是便主动向上一级名称空间查找。题目中的全局名称空间中有name = 'joe',于是得到结果后函数便打印出全局名称空间中的数据。

  如果全局名称空间中也没有,那么就再往上一级,从内置名称空间中寻找。如果还找不到,才会报错。

特殊情况(扩展了解)

  现在有这样一个程序:

count = 1def index():
    count += 1
​
index()

  依照上面讲述的名称空间作用域的原理,看起来没问题,但是一旦运行就会报错。

  程序这么写,就把python搞混了,它不知道该在哪个空间里索要count。

  函数中的count += 1是我们之前学过的增量赋值,等同于count = count + 1。这句话写在函数中,那么count理应是在局部名称空间中寻找数据。但是现在在局部名称空间中只有count这么一个变量名却没有变量值,于是在逻辑上产生了矛盾。

  报错的意思就是该变量名在还没有定义之前就已经开始使用了,即只有变量名但找不到变量值。

  如果想在局部名称空间直接使用到全局名称空间中的相同变量名所对应的值,可以使用global关键字:

count = 1
def index():
    global count
    count += 1
index()

小练习

1.平行宇宙

name = 'jerry'
def func1():
    name = 'joe'
def func2():
    name = 'simon'
def func3():
    name = 'frank'
func1()
func2()
func3()
print(name)

  最后打印出来的是谁?

  答:jerry

  这道题的名称空间想象图是这样的。

  只要搞清楚这四个name分别在哪里,这道题就没什么难度。

2.多元宇宙

x=1
def outer():
    x=2
    def inner():
        x=3
        print('inner x:%s' %x)
    inner()
    print('outer x:%s' %x)
outer() 

  最后打印的数值是什么?

  答:

inner x:3
outer x:2

  还是先从名称空间想象图入手。

  知道了名称空间和他们各自储存的变量,接下来回到程序流程,来看看程序是怎么运行的。

  这道题的主要难点在于函数的相互嵌套关系,厘清关系之后也就很好懂了。

 

匿名函数

  匿名函数即没有函数名的函数。

    lambda 形参:返回值
# 匿名函数一般不单独使用 需要结合内置函数/自定义函数等一起使用
eg:
res = lambda x:x+1
res1 = res(1)
print(res1)

  上面只是一个给大家看看匿名函数语法结构的案例,实际使用中并不是这么做的,现在举个实际点的栗子来看看:

l = [1, 2, 3, 4, 5, 6, 7]
# 需求,将列表中的每个元素+10
# 方法1
res = map(lambda x: x + 10, l)
print(list(res))
# 方法2
def index(x):
    return  x + 10
res1 = map(index,l)
print(list(res1))

  匿名函数主要用于一些比较简单的业务逻辑中,减少代码量。

 

列表生成式

  现在我们回到列表的学习中。

l1 = [11, 22, 33, 44, 55, 66, 77, 88]

  现在我想让列表中每个元素都+1,使用我们学习过的知识不难做到:

new_list = []  # 设置一个空列表new_list
for i in l1:  # 从列表l1中循环取值
    new_list.append(i+1)  # 让取到的值+1之后加入列表new_list中
print(new_list)  # 打印new_list

  这么做有点繁琐了,我们可以试试用列表生成式来解这道题。

# 列表生成式
new_list = [i + 1 for i in l1]
print(new_list)

  只要需要两步就能解决,确实简洁了许多。

练习1

name_list = ['joe', 'simon', 'frank', 'jerry']
将列表每个元素后面加上_NB的后缀
# 经典方式
new_list = []
for name in name_list:
    new_list.append(name + '_NB')
print(new_list)
# 列表生成式
new_list1 = [name+'_NB' for name in name_list]
print(new_list1)

练习2

l1 = [11, 22, 33, 44, 55, 66, 77, 88, 99]
筛选出加一之后大于60的元素
# 经典方式
new_list = []
for i in l1:
    if i+1 > 60:
        new_list.append(i)
print(new_list)
# 列表生成式
new_list1 = [i for i in l1 if i+1 > 60]
print(new_list1)

 

三元表达式

  现在我们来做个自恋的用户评价系统。

  如果输入的用户名是joe的话,显示“good”;如果是其他人的话,则显示“poor”。

name = input('请输入用户名>>>:')
if name == 'joe':
    print('goodXD')
else:
    print('poorT_T')

  当if判断仅仅是二选一的情况下,我们可以使用三元表达式来节省代码量。

name = input('请输入用户名>>>:')
res = 'goodXD' if name == 'joe' else 'poorT_T'
print(res)

  三元表达式的语法结构如下:

A if 条件 else B

  原理:当if后面的条件为True时使用A,条件为False时使用else后面的B。

补充

  现在我们稍作修改,程序依然保留自恋的部分但不损别人,应该怎么简化代码?

name = input('请输入用户名>>>:')
if name == 'joe': print('goodXD')

  当逻辑判断只有if单独一行没有elif也没有else的情况下,可以将整个判断流程写在一行,但是不推荐,仅作了解。

posted @ 2021-08-30 09:45  veryjoe  阅读(93)  评论(0)    收藏  举报