函数对象

函数是第一对象: python 中万物皆对象,函数同样也是 python 中的对象 、 函数作为对象可以赋值给一个变量、可以作为元素被添加到容器对象中、可以作为参数传给其他函数、可以作为函数的返回值(这些特性就是第一类对象所特有的)

函数对象的特性(*****灵活运用,后面讲装饰器会用到)

  函数名可以像变量一样被传递

# 变量可以被传递
name = 'jason'
x = name
print(x)
# jason
print(id(name), id(x))
# 3085464224688 3085464224688 # 尝试函数像变量一样被传递
def func():
print('from func')
print(func)
# <function func at 0x0000016E5E062E18> f = func # 其实指向的也是函数func 指向的函数体代码的内存地址
print(f)
# <function func at 0x000001B4D0D92E18>
f()
# from func
print(id(func), id(f))
# 3085463137816 3085463137816

  函数名可以被当做参数传递给其他函数

def func():
print("from func") def index(args):
print(args)
args() # 函数只要一定义(先定义),就可以在任意地方调用
print("from index") index(func)
# <function func at 0x000001B7429A2E18>
# from func
# from index

  函数名可以被当做函数的返回值

def index():
print('index') def func():
print('func')
return index res = func() # 将返回的函数名index 赋值给变量res
# func
print(res)
# <function index at 0x000001EF64362E18>
res() # 函数名加括号(...内存地址)就可以直接调用该函数
# index

  函数名可以被当做容器类型的参数

def func():
print('func') l = [1, 2, func, func()] # 定义的时候默认执行了func(),所以下面先打印了 func
# func # func函数没有返回值,默认返回None,所以列表 l 的第四个元素就是None
print(l)
# [1, 2, <function func at 0x0000013931C92E18>, None]

函数对象小练习

题目: # 循环打印项目功能提示信息 供用户选择 用户选择谁就执行谁

def register():
print("注册了")
pass def login():
print("登录了")
pass def shopping():
print("购物了")
pass def output_func_list():
print("----- 请选择功能!")
for key in func_list:
print(f"---- {key}.{func_list[key][1]}") func_list = {
0: [register, '注册'],
1: [login, '登录'],
2: [shopping, '购物'],
} while True:
output_func_list()
chose_func = input("请输入功能编号(q 退出系统):").strip()
if chose_func.isdigit():
# 执行相应功能
chose_func = int(chose_func)
# 判断输入的编号在不在功能列表里
if chose_func in func_list:
func_list[chose_func][0]() # 取到功能函数名,加括号调用
else:
print("您输入的功能编号不存在,请重新输入!")
elif chose_func.lower() in ['q', 'quit']:
print("感谢您的使用,祝您生活愉快~")
break
else:
print("请正确输入数字!")

实现代码

知识点: # 函数名可以作为容器对象的元素值 , # 函数名(即函数内存地址)可以加括号直接调用

  上述其他三个特性在装饰器中会有灵活运用,就暂不举例了

函数的嵌套调用与定义

嵌套调用

函数的嵌套调用: # 在函数内部调用其他函数

def index():
print('index') def func():
index() # 在定义 func 函数的时候不会直接调用 index 的方法 --> 函数定义的时候不执行代码
print('func') func()
# index # 通过 func()函数内部调用了index() 函数,打印出了 index
# func

函数的嵌套调用可以 # 将复杂的逻辑简单化

  小练习: # 写一个函数可以求四个数中的最大值(不许用内置函数 max)

def my_max(x, y):
if x > y:
return x
return y def my_max4(a, b, c, d):
res = my_max(a, b)
res = my_max(res, c)
res = my_max(res, d)
return res print(my_max4(1, 5, 7, 1))
#

案例代码

嵌套定义

def outer():
x = 1
print("outer")
def inner():
print("inner")
inner() # inner() # 会报错,在外部无法访问内部内容
outer()
# outer
# inner

实现在外部调用 outer函数的内部函数 inner

# 想在外部调用inner 可通过把内部的函数名当做外部函数的返回值来返回给外部
def outer():
x = 1
print("outer")
def inner():
print("inner")
return inner # 把 inner 函数当做函数的返回值返回给 outer函数的调用者 res = outer()
# outer
res() # 变相调用inner
# inner

实现代码

