一、global和nonlocal方法

global方法

global方法的作用是在局部名称空间内修改全局名称空间中的变量名绑定的值。

money = 666

def index():
global money
money = 123 index()
print(money)
"""
局部名称空间直接修改全局名称空间中的数据
"""

nonlocal方法

nonlocal方法的作用是在出现嵌套函数的时候,在内层函数的局部名称空间中修改外层函数的局部名称空间中的变量名绑定的值

def index():
name = 'jason'
def inner():
nonlocal name
name = 'kevin'
inner()
print(name) index()
'''
内层局部名称空间修改外层局部名称空间中的数据
'''

二、函数名的多种用法

函数名称其实就是绑定了一个内存地址的变量名,只不过它的内部放的不是数据值而是一堆代码,我们可以通过不同的形式调用他们,最后只需要在末尾加上一个括号。下面我们介绍函数的四种用法:

1、函数名可以当作变量名赋值

我们可以把函数名跟别的变量名绑定关系,这样通过调用这个变量名就等于调用了函数,调用的时候在变量名后面需要加括号。

def index():pass
res = index
res()

2、函数名可以当作函数的参数

当我们在使用函数的时候,可以把别的函数的函数名当成参数传到函数中去,如果那个函数有定义相关的内容就可以执行对应的功能。

def index():
print('from index')
def func(a):
print(a)
a()
func(index)

3、函数名可以当作函数的返回值

我们还可以把函数当作其他函数或函数本身的返回值,这样当我们调用函数后就会发现变量名绑定的结果时返回的那个函数的名称。

def index():
print('from index') def func():
print('from func')
return index
res = func()
print(res) # 会返回一串文字,表示这是func函数
res() def index():
print('from index')
def func():
print('from func')
return func res = index()
print(res) # 会返回一串文字,表示这是index函数
res()

4、函数名还可以当作存储数据的容器中的数据

这里我们举例,函数的名称被放到字典中形成了一种存储数据的容器,这时候我们就不需要一直使用if方法创建很多分支来达到相应的功能了。直接根据对应的功能选择对应的字典的key,然后根据key调用对应的函数。

	def register():
print('注册功能') def login():
print('登录功能') def withdraw():
print('提现功能') def transfer():
print('转账功能') def shopping():
print('购物功能') # 定义功能编号与功能的对应关系
func_dict = {
'1': register,
'2': login,
'3': withdraw,
'4': transfer,
'5': shopping
} while True:
print("""
1.注册功能
2.登录功能
3.提现功能
4.转账功能
5.购物功能
""")
choice = input('>>>:').strip()
if choice in func_dict:
func_name = func_dict.get(choice)
func_name()
else:
print('功能编号不存在')
# if choice == '1':
# register()
# elif choice == '2':
# login()
# elif choice == '3':
# withdraw()
# elif choice == '4':
# transfer()
# elif choice == '5':
# shopping()
# else:
# print('去你妹的 终于写完了')

三、闭包函数

1、基础知识

所谓闭包函数就是定义在函数内部的函数,但是他有一些限制条件:

1、定义在函数内部

2、用到了外部函数名称空间中的名称

def index():
name = 'jason'
def inner():
print(name)

2、作用

闭包函数在使用的过程中的作用其实很简单——是另外一种给函数体代码传参的方式!!!

通过之前的学习我们了解到,当函数需要参数的时候我们会在括号内加入他们需要的参数,这是给函数传参的第一种方式。第二种方式就是使用闭包函数给其他函数体代码传参。代码举例:

# 给函数体代码传参的方式1:代码里面缺什么变量名形参里面就补什么变量名
def register(name,age):
print(f"""
姓名:{name}
年龄:{age}
""")
register('jason', 18)
# 给函数体代码传参的方式2:闭包函数
def outer(name, age):
def register():
print(f"""
姓名:{name}
年龄:{age}
""") return register res = outer('jason', 18)
res()
res()
res = outer('kevin', 28)
res()
res()
res()

第一种情况下的参数传参简单明了就不做具体说明了,第二种方式是使用了闭包函数的方式来传参的,由于工作流程有些绕,我们分条具体解释:

1.当我们运行代码的时候会先定义函数,但是不会运行其中的代码。

2.接着就到了res代码堆这里,我们需要先看代码右边的内容进行了什么操作

3.第一个res代码运行了outer函数并且给他传入了'jason', 18两个参数,之后我们回到代码内部根据流程运行代码,最后发现返回值是register函数名

4.这里我们可以发现第一个res变量名绑定了register函数名

5.后面两行代码都是在调用res绑定的当前参数下的register函数,返回的信息是'jason', 18

