==============================函数:命名空间和作用域=============================
一、命名空间
1、命名空间的定义:
名称到对象的映射。命名空间是一个字典的实现,键为变量名,值是变量对应的值。
各个命名空间是相互独立的没有关系的,同一个命名空间不能有重名(会相互影响),不同的命名空间可以重名,不会相互影响。
2、命名空间的分类:
python程序执行期间会有2个或3个活动的命名空间(函数调用时有3个,函数调用结束后有2个)。按照变量定义的位置,可以划分为以下3类。
(1)局部命名空间:每个函数所拥有的命名空间,记录了函数中定义的所有变量,包括函数的入参,内部定义的局部变量。
(2)全局命名空间:每个模块加载执行时创建的,记录了模块中定义的变量,包括模块中定义的函数、类、其他导入的模块、模块级的常量和变量。
(3)内置命名空间:python自带的内置空间,任何模块都可以访问,放着内置的函数和异常。
3、命名空间的持续时间:生命周期
(1)局部命名空间:在函数调用时才被创建,但函数返回结果或抛出异常时被删除。(每一个递归函数都拥有自己的命名空间)
(注:在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数为递归函数)
(2)全局命名空间:在模块被加载时创建,通常一直保留到python解释器退出。
(3)内置命名空间:在python解释器启动时创建,一直保留到解释器退出。
各个命名空间创建顺序:
python解释器启动---->创建内置命名空间----->加载模块----->创建全局命名空间------->函数被调用------>创建局部命名空间
各个命名空间销毁顺序:
函数调用结束----->销毁函数对应的局部命名空间------->python虚拟机(解释器)退出------>销毁全局命名空间------>销毁内置命名空间
4、局部命名空间的使用:
(1)在局部中:可以使用全局、内置命名空间的名字
# 例子:局部命名空间使用全局命名空间的名字
a = 1
def func():
print(a)
func()
# 局部中使用内置命名空间的名字
def func():
return input('请输入内容')
ret= func()
print(ret)
# 在全局中,使用内置命名空间的名字
print('江河')
# 在全局中,使用局部命名空间的名字。报错,提示未定义
def func():
a = 1
func()
print(a) # NameError: name 'a' is not defined
(2)在全局中:可以使用内置命名空间的名字,但不能使用局部命名空间的名字
二、作用域:
1、定义:作用域是针对变量而言,指声明的变量在程序中可应用的范围。
2、分类:
(1)只有函数、类、模块会产生作用域,代码块不会产生作用域。
(2)作用域按照变量的定义位置可以划分为4类:
local--局部作用域(函数内)
global--全局作用域(模块全局)
enclosing--嵌套作用域(闭包)(嵌套函数的外层函数内部)
builtin--内置作用域(内置模块python)
3、作用域按照生效范围分为:全局作用域和局部作用域
全局作用域:包含 内置命名空间、全局命名空间,在整个文件的任意位置都能被引用、全局有效
局部作用域:局部命名空间,只能在局部范围内生效
(注:全局作用域----作用域在全局---内置和全局命名空间都属于全局作用域----globals()
局部作用域----作用在局部----函数(局部命名空间中属于局部作用域)---locals())
(1)例子:global是声明a:
如果在一个局部(函数)内声明了 一个global变量,那么这个变量在局部的所有操作将对全局的变量有效(少用或不用)
a = 1
def func():
global a
a = 2
func()
print(a) # 2
4、对于不可变数据类型,在局部可以查看全局作用域中的变量,但是不能直接修改,如果想要修改,需要在程序的一开始添加global声明,如果在一个局部函数内声明了一个global变量,那么这个变量在局部所有的操作将对全局变量有效。
5、内置函数:locals()和globals()
(1)globals()函数会以字典形式返回当前位置的全局变量(永远打印全局变量的名字,以字典形式)
例子:globals()返回一个全局变量的字典,包括所有导入的变量
a = 'run'
print(globals())
# 打印{'__name__': '__main__', '__doc__': '\nfor i in range(1,5):\n for j in range(1,5):\n for k in range(1,5):\n if (i != k) and (i != j) and (j != k):\n print(i,j,k)\n\n\n# 方法二\nfor i in range(1,5):\n for j in range(1,5):\n if (j == i):\n continue\n for k in range(1,5):\n if (k == i or k == j):\n continue\n print(i,j,k)\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000000616828>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/wj/test_project1/python程序练习.py', '__cached__': None, 'a': 'run'}
(2)locals() 函数以字典形式返回当前位置的全部的局部变量
=============================函数的嵌套和作用域链==========================================
1、函数的嵌套:在一个函数中 定义了另一个函数
(1)函数嵌套调用的例子
def max2(x,y):
return x if x > y else y
def max3(a,b,c):
d = max2(a,b)
return max2(d,c)
print(max3(2,3,4)) # 4
2、内部函数可以使用外部函数的变量
nonlocal可以修改,声明了nonlocal的内部函数变量修改会影响到距离当前上层函数最近一层的局部变量,
对全局变量无效,对局部也只是最近一层的影响。
例子:
a = 1
def fun():
a = 1
def func():
a = 1
def func1():
nonlocal a
a += 1
func1()
print(a)
func()
print("**a**:",a)
fun()
print('全局:',a)
# 如果都找不到,提示“SyntaxError: no binding for nonlocal 'a' found”
3、nonlocal关键字
(1)外部必须有这个变量
(2)在内部函数声明nonlocal变量之前不能再出现同名变量
(3)内部修改这个变量如果在外部有这个变量的第一层函数中生效
函数的作用域链
例子:
def f1():
a = 1
def f2():
def f3():
print(a)
f3()
f2()
f1() # 1
4、函数名的本质:就是函数的内存地址
(1)函数名可以被引用(即赋值)
例子:
def func():
print('函数名引用')
f = func
f() # 函数名引用
(2)可以被当做容器类的元素(容器类是指:list、tuple、dict等)
例子:
def f1():
print('f1')
def f2():
print('f2')
def f3():
print('f3')
l = [f1,f2,f3]
l[0]() # f1
dic = {'f1':f1,'f2':f2,'f3':f3}
dic['f1']() # f1
(3)函数名可以作为函数的返回值和参数
def func(f):
f()
return f # 函数名可以作为函数的返回值
fun = func(func) # 函数名可以作为函数的参数
fun()
# 注意:在python中,所有元素都是对象,其中函数是第一类对象。第一类对象的通用特性:函数名可以
# 被引用(即赋值)、可以被当做容器类的元素,函数名可以作为函数的返回值和参数
============================闭包================================
1、闭包的定义:如果一个内部函数里,包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数。
2、闭包的作用:保存函数的状态信息,使函数的局部变量信息依然可以保存下来
3、闭包实例:
def func():
a = 1
def func1():
print(a)
func1()
func()
4、闭包常用用法的原因:
由于有了作用域的关系,我们不能拿到函数内部的变量和函数了,如果我们就是想拿怎么办?返回呀
我们都知道函数内的变量我们要想在函数外部用,可以直接返回这个变量,那么如果我们想在函数外部调用函数内部的函数呢?
是不是直接就把这个函数的名字返回就好了?这才是闭包函数最常用的用法。
例子:
def func():
a = 1
def func1():
print(a)
print(func1.__closure__) # 判断闭包的方法
return func1 # 返回函数名即函数的内存地址
fun = func()
fun()
5、判断闭包的方法:函数名.__closure__打印
例子:
def func():
a = 1
def func1():
print(a)
print(func1.__closure__) # (<cell at 0x000000000216D978: int object at 0x000007FEED50B350>,)
return func1
fun = func()
fun()
# 输出的__closure__有cell元素,是闭包函数
# 例子:闭包函数的嵌套
def func():
a = 1
def func1():
b = 2
def func2():
print(a,b)
return func2
return func1
fun = func()
i = fun()
i()
# 闭包获取网络应用
import urllib
from urllib.request import urlopen
def index():
url = ''
def get():
return urlopen(url).read()
return get
urll = index()
print(urll())
======================================复习======================
1、三元运算符:
接收结果的变量 = 条件为真的结果 if 条件 else 条件为假的结果
接收结果的变量 = ‘真结果’ if 条件 else ‘假结果’
2、命名空间和作用域
三种:内置、全局、局部
作用域:全局 局部(globals()、locals())
globals 在局部声明一个全局变量
nonlocal 在局部声明最近的上一层局部中的变量
作用域链:小范围用变量的时候,先从自己的名字空间找,
找不到就一层一层向外层找,直到找到未止。找不到就报错
3、函数的嵌套调用和嵌套定义:
定义在函数内部的函数不能被外界直接调用
内部函数可以使用外部函数的变量
4、函数名的本质:
就是一串内存地址
可以赋值、可以作为容器类型的元素、函数的参数和返回值 ---- 第一类对象
5、闭包:内部函数使用外部函数的变量(非全局变量)
================================作业讲解===============================
1、
# 2、写函数,接收n个数字,求这些参数数字的和。
# 全局变量报错,如果内部函数有引用外部函数的同名变量或者全局变量,并且对这个变量有修改.那么python会
# 认为它是一个局部变量,又因为函数中没有sum的定义和赋值,所以报错。
def func(*args):
sum = 0
for i in args:
sum += i
return sum
print(func(1,2,3,4,5))
# 3、读代码,回答:代码中,打印出来的值a,b,c分别是什么?为什么?
#2、写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。
def func(l):
return l[1::2]
print(func([1,23,4,4])) # [23, 4]
# 3、写函数,判断用户传入的值(字符串、列表、元组)长度是否大于5。
def func(l):
return len(l) > 5
if func('adfs'):
print('长度大于5')
else:
print('长度小于5')
# 4、写函数,检查传入列表的长度,如果大于2,
# 那么仅保留前两个长度的内容,并将新内容返回给调用者。
def func(l):
return l[0:2]
print(func([1,23,3]))
# 5、写函数,计算传入字符串中【数字】、【字母】、【空格】 以及 【其他】的个数,并返回结果。
def func(s):
dic = {'num':0,'alpha':0,'space':0,'other':0}
for i in s:
if i.isdigit():
dic['num'] += 1
elif i.isalpha():
dic['alpha'] += 1
elif i.isspace():
dic['space'] += 1
else:
dic['other'] += 1
return dic
print(func('+0-0skahe817jashf wet1'))
# 6、写函数,检查用户传入的对象(字符串、列表、元组)
# 的每一个元素是否含有空内容,并返回结果。
def func(information):
if type(information) is str and information:
for i in information:
if i == ' ':
return True
elif type(information) is list or type(information) is tuple:
for i in information:
if not i:
return True
elif not information:
return True
print(func('121 '))
#7、写函数,检查传入字典的每一个value的长度,如果大于2,
# 那么仅保留前两个长度的内容,并将新内容返回给调用者。
def func(dic):
for k in dic:
if len(dic[k]) > 2:
dic[k] = dic[k][:2]
return dic
dic = {'k1':'sdkfj','k2':'dkfa','k3':'sfasdf'}
print(func(dic))
# 8、写函数,接收两个数字参数,返回比较大的那个数字。
def func(a,b):
if a > b:
return a
else:
return b
print(func(1,-2))
def func(a,b):
return a if a>b else b # 三元运算
print(func(3,1))
# 变量 = 条件返回True的结果 if 条件 else 条件返回False的结果(仅用于简单情况)
# 9、写函数,用户传入修改的文件名,与要修改的内容,
# 执行函数,完成整个文件的批量修改操作(进阶)。
def func(filename,old,new):
with open(filename,encoding='utf-8') as f,open('%s.bak'%filename,'w',encoding='utf-8') as f2:
for line in f:
if old in line:
line = line.replace(old,new)
# 写文件
f2.write(line)
import os
os.remove(filename) # 删除文件
os.rename('%s.bak'%filename,filename) # 重命名文件
func('cs.txt','新内容1','新内容1')