小案例: # 写一个函数,该函用户可以通过传参的不同 控制函数指向不同的功能

def all_func(type):
def register():
print('register')
def login():
print('login')
def shopping():
print('shopping') if type == 1:
register()
if type == 2:
login()
if type == 3:
shopping() all_func(1)
all_func(2)
all_func(3)
# register
# login
# shopping

实现代码

命名空间(又叫名称空间)(****绕且重要)

命名空间: # 存放的是变量名与变量值的内存地址绑定关系的地方

访问变量的值: # 要想访问一个变量的值,必须先去命名空间拿到对应的名字,才能访问变量的值

命名空间的分类

命名空间分为: # 内置命名空间、全局命名空间、局部命名空间 三大类

内置命名空间

内置命名空间: # python 解释器提前已经定义好了的名字(已经存放到了内置命名空间中了)

print("hello world")
max(1, 44, 62, 15)
len('26515f1asfafqw')
sum([1, 2, 3, 4, 5])
# 像上面的print max len sum 并没有定义就可以值使用,它们就是python解释器提前定义好了的函数,属于内置命名空间的

全局命名空间

全局命名空间: # 文件级别的代码

x = 1
if x == 1:
y = 2
print(y)
# for i in [1, 2]:
print(i)
print(i)
#
#
# # 上面的 x y z 都在全局命名空间,不要以为缩进的就是局部的(if、 for、 while 无论嵌套,多少层,他们内部所创建的名字都是全局命名空间的)

局部命名空间

局部命名空间: # (目前所学)函数体内创建的名字都属于局部命名空间(最外层的函数名是属于全局命名空间的)

def func():
username = 'jason'
# print(username) # 会报错 NameError: name 'username' is not defined ,username 这个变量在 func 这个函数的命名空间里,在全局命名空间里访问不到它
func()

  至于为什么上面的 print(username) 为什么会报错,学完下面的知识你就知道啦。

命名空间的生命周期

'''
命名空间的生命周期
内命名空间:(最长)只要 python解释器启动,立马创建 关闭 python解释器时自动销毁
全局命名空间: 只要右键运行 py文件就会自动创建 py文件程序运行结束自动销毁
局部命名空间:(动态创建动态销毁)函数被调用的时候自动创建 函数执行结束后立即销毁
'''

补充:与垃圾回收机制的关系

# 命名空间生命周期结束 -- >  里面存的变量与指向值的内存地址解绑,内存中的值等待垃圾回收机制回收
# del 删除变量 -- > 里面存的变量与指向值的内存地址解绑,内存中的值等待垃圾回收机制回收 ---> 等同于命名空间里删除了一个变量(绑定关系)
# 垃圾回收机制:垃圾回收机制隔一段时间就会检查一次,内存中的值如果没有变量指向它(引用),那垃圾回收机制就会把它清除掉(释放内存)
# 如果多次检查都有变量等指向它,那就会把它等级提升,检查频率就会变低

命名空间的查找顺序

验证思路: # 找一个三个地方都有的东西来验证(比如 len、max等,暂时忽略命名规范不能与关键字重复) , # 分别注释来测试其查找顺序(全局、局部)

验证过程

len = '我是全局命名空间的len'
def func():
len = '我是局部命名空间的len'
print(len)
print(len) # 这里是全局的位置
# 我是全局命名空间的len
'''
# 把全局的len 注释掉,就去找了内置的len
print(len) # 是全局的位置
# <built-in function len>
'''
func()
# 我是局部命名空间的len

大致结论:

'''
(******)命名空间的查找顺序
1.需要先确定当前的在哪(全局、局部),大前提
1.1 站在全局:全局 >>> 内置
1.2 站在局部:局部 >>> 全局 >>> 内置
1.2.2 站在局部的内部(多个局部嵌套):局部 >>> 上一级局部 >>> 上一级局部 >>> .... >>> 全局 >>> 内置
会在作用域同级的前后(这句代码前后的同级语句)去找,然后再上一级
2.函数在定义阶段查找名字的顺序(范围)就已经固定了, 不会因为函数的调用位置变化而变化(*******)
可以在函数定义的时候写个注释,指出他查找的位置,防止逻辑复杂了搞不清楚
'''

加深理解的小案例