6.到了第二次给res变量名赋值的地方,也是一样的判断方式,可以发现现在res绑定成了在'kevin', 28两个参数下的register函数

7.之后又是连续几次调用res绑定的在'kevin', 28两个参数下的register函数,返回的信息也是'kevin', 28

四、装饰器简介

为何要用装饰器

软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。

软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃,而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。

什么是装饰器

’装饰’代指为被装饰对象添加新的功能,’器’代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

提示:可调用对象有函数,方法或者类,此处我们单以本章主题函数为例,来介绍函数装饰器,并且被装饰的对象也是函数。

本质

虽然装饰器功能强大并且理解比较困难,但是他并不是一个新的知识点,而是由函数参数、名称空间、函数名多种用法、闭包函数组合到一起的结果。

口诀

对修改封闭 对扩展开放

储备知识

由于接下来的装饰器推到流程中,需要用到时间模块来计时,这就需要介绍一下时间模块的一些功能:

import time
# 表示引入时间模块
print(time.time()) # 返回时间戳(距离1970-01-01 00:00:00所经历的秒数)
time.sleep(3) # 这是让程序在运行到这里的时候暂停三秒的作用
print('睡醒了 干饭') count = 0
# 循环之前先获取时间戳
start_time = time.time()
while count < 100:
print('哈哈哈')
count += 1
end_time = time.time()
# 循环解释后获取时间戳
print('循环消耗的时间:', end_time - start_time)

五、装饰期推导流程

这里我们定义两个函数,并想办法使用装饰器来达到统计调用函数运行时间的目的,修改代码期间需要时刻牢记装饰器的作用:在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能

步骤一:

先写出直接达成功能的代码

import time
# 引入时间模块
def index():
time.sleep(3)
print('from index')
def home():
time.sleep(1)
print('from home')
# 定义两个会暂停一段时间的函数,方便等下统计运行时间
'''1.当我们刚解除这个要求的时候,会直接采取最直接的方法看看完成功能的代码的基本情况'''
start_time = time.time()
index()
end_time = time.time()
print('函数index的执行时间为>>>:', end_time-start_time)

步骤二:

使用函数简化代码

'''2.我们发现针对不同的函数需要执行类似的一段代码,这个时候我们应该想到函数这个功能来简化代码'''
def get_time():
start_time = time.time()
index()
end_time = time.time()
print('函数index的执行时间为>>>:', end_time - start_time)
get_time()

步骤三:

尝试用参数传参

'''3.由于我们这个函数是要适用于不同的函数统计运行的时间的,所以需要给函数定义参数,并把内部代码中的具体函数名换成参数名加括号'''
def get_time(xxx):
start_time = time.time()
xxx()
end_time = time.time()
print('函数的执行时间为>>>:', end_time - start_time)
get_time(index)
get_time(home)

步骤四:

尝试用闭包函数传参

'''4.由于加括号的形式改变了调用阶段的调用方式,需要新的参数,所以我们想到了第二种给函数传参的方式——闭包函数'''
# 闭包函数的特点:嵌套函数和内部函数调用外部函数的名称
def outer(xxx):
# xxx = index
def get_time():
start_time = time.time()
xxx()
end_time = time.time()
print('函数的执行时间为>>>:', end_time - start_time)
return get_time
res = outer(index)
res()
res1 = outer(home)
res1()

步骤五:

通过改变调用变量名的名称为被计时函数的名称来达成不改变调用方式的目的

'''5.改动到这里后我们发现我们的调用方式还是不对,因此我们想到不如用被检测的那个函数的名称来当变量名,这样偷天换日之后就等于没有改变调用方式'''
def outer(xxx):
def get_time():
start_time = time.time()
xxx()
end_time = time.time()
print('函数的执行时间为>>>:', end_time - start_time)
return get_time
res = outer(index) # 赋值符号的左边是一个变量名 可以随意命名
res1 = outer(index)
res2 = outer(index)
jason = outer(index)
index = outer(index)
index()
home = outer(home)
home()

步骤六:

尝试给函数加上参数

'''6.但是我们在运行的时候又发现这个时候的半成品装饰器不能接收参数,于是我们给他加上参数(内部用于统计运行时间的函数)'''
def func(a):
time.sleep(0.1)
print('from func', a) def func1(a,b):
time.sleep(0.2)
print('from func1', a, b) def func2():
time.sleep(0.3)
print('from func2')
func(123)
def outer(xxx):
def get_time(a, b):
start_time = time.time()
xxx(a, b)
end_time = time.time()
print('函数的执行时间为>>>:', end_time - start_time)
return get_time
func1 = outer(func1)
func1(1, 2)
func = outer(func)
func(1)
func2 = outer(func2)
func2()

