一、Python高阶用法
- 讲这一章节之前,我们先学习下Python的一个特性:python一切皆对象 ,举例如下
1、变量可以赋值为函数、函数也是一个对象,类型是function;这在Java里面是不可能的
2、一个函数里面可以返回另外一个函数
1.1、闭包
- 闭包 = 函数 + 环境变量(函数外部的变量就是该函数的环境变量)
1、第一个简单的闭包程序
def current1():
# a 叫做play的环境变量
a = 25
def play(x):
return a * x *x
return play
c1 = current1()
print('current1测试结果:' + str(c1(2)))
print('-----------------------------------------')
-
测试结果:play函数可以使用它的外部环境变量a
-
思考:在闭包程序的外部将a的值改变会不会影响到play的输出结果?
def current2(): b = 30 def play(x): return b * x * x return play b = 50 c2 = current2() print('current2测试结果:' + str(c2(2)))
-
测试结果:b = 50 并没有对play函数的运行结果产生影响
-
对比以上两段代码总结如下:
1、b等于50并没有起到作用、play里面b的值只与play外面的b=30有关,b=30是play的环境变量; current2()与play形成了一个闭包
2、b=50是外部变量,它的操作与play()无关、闭包 = 函数 + 环境变量(即play外层的current函数定义的变量)
2、闭包经典案例(笔试必考)
def test():
a = 10
def work():
a = 50
print(a)
print(a)
work()
print(a)
test()
print("============================================================")
-
执行结果:
-
上述程序执行流程分析:
1、打印的是a = 10,不做过多解释
2、执行work函数改变a的值,打印得到50
3、a = 10 -
从上我们可以得出以下结论:
1、局部变量的操作不能够影响到外部环境变量
2、此时work函数与test函数没有形成闭包了
3、因为work函数与test函数没有形成闭包,所以现在work函数里面的a参数是作用于它本身的局部变量,并不是外部环境变量a,只有形成了闭包a = 10才是work函数的环境变量,这样在work函数里面就可以操作a = 50,这样外部环境变量a的值才会发生改变。 -
加入下列新的闭包代码,对比分析:相较于第一个闭包程序,work函数多了一个 b = 20 的操作
-
python变量作用域:
1、L (Local) 局部作用域
2、E (Enclosing) 闭包函数外的函数中
3、G (Global) 全局作用域
4、B (Built-in) 内置作用域(内置函数所在模块的范围def test1(): b = 10 def work(): b = 20 return b * 2 return work t1 = test1() print(t1) # 打印对象值:我们发现t1的对象并不存在即t1.__closure__的值为none,此时闭包效果没有了 print(t1.__closure__)
**__closure__字段是用来打印对象值的;**为none表示此次闭包失败
-
测试结果:
-
执行流程分析:为什么闭包效果没有了,程序用b = 20这个局部变量计算,而不是用b = 10这个外部环境变量进行计算?
1、此时work函数内的b变量会被python识别为work内新定义的环境变量即work函数本身的局部变量,与test1外部函数的b变量并没有关联关系, 此时闭包失效
2、弥补方法:删除b = 20 即可实现闭包,print(t1.closure)也能打印出对象值,不会报none空
3、如要实现闭包、b也就不能再work函数内进行新的复制操作,否则闭包失效
3、闭包经典面试问题:旅行者路程计数问题(必考)
-
方式一:通过全局变量实现旅行者路程问题
origin = 0 def run(step): """ 1、此时origin属于局部变量,run是拿不到外部全局变量origin,只有将run函数内部的局部变量origin声明为全局变量才能行的通 2、global关键字就是将origin声明为全局变量的 """ global origin new_step = origin + step origin = new_step return new_step print(run(2)) print(run(3)) print(run(6)) print('++++++++++++++++++++++++++++++')
global 关键字是将局部变量声明为全局变量的,run是拿不到外部全局变量origin,只有将run函数内部的局部变量origin声明为全局变量才能行的通。
-
测试结果:
-
思考:这种方式虽然能够实现需求,但是我们操作和修改的都是全局变量,通常情况下最好不要修改全局变量,这个时候我们就需要用到闭包了,推荐使用方式二
-
方式二:闭包实现旅行者路程问题
start = 0 def factory(start): def run(step): """ 1、此时start属于run的局部变量,和factory的外部变量没有关联,所以这个start相当于run函数中新建的局部变量 2、解决方法:nonlocal关键字声明start不是run函数的局部变量,这样操作的就是外部的环境变量即factory函数的start变量 """ nonlocal start new_step = start + step start = new_step return start return run f = factory(start) # 我们可以观察到start全局变量并没有发生改变 print(f(2)) print('全局变量start值是否改变:'+ str(start)) print(f(3)) print('全局变量start值是否改变:'+ str(start)) print(f(6)) print('全局变量start值是否改变:'+ str(start))
nonlocal 关键字的作用:
1、当没有使用nonlocal 关键字时,start属于run的局部变量,和factory传入的外部环境变量start没有关联,所以这个start相当于run函数中新建的局部变量。
2、解决方法:nonlocal关键字声明start不是run函数的局部变量,这样操作的就是外部的环境变量即factory函数的start变量 -
运行结果:
-
总结:
1、直接操作全局变量不可行,因为所有的函数都能操作全局变量,不安全
2、闭包不会改变全局变量的值,推荐
1.2、Python高级表达式
1、lambda函数(匿名函数)
- lambda函数,又名匿名函数,在其它的语言中叫做lambda表达式,如何使用:
1、使用lambda关键字声明,且没有方法名
2、冒号前面是方法要传递的参数、冒号后面是方法的返回值
3、冒号后面只能是表达式,不能进行变量的赋值操作,不能是代码块 - 使用lambda表达式实现两数相加之和:
f = lambda x,y:x+y print('lambda函数运行结果:'+str(f(2,6))) print('+++++++++++++++++++++++++')
- 测试结果:
- python的三元表达式:与Java的三元表达式有一点差异
1、其它语言的表达方式:x > y ? x:y ,x 为表达式结果为true时返回,y为结果为false时返回
2、python:表达式正确时的返回结果 if 表达式 else 表达式错误时的返回结果a = 2 b = 1 # 表达式条件成立,返回a print(a if a > b else b)
- 测试结果:
2、map函数
-
map、三元表达式常常和lambda表达式联合使用;map() 会根据提供的函数对指定序列做映射,
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。 -
map 函数语法:
map(function, iterable, ...)
-
方式一:不使用map函数计算一个列表的每一个数字的平方
list_x = [1,2,3,4,5,6] # 普通函数实现x的平方 def pingfang(x): return x * x for x in list_x: print(pingfang(x),end = ' | ') print('普通调用结束') print('====================================')
-
测试结果:
- 方式二:使用map函数计算平方值
m = map(pingfang,list_x) print(m) print(list(m)) print('====================================') # 使用lambda函数与map函数精简执行 ma = map(lambda x : x * x,list_x) print(ma) print(list(ma)) print('====================================')
- 方式二:使用map函数计算平方值
-
测试结果:
-
由上可知map函数返回的是一个map类型的对象,所以我们需要将它转换成为我们所需要的数据类型。
-
测试多个列表使用map
list_a = [1,2,3,4,5,6,7,8] list_b = [1,2,3,4,5,6] m1 = map(lambda a,b:a+b,list_a,list_b) print(m1) print(list(m1))
-
测试结果:
-
总结:
1、lambda后面传递的list参数的个数要与lambda中要求传参的个数一致,一一对应
2、当两个或多个集合长度不一样是,map的运行结果以序列长度较短的个数为准;如下以list_b 的元素个数为标准输出结果
3、reduce函数
- 定义: 函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
- 什么是reduce:
1、reduce主要做连续运算、连续调用lambda表达式或者函数
2、使用方法:从functools模块导入reduce类即可使用
3、reduce的lambda函数的参数必须是两个、一个或者两个以上都不行
4、reduce只能操作一个序列,不能同时传入多个序列from functools import reduce list_x = [1,2,3,4,5,6,7,8] r1 = reduce(lambda x,y:x+y,list_x) print(r1) print('=======================================')
- 测试结果:
- 执行流程分析:
1、x = 1,y = 2 执行lambda函数得到结果为3
2、上一次执行的结果为x,即x = 3,y = 3 执行lambda函数3 + 3 =6
3、x = 6 ,y = 4 执行lambda函数结果为 6 + 4 = 10
4、x = 10 ,y = 5 执行结果为10 + 5 = 15
6、x = 15 ,y = 6 执行结果为15 + 6 = 21
7、x = 21 ,y = 7 执行结果为21 + 7 = 28
8、x = 28 ,y = 8 执行结果为28 + 8 = 36 - reduce函数设置连续计算的起始值
r2 = reduce(lambda x,y:x+y,list_x,10) print(r2) print('=======================================')
- 测试结果:上述代码中10就是计算的初始值,没有指定默认为0
4、filter过滤类
-
作用:
1、作用:将lambda表达式或者函数的返回结果为true的筛选并打印出来
2、可以过滤一个序列,不能过滤多个序列
3、返回一个filter的对象list_x = [1,2,3,4,5,6] f = filter(lambda x:True if x > 3 else False,list_x) print(f) print(list(f))
-
测试结果:
二、Python的装饰器
- 定义:类似于Java里面的@标签,可复用,简化代码结构;装饰器对修改是封闭的,对扩展是开放的。
2.1 自定义装饰器:获取当前时间
-
自定义装饰器
import time # 自定义装饰器:获取当前时间 def dirhappy(fun): def happy(name): # time.time()获取当前时间 print(time.time()) fun(name) return happy
-
原生调用方式
def test1(name): print('this id a directory test1' + str(name)) # 原生调用方式 h1 = dirhappy(test1) h1('zhangsan') print('=================================')
-
测试结果
-
语法糖调用,推荐
@dirhappy def test2(name): print('this id a directory test2' + str(name)) test2('lisi')
-
测试结果:
2.2、自定义装饰器:
-
要求:1、兼容可变参数;2、兼容关键字参数;3、打印当前时间。
import time def dirsun(fun): """ 1、*args代表可变参数、**KW代表关键字参数 2、可变参数不兼容关键字参数 """ def sun(*args,**kw): print('当前时间'+str(time.time())) fun(*args,**kw) return sun @dirsun def test1(name): print('this is direction test' + name) @dirsun def test2(name1,name2): print('this is direction test' + name1) print('this is direction test' + name2) @dirsun def test3(name1,name = 'name',age = 0,beizhu = '长安'): print('this is direction test' + name1) print('this is direction test' + str(name) + str(age) + str(beizhu)) @dirsun def test4(**kw): print('this is direction test' + str(kw))
注意:*args代表可变参数、**KW代表关键字参数;可变参数不兼容关键字参数
-
测试代码:
test1('张三') test2('石敢当',"小福贵") test3('小微',name = '张小敬',age = 24,beizhu = '十年陇右兵,九年不良帅') test4(name = "崔器",shiji = '陇右九年兵,旅贲军统帅,军帐中攒有贼寇人头84科',age = 28)
-
测试结果:
三、列表推导式
- 定义:序列,集合,字典都是用于列表推导式,可以大大的简化开发
# 列表推导式
list = [1,2,3,4,5,6]
# 测试数组
b = [i * i for i in list]
print(b)
# 条件推到
b = [i * i for i in list if i>3]
print(b)
print('==================================')
# 测试元组
yuanzu = (1,2,3,4,5,6)
a = (i **3 for i in yuanzu)
# 注意元组返回的结果是一个对象,需要转一下
print(tuple(a))
print('==================================')
# 测试集合
jihe = {1,2,3,3,3,4,4,5}
c = {i * 3 for i in jihe}
print(c)
print('==================================')
# 测试字典
zidian = {'name':'zhangsan','age':24,'sex':'男'}
# 注意:一定是字典的items()迭代器,否则遍历不了
d = {key for key,value in zidian.items()}
print(d)
# 将key,value的值交换输出
e = {value:key for key,value in zidian.items()}
print(e)
- 测试结果:
四、None详解
-
None本身就是一个对象,type为NoneType:
1、空字符串、空[]数组、false等都不是None他们之间没有相等的关系
2、空字符串、空数组、空集合、false以及None在做if条件判断是都默认返回False -
六种基本数据测试
a = '' b = [] c = set() d = False e = 0 f = {} print(a == None) print(b == None) print(c == None) print(d == None) print(e == None) print(f == None) print('===================') print(a is None) print(b is None) print(c is None) print(d is None) print(e is None) print(f is None) print('===================') print(type(None)) print('===================') if not a: print(True) else: print(False)
-
打印结果:
由上可知,元素为空与None并不等同 -
如何判断一个对象是否为空:以下是一个特例对象实际上不为空,当事结果判断为空
class test(): # def __play__(self): # return 0 # __len__是python里面的一个特殊函数,用于描述对象的长度,如下当返回值为0 或者 false时,判断语句为假说明此对象为空但实际上此对象并不为空 def __len__(self): return 0 test = test() if test: print('T') else: print("F")
-
测试结果:
__len__是python里面的一个特殊函数,用于描述对象的长度,如下当返回值为0 或者 false时,判断语句为假说明此对象为空但实际上此对象并不为空