# 通过注释不同函数层内的x 来加深理解命名空间查找顺序(可以采用收起(折叠)代码块的技巧来快速指定)
x = 111
def f1():
x = 222
def f2():
x = 333
def f3():
# x = 444
def f4():
# x = 555
print(x) # 这个案例在本局部找到了 变量x, 所以用的是内部的这个 777,在调用前定义了,所以不会报错
x = 777 # 纯粹为了教学演示
f4()
x = 777 # 纯粹为了教学演示
f3()
f2()
f1()
#

案例一(可多次练习)

def func():
x = 1
def index():
print(x) # 查找顺序:本作用域找x,没找到,上一级func里找,找到了,那就引用的是func 作用域里的 局部变量x
return index res = func()
x = 999
res()
#

案例二

x = 111
def outer():
def inner():
print('from inner', x) # 查找顺序:函数体inner 内上下没有x,再找 outer里面,也没有x, 那就找全局,找到了x,所以这里的x 就是全局的x
return inner
f = outer()
x = 222 # 调用前改变了全局 x 的值,所以最后结果是 222
f()
# from inner 222

案例三

x = 111
def outer():
def inner():
print('from inner', x) # 查找顺序:函数体inner 内上下没有x,再找 outer里面,也没有x, 那就找全局,找到了x,所以这里的x 就是全局的x
return inner
f = outer()
def func():
x = 333 # 没有global 关键字指定x 为全局变量,这里是重修申请了一个局部变量 x,这里并不会影响 全局的那个x
f()
func()
# from inner 111

案例四

# 下面这个案例会直接报错
x = 111
def outer():
def inner():
print('from inner', x) # 会直接报错,UnboundLocalError: local variable 'x' referenced before assignment ---> 本地变量(局部变量)x 在定以前被引用了
# 报错原因: ---> 查找顺序:函数体 inner内部有 x, 所以这个print 内x 指定的是x = 66666666的那个局部变量,而调用print 时,他还没定义出来
x = 66666666
return inner
f=outer()
f()

案例五(讲原理,忽略智能语法报错)

案例一原理图

作用域

python中的作用域有 全局作用域 与 局部作用域 , 全局作用域: # 全局有效: 内置命名空间、全局命名空间 都属于全局作用域 , 局部作用域: # 局部有效:局部命名空间

  局部修改全局变量(修改和访问是两回事)

# 尝试修改不可变类型的全局变量
x = 1
def func():
x = 2 # 实质是又创建了一个局部变量 x
func()
print(x) # 局部无法修改不可变类型的全局变量
# # 尝试修改可变类型的局部变量
x = []
def func():
x.append('嘿嘿嘿')
func()
print(x) # 修改成功,局部可以修改可变类型的全局变量
# ['嘿嘿嘿'] # 全局访问不了局部的变量,所以不展开探讨

    小结论: # 局部无法修改不可变类型的全局变量 , # 局部可以修改可变类型的全局变量 (前提:在不使用 global  和  nonlocal  关键字的情况下)

  通过  global  关键字在局部修改全局,修改多个用 , 隔开

x = 1  # 不可变类型
username = 'jason'
def func():
global x,username
x = 999 # 修改全局变量,而不是创建局部变量
username = 'egon'
func()
print(x, username)
# 999 egon

  通过  nonlocal  关键字在局部修改局部,修改多个用 , 隔开

def func():
x = 1
def index():
x = 2
index()
print(x)
func()
# # 想就在 index 里把 x 改了
def func():
x = 1
def index():
nonlocal x
x = 2
index()
print(x)
func()
#

小扩展(python之禅)--- 娱乐了解

import this