步骤七:

使用可变长参数来接收和传递参数

'''7.一些情况下我们又发现有些函数有参数有些函数没参数,因此我们想到使用可变长参数来达成接收这些参数并给计时的函数传参的方式'''
def func(a):
time.sleep(0.1)
print('from func', a)
def func1(a,b):
time.sleep(0.2)
print('from func1', a, b)
def outer(xxx):
# 这里的*args, **kwargs是定义阶段的形参——可变长形参,用于接收数据值
def get_time(*args, **kwargs): # get_time(1,2,3) args=(1,2,3)
start_time = time.time()
# 这里的*args, **kwargs是调用阶段的实参——可变长实参,把之前接收的数据值一个个拆分出来传参给被计时的函数
xxx(*args, **kwargs) # xxx(*(1,2,3)) xxx(1,2,3)
end_time = time.time()
print('函数的执行时间为>>>:', end_time - start_time)
return get_time
func = outer(func)
func(123)
func1 = outer(func1)
func1(1, 2)

步骤八:

给函数加上返回值

'''8.处理完这些之后我们又考虑到函数返回参数的话没有变量名接收这个返回值,于是我们在检测运行时间的时候给内部调用函数的代码绑定上变量名,之后在返回这个变量名'''
def func(a):
time.sleep(0.1)
print('from func', a)
return 'func'
def func1(a,b):
time.sleep(0.2)
print('from func1', a, b)
return 'func1'
def outer(xxx):
def get_time(*args, **kwargs):
start_time = time.time()
res = xxx(*args, **kwargs)
end_time = time.time()
print('函数的执行时间为>>>:', end_time - start_time)
return res
return get_time
# func = outer(func)
# res = func(123)
# print(res) func1 = outer(func1) res = func1(123, 123)
print(res)

六、装饰器模版

如果实在不能理解上文中的推导流程就直接记忆这个模版,模版中给的变量名函数名都是可以替换的

# 务必掌握
def outer(func):
def inner(*args, **kwargs):
# 执行被装饰对象之前可以做的额外操作
res = func(*args, **kwargs)
# 执行被装饰对象之后可以做的额外操作
return res
return inner

七、装饰器语法糖

当我们使用上面的装饰器模版的时候,改变调用方式的那个、偷天换日的操作容易造成误导。因此我们引进语法糖的概念:

def outer(func_name):
def inner(*args, **kwargs):
print('执行被装饰对象之前可以做的额外操作')
res = func_name(*args, **kwargs)
print('执行被装饰对象之后可以做的额外操作')
return res
return inner
"""
语法糖会自动将下面紧挨着的函数名当做第一个参数自动传给@函数调用,语法糖就是上文中把装饰器调用名变成原函数的操作
"""
@outer # func = outer(func)
def func():
print('from func')
return 'func' @outer # index = outer(index)
def index():
print('from index')
return 'index' func()
index()

八、作业

编写一个用户认证装饰器

函数:register login transfer withdraw

基本要求

执行每个函数的时候必须先校验身份 eg: jason 123

拔高练习(有点难度)

执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过

提示:全局变量 记录当前用户是否认证