python函数对象-命名空间-作用域-02的更多相关文章

  1. 【Python 函数对象 命名空间与作用域 闭包函数 装饰器 迭代器 内置函数】

    一.函数对象 函数(Function)作为程序语言中不可或缺的一部分,但函数作为第一类对象(First-Class Object)却是 Python 函数的一大特性. 那到底什么是第一类对象(Firs ...

  2. 九. Python基础(9)--命名空间, 作用域

    九. Python基础(9)--命名空间, 作用域 1 ● !a 与 not a 注意, C/C++可以用if !a表示if a == 0, 但是Python中只能用if not a来表示同样的意义. ...

  3. python 函数对象(函数式编程 lambda、map、filter、reduce)、闭包(closure)

    1.函数对象 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 秉承着一切皆对象的理念,我们再次回头来看函数(function).函 ...

  4. Python 函数对象的本质

    Python 函数对象本质上是 function 类的实例. 1 从示例说起 def factorial(n): '''return n!''' return 1 if n < 2 else n ...

  5. python 函数对象、函数嵌套、名称空间与作用域、装饰器

    一 函数对象 一 函数是第一类对象,即函数可以当作数据传递 1 可以被引用 2 可以当作参数传递 3 返回值可以是函数 3 可以当作容器类型的元素 二 利用该特性,优雅的取代多分支的if def fo ...

  6. Python 函数对象-函数嵌套-名称空间与作用域-闭包函数

    今日内容: 1. 函数对象 函数是第一类对象: 指的是函数名指向的值可以被当中数据去使用 1.可以被引用 2.可以当做参数传给另一个函数 3.可以当做一个函数的返回值 4.可以当做容器类型的元素 2. ...

  7. python 函数对象、函数嵌套、名称空间与作用域

    一 函数对象 一 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素二 利用该特性,优雅的取代多分支的if def ...

  8. python 函数及变量作用域及装饰器decorator @详解

    一.函数及变量的作用   在python程序中,函数都会创建一个新的作用域,又称为命名空间,当函数遇到变量时,Python就会到该函数的命名空间来寻找变量,因为Python一切都是对象,而在命名空间中 ...

  9. Python入门笔记(22):Python函数(5):变量作用域与闭包

    一.全局变量与局部变量 一个模块中,最高级别的变量有全局作用域. 全局变量一个特征就是:除非被删除,否则他们存活到脚本运行结束,且对于所有的函数都可访问. 当搜索一个标识符(也称变量.名字等),Pyt ...

随机推荐

  1. c#基于Tablet pc实现的手写输入

    需要安装Tablet pc,win7的话 直接在控制面板>程序和应用>添加组建里面勾选上添加 然后就是下面的程序了,看代码 设计文件 namespace 手写识别 { partial cl ...

  2. Windows下获取高精度时间注意事项 [转贴 AdamWu]

    花了很长时间才得到的经验,与大家分享. 1. RDTSC - 粒度: 纳秒级 不推荐优势: 几乎是能够获得最细粒度的计数器抛弃理由: A) 定义模糊 - 曾经据说是处理器的cycle counter, ...

  3. Qt元类型(MetaType)注册入门(附一些官方文档的关键摘录)

    昨天调试项目时,突然发现如下消息: QObject::connect: Cannot queue arguments of type 'ERROR_LEVEL' (Make sure 'ERROR_L ...

  4. 【数据结构】30、hashmap=》hash 计算方式

    前提知识 写在前面,为什么num&(length - 1) 在length是2的n次幂的时候等价于num%length n - 1意味着比n最高位小的位都为1,而高的位都为0,因此通过与可以剔 ...

  5. hadoop之hive集合数据类型

    除了string,boolean,date等基本数据类型之外,hive还支持三种高级数据类型: 1.ARRAY ARRAY类型是由一系列相同数据类型的元素组成,这些元素可以通过下标来访问.比如有一个A ...

  6. ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理

    在前面两篇随笔<ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理>和<ABP开发框架前后端开发系列---(8)ABP框架之Winform界面的开发过程>开始 ...

  7. ios开发系列之内存泄漏分析(上)

    ios自从引入ARC机制后,一般的内存管理就可以不用我们码农来负责了,但是一些操作如果不注意,还是会引起内存泄漏. 本文主要介绍一下内存泄漏的原理.常规的检测方法以及出现的常用场景和修改方法. 1.  ...

  8. Spring源码阅读-IoC容器解析

    目录 Spring IoC容器 ApplicationContext设计解析 BeanFactory ListableBeanFactory HierarchicalBeanFactory Messa ...

  9. 5分钟学会Java9-Java11的七大新特性

    现在Java有多元化的发展趋势,既有JS又有C++还有C#的影子,不学习那是不行滴. 来来来,花5分钟看看Java9-Java11的七大新特性,还有代码样例. Java11 发布了,然而很多公司还在用 ...

  10. 【设计模式】行为型05责任链模式(Chain of responsibility Pattern)

    学习地址:http://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html demo采用了DEBUG级别举例子,理解起 ...