# 定义一个变量判断是否登陆,另一个变量记录登陆的用户名
is_log_in = False
log_in_name = '' # 装饰器
def outer(func):
def inner(*args, **kwargs):
# 使用函数前 先进行判断,如果已经登陆过了就直接执行对应功能的代码,没有登陆就先登陆
global is_log_in
if is_log_in:
res = func(*args, **kwargs)
# 执行被装饰对象之后可以做的额外操作
return res else:
print('进行操作前需先验证身份')
login_uname = input('请输入账号').strip()
login_upwd = input('请输入密码').strip()
# 输入信息进行登陆
res00 = login(login_uname, login_upwd)
if res00:
# 登陆成功后更改全局变量中的状态和登陆对象(在login函数中改的is_log_in)
global log_in_name
log_in_name = login_uname
res = func(*args, **kwargs)
# 执行被装饰对象之后可以做的额外操作
return res
else:
return res00 return inner # 定义一个函数用于注册
@outer
def register(username, password):
is_reuse = False
# 定义变量判断是否登陆成功
for i in dictionary_log_dict:
# 遍历数据值拆开对比
inf_t_n, inf_t_p = dictionary_log_dict[i].split('|')
if inf_t_n == username:
# 名称重复就停止
is_reuse = True
return '重复的用户名'
if is_reuse == False:
# 名称没有重复就开始往字典中添加信息
ch = str(len(dictionary_log_dict) + 1)
dictionary_log_dict['user_infor' + ch] = '|'.join([username, password])
# 新用户默认金额为0
dictionary_infor_dict[username] = 0
return '注册成功' # 定义一个函数用于判断用户输入的信息是否正确
def login(username, password):
answer = False
for i in dictionary_log_dict:
inf_t_n, inf_t_p = dictionary_log_dict[i].split('|')
if inf_t_n == username and inf_t_p == password:
answer = True
is_log_in = True
log_in_name = username
if answer:
return answer
else:
return '用户名或密码错误' # 定义一个函数用于转账
@outer
def transfer(tran_n,tran_m):
# 定义转账的对象、金额
for i in dictionary_log_dict:
inf_t_n, inf_t_p = dictionary_log_dict[i].split('|')
if inf_t_n == tran_n:
# 转账对象在数据库中时进行转账
dictionary_infor_dict[tran_n] += tran_m
dictionary_infor_dict[log_in_name] -= tran_m
# re = dictionary_infor_dict[log_in_name]
print('剩余金额:'+ str(dictionary_infor_dict[log_in_name]))
return '转账成功'
else:
return '转账信息输入错误' # 定义一个函数用于提现
@outer
def withdraw(m_num):
# 提现之后金额减少,之间做减法
dictionary_infor_dict[log_in_name] = dictionary_infor_dict[log_in_name] - m_num
print('剩余金额:'+str(dictionary_infor_dict[log_in_name]))
return '提现成功' # 定义一个数据库存储数据值
dictionary_log_dict = {'user_infor1': '虹桥炮王|123',
'user_infor2': '猪猪男孩小康康|123',
'user_infor3': '赵世漫|123',
'user_infor4': '李帆|123',
'user_infor5': '段宇航|123',
'user_infor6': '猪猪男孩张红|123',
'user_infor7': '吴强|123',
'user_infor8': '陈傲|123',
'user_infor9': '郭贺阳|123',
'user_infor10': '李明轩|123',
'user_infor11': '宋晨晓|123',
'user_infor12': '刘少伟|123', }
# 定义一个字典存储用户余额
dictionary_infor_dict = {'虹桥炮王': 999,
'猪猪男孩小康康': 999,
'赵世漫': 999,
'李帆': 999,
'段宇航': 999,
'猪猪男孩张红': 999,
'吴强': 999,
'陈傲': 999,
'郭贺阳': 999,
'李明轩': 999,
'宋晨晓': 999,
'刘少伟': 999 } while True:
operation = input('功能简介:\n1、登陆\n2、注册\n3、转账\n4、提现\n5、退出\n请输入需要执行的指令:')
if operation == '1':
# 选择了登陆
login_uname = input('请输入账号').strip()
login_upwd = input('请输入密码').strip()
# 调用函数进行判断
ans1 = login(login_uname, login_upwd) if ans1:
is_log_in = True
print('登陆成功')
elif operation == '2':
# 选择了注册
sign_uname = input('请输入需要注册的账号').strip()
sign_upwd = input('请输入需要注册的账号的密码').strip()
# 调用函数进行判断
ans2 = register(sign_uname,sign_upwd)
print(ans2) elif operation == '3':
# 选择了转账
t_n = input('请输入转账对象名称:').strip()
t_m = int(input('请输入转账金额:').strip())
# 调用函数进行转账功能
tran = transfer(t_n,t_m)
print(tran) elif operation == '4':
# 选择了提现
number_out = int(input('请输入需要提现的金额:').strip())
# 调用函数进行提现功能
with_h = withdraw(number_out)
print(with_h)
elif operation == '5':
# 选择了退出
break
else:
print('请输入正确的指令')

10月11日内容总结——global和nonlocal方法、函数名的多种用法、闭包函数和装饰器的更多相关文章

  1. 北京Uber优步司机奖励政策(10月5日~10月11日)

    用户组:优步北京人民优步A组(适用于10月5日-10月11日) 滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/ ...

  2. 2016年10月11日 星期二 --出埃及记 Exodus 18:22

    2016年10月11日 星期二 --出埃及记 Exodus 18:22 Have them serve as judges for the people at all times, but have ...

  3. [mock]10月11日

    第二次mock.出的题是,假设有一个地区不能使用7,都用之后的数字代替,比如8代表7,18代表16(中间有7和17).那么给出一个这个地区的数X,求普通地区的数Y.首先是找规律,发现其实就是找给出的数 ...

  4. Android学习2013年10月11日

    1.LinearLayout http://www.cnblogs.com/salam/archive/2010/10/20/1856793.html LinearLayout是线性布局控件,它包含的 ...

  5. Week5(10月11日):国庆后补课的复杂心情

    Part I:提问  =========================== 1.说说你所知道的强类型视图HTML扩展方法. 2.请解释代码. @Html.ActionLink("链接文字& ...

  6. A song for a new begining 8月26日到10月11日 第一阶段

  7. c++ 作业 10月13日 进制转换最简单方法,控制c++输出格式方法 教材50的表格自己实践一下 例题3.1 setfill() setw()

    #include <iostream> #include <iomanip> using namespace std; int main(){ // int i; // cou ...

  8. 【¥200代金券、iPad等您来拿】 阿里云9大产品免费公测#10月9日-11月6日#

    #10.09-11.06#200元代金券.iPad大奖, 9大产品评测活动! 亲爱的阿里云小伙伴们: 云产品的多样性(更多的云产品)也是让用户深度使用云计算的关键.今年阿里云产品线越来越丰富,小云搜罗 ...

  9. 北京Uber优步司机奖励政策(10月26日~11月1日)

    用户组:优步北京人民优步A组(适用于10月26日-11月1日) 滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/ ...

  10. Java分布式互联网架构/微服务/高性能/springboot/springcloud 2018年10月17日直播内容

    2018年10月17日直播内容 大规模并发必备的消息中间件技术ActiveMq 网盘链接: https://pan.baidu.com/s/1GlxsZ2JnrvX- YN16-S7lQw 提取码: ...

随机推荐

  1. 要写文档了,emmm,先写个文档工具吧——DocMarkdown

    前言 之前想用Markdown来写框架文档,找来找去发现还是Jekyll的多,但又感觉不是很合我的需求 于是打算自己简单弄一个展示Markdown文档的网站工具,要支持多版本.多语言.导航.页内导航等 ...

  2. 轻量级领域驱动设计DDD Lite在嵌入式系统重构中的应用

    前言 目前,关于领域驱动设计(Domain Driven Design)DDD的培训,材料,视频都比较多,大家对DDD的一些概念都有所了解,但是在实际使用过程中,有很多的问题.例如 为什么DDD的架构 ...

  3. GO语言内存操作指导—unsafe的使用

    在unsafe包里面,官方的说明是:A uintptr is an integer, not a reference.Converting a Pointer to a uintptr creates ...

  4. liunx下安装Jexus5.8.2独立版运行asp.net MVC5

    Jexus 5.8.2独立版介绍 支持系统(64位):CentOS 6.5.Ubuntu 12.04版本以上包含当前版本. 运行环境:WebForm.Mvc3-5.WebService WebApi, ...

  5. 关于linux mint新增加的鼠标样式的示例图片不能正确显示的解决办法

    前言 我相信你在linux mint 做鼠标主题美化的时候一定遇到过这样的问题 没错!!! 下载的鼠标的主题的示例图片不能正确显示,当然这样虽然不影响正常的鼠标主题更换使用,但是对于我这种强迫症来说简 ...

  6. SpringCloud Alibaba(二) - Sentinel,整合OpenFeign,GateWay服务网关

    1.环境准备 1.1Nacos 单机启动:startup.cmd -m standalone 1.2 Sentinel 启动命令:java -Dserver.port=8858 -Dcsp.senti ...

  7. 私藏!资深数据专家SQL效率优化技巧 ⛵

    作者:韩信子@ShowMeAI 数据分析实战系列:https://www.showmeai.tech/tutorials/40 本文地址:https://www.showmeai.tech/artic ...

  8. 线上服务异常的定位、处理与优化的探索 - 第三章 Java虚拟机

    Java虚拟机   之所以引入关于JVM的篇章,是发现多数项目发生的线上问题很大的几率源自JVM调优配置不当引起.对JVM的内存模型.GC垃圾回收机制.调优方式有一个系统化的了解后,可以快速处理或避免 ...

  9. Service层

    package com.neu.service; import java.util.List; import com.neu.bean.User;import com.neu.dao.UserDao; ...

  10. Vm无法连接到虚拟机,请确保您有权限运行该程序、访问该程序使用的所有目录以及访问所有临时文件目录,未能将管道连接到虚拟机:所有的管道范例都在使用中解决方法

    可能是杀掉进程导致 解决办法: 1.首先杀掉所有VM打头的任务. 2.删掉所有lck文件 3.VM文件夹内有一串很长的数字命名的文件夹或文件,删掉 4.发现被VMware-vmx.exe占用 5.打开 ...