Python语言的高级特性
函数式编程(FunctionalProgramming)
-
基于lambda演算的一种编程方式
- 程序中只有函数
- 函数可以作为参数,同样可以作为返回值
- 纯函数式编程语言: LISP, Haskell
-
Python函数式编程只是借鉴函数式编程的一些特点,可以理解成一半函数式一半Python
- 高阶函数
- 返回函数
- 匿名函数
- 装饰器
- 偏函数
lambda表达式
-
函数: 最大程度复用代码
- 存在问题: 如果函数很小,很短,则会造成啰嗦
- 如果函数被调用次数少,则会造成浪费
- 对于阅读者来说,造成阅读流程的被迫中断
-
lambda表达式(匿名函数):
- 一个表达式,函数体相对简单
- 不是一个代码块,仅仅是一个表达式
- 可以有参数,有多个参数也可以,用逗号隔开
# lambda表达式的用法
# 1. 以lambda开头
# 2. 紧跟一定的参数(如果有的话)
# 3. 参数后用冒号和表达式主体隔开
# 4. 只是一个表达式,所以,没有return
# 计算一个数字的100倍数
# 因为就是一个表达式,所以没有return
stm = lambda x: 100 * x
# 使用上跟函数调用一模一样
stm(123)
12300
stm1 = lambda x,y,z: x + y*10 + z*100
stm1(1,2,3)
321
高阶函数
- 把函数作为参数使用的函数,叫高阶函数
# 变量可以赋值
a = 100
b = a
print(b)
# 函数名称就是一个变量
def funA():
print('i am funA')
funA()
funB = funA
funB()
100
i am funA
i am funA
得出的结论:
- 函数名称是变量
- funB 和 funA只是名称不一样而已
- 既然函数名称是变量,则应该可以被当做参数传入另一个函数
# 高阶函数举例
# funA是普通函数,返回一个传入数字的100倍数字
def funA(n):
return n * 100
#再写一个函数,把传入的参数乘3倍
def funB(n):
return funA(n) * 3
print(funB(10))
#写一个高阶函数
def funC(n,f):
return f(n) * 3
print(funC(10,funA))
# 比较funC和funB, 显然funC的写法要优于funB
# 例如:需求变更,需要把n放大三十倍,此时funB则无法实现
# funC只需要再写一个其他函数再调用即可
def funD(n):
return n * 10
print(funC(10,funD))
3000
3000
300
高阶函数-map
- 原意就是映射,即把集合或者列表的元素,每一个元素都按照一定规则进行操作,生成一个新的列表或者集合
- map函数是系统提供的具有映射功能的函数,返回值是一个迭代对象
# map举例
# 有一个列表,想对列表里的每一个元素乘以10, 并得到新的列表
l1 = [i for i in range(10)]
print(l1)
l2 = []
for i in l1:
l2.append(i * 10)
print(l2)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
# 利用map实现
def mulTen(n):
return n * 10
l3 = map(mulTen,l1)
print(list(l3))
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
高阶函数-reduce
- 原意是归并,缩减
- 把一个可迭代对象最后归并成一个结果
- 对于作为参数的函数要求: 必须由两个参数,必须由返回结果
- reduce([1,2,3,4,5]) == f( f(f(f(1,2),3), 4),5)
- reduce 需要导入functools包
from functools import reduce
#定义一个操作函数
def myAdd(x,y):
return x + y
print(reduce(myAdd,[i for i in range(10)]))
45
高阶函数-filter
- 过滤函数: 对一组数据进行过滤,符合条件的数据会生成一个新的列表并返回
- 跟map相比较:
- 相同:都对列表的每一个元素逐一进行操作
- 不同:
- map会生成一个跟原来数据相对应的新队列
- filter不一定,只有符合条件的才会进入新的数据集合
- filter函数怎么写:
- 利用给定函数进行判断
- 返回值一定是个布尔值
- 调用格式: filter(f, data), f是过滤函数, data是数据
# 案例
# 对一个列表进行过滤,偶数组成一个新列表
# 定义过滤函数
# 过滤函数要求有输入,返回布尔值
def isEven(n):
return n % 2 == 0
print(list(filter(isEven,[i for i in range(20)])))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
高阶函数-sorted
- 把一个序列按照给定算法进行排序
- key: 在排序前对每一个元素进行key函数运算,可以理解成按照key函数定义的逻辑进行排序
a = [11215,55566,56166,568,516,6,15,654,46584,4]
print(sorted(a))
print(sorted(a,reverse=True)) # 倒序
[4, 6, 15, 516, 568, 654, 11215, 46584, 55566, 56166]
[56166, 55566, 46584, 11215, 654, 568, 516, 15, 6, 4]
a = [-43,23,45,6,-23,2,-4345]
# 按照绝对值进行排序
# abs是求绝对值的意思
al = sorted(a, key=abs)
print(al)
al = sorted(a, key=abs, reverse=True)
print(al)
[2, 6, 23, -23, -43, 45, -4345]
[-4345, 45, -43, 23, -23, 6, 2]
astr = ['sda','asd','fsdf','fsgfg','Ada']
str1 = sorted(astr)
print(str1)
str2 = sorted(astr, key=str.lower)
print(str2)
['Ada', 'asd', 'fsdf', 'fsgfg', 'sda']
['Ada', 'asd', 'fsdf', 'fsgfg', 'sda']
返回函数
- 函数可以返回具体的值
- 也可以返回一个函数作为结果
# 定义一个普通函数
def myF(a):
print('In myF')
return None
a = myF(8)
print(a)
In myF
None
# 函数作为返回值返回, 被返回的函数在函数体内定义
def myF2():
def myF3():
print('In myF3')
return 3
return myF3
# 使用上面定义
# 调用myF2, 返回一个函数myF3,赋值给f3
f3 = myF2()
print(type(f3))
print(f3)
f3()
<class 'function'>
<function myF2.<locals>.myF3 at 0x00000199649DB5E8>
In myF3
3
def myF4( *args):
def myF5():
rst = 0
for n in args:
rst += n
return rst
return myF5
f5 = myF4(1,2,3,4,5,6,7,8,9,0,)
f5()
45
闭包(closure)
- 当一个函数在内部定义函数,并且内部的函数应用外部函数的参数或者局部变量,当内部函数被当做返回值的时候,相关参数和变量保存在返回的函数中,这种结果,叫闭包
- 上面定义的myF4是一个标准闭包结构
#闭包常见问题
def count():
fs = []
for i in range(1,4):
def f():
return i * i
fs.append(f)
return fs
f1,f2,f3 = count()
print(f1())
print(f2())
print(f3())
9
9
9
出现的问题:
- 造成上述状况的原因是,返回函数引用了变量i, i并非立即执行,而是等到三个函数都返回的时候才统一使用,此时i已经变成了3,最终调用的时候,都返回的是 3*3
- 此问题描述成:返回闭包时,返回函数不能引用任何循环变量
- 解决方案: 再创建一个函数,用该函数的参数绑定循环变量的当前值,无论该循环变量以后如何改变,已经绑定的函数参数值不再改变
# 修改上述函数
def count2():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1,4):
fs.append(f(i))
return fs
f1,f2,f3 = count2()
print(f1())
print(f2())
print(f3())
1
4
9
装饰器(Decrator)
- 在不改动函数代码的基础上无限制扩展函数功能的一种机制,本质上讲,装饰器是一个返回函数的高阶函数
- 装饰器的使用: 使用@语法, 即在每次要扩展到函数定义前使用@+函数名
def hello():
print('Hello world')
hello()
Hello world
f = hello
f()
Hello world
# f和hello是一个函数
print(id(f))
print(id(hello))
print(f.__name__)
print(f.__name__)
1758329683688
1758329683688
hello
hello
# 现在由新的需求:
# 对hello功能进行扩展,每次打印hello之前打印当前系统时间
# 而实现这个功能又不能改动现有代码
# ==>使用装饰器
# 定义一个装饰器
import time
def printTime(f):
def wrapper(*args, **kwargs):
print('Time is ',time.ctime())
return f(*args, **kwargs)
return wrapper
# 上面定义了装饰器,使用的时候需要用到@, 此符号是python的语法糖
@printTime
def hello():
print("Hello world")
hello()
Time is Mon Dec 16 22:53:20 2019
Hello world
# 装饰器的好处是,一旦定义,则可以装饰任意函数
# 一旦被其装饰,则把装饰器的功能直接添加到定义函数的功能上
@printTime
def hello1():
print('i love python')
hello1()
Time is Mon Dec 16 22:54:34 2019
i love python
##偏函数
- 参数固定的函数,相当于一个由特定参数的函数体
- functools.partial的作用是,把一个函数某些函数固定,返回一个新函数
# 把字符串转化成十进制数字
int("12345")
# 求八进制的字符串12345,表示成十进制的数字是多少
int("12345", base=8)
5349
# 新建一个函数,此函数是默认输入的字符串是8进制数字
# 把此字符串返回十进制的数字
def int8(x, base=8):
return int(x, base)
int8("12345")
5349
import functools
#实现上面int8的功能
int8 = functools.partial(int, base=8)
int8("12345")
5349
zip
- 把两个可迭代内容生成一个可迭代的tuple元素类型组成的内容
l1 = [1,2,3,4,5,6]
l2 = [11,22,33,44,55,66]
z = zip(l1,l2)
print(type(z))
print(z)
for i in z:
print(i)
<class 'zip'>
<zip object at 0x000001562080E108>
(1, 11)
(2, 22)
(3, 33)
(4, 44)
(5, 55)
(6, 66)
l1 = ['xiaoliu','xiaolong','caida','cquan']
l2 = [1,2,3,4]
z = zip(l1,l2)
for i in z:
print(i)
('xiaoliu', 1)
('xiaolong', 2)
('caida', 3)
('cquan', 4)
enumerate
- 跟zip功能比较像
- 对可迭代对象里的每一元素,配上一个索引,然后索引和内容构成tuple类型
l1 = ['xiaoliu','xiaolong','caida','cquan']
em = enumerate(l1)
print(type(em))
l2 = [i for i in em]
print(l2)
<class 'enumerate'>
[(0, 'xiaoliu'), (1, 'xiaolong'), (2, 'caida'), (3, 'cquan')]
em = enumerate(l1, start=10)
l2 = [i for i in em]
print(l2)
[(10, 'xiaoliu'), (11, 'xiaolong'), (12, 'caida'), (13, 'cquan')]
collections模块
namedtuple
- tuple类型
- 是一个可命名的tuple
import collections
Point = collections.namedtuple('Point',['x','y'])
p = Point(11,22)
print(p.x)
print(p.y)
print(p[0])
print(p[1])
11
22
11
22
Circle = collections.namedtuple('Circle',['x','y','r'])
c = Circle(100,150,50)
print(type(c))
print(c)
<class '__main__.Circle'>
Circle(x=100, y=150, r=50)
dequeue
- 比较方便的解决了频繁删除插入带来的效率问题
from collections import deque
q = deque(['a','b','c'])
print(q)
q.append('d')
print(q)
q.appendleft('x')
print(q)
deque(['a', 'b', 'c'])
deque(['a', 'b', 'c', 'd'])
deque(['x', 'a', 'b', 'c', 'd'])
defaultdict
- 当直接读取dict不存在的属性时,直接返回默认值
d1 = {"one":1, "two":2, "three":3}
print(d1['one'])
print(d1['four'])
1
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-24-83fcfcbfcf65> in <module>
1 d1 = {"one":1, "two":2, "three":3}
2 print(d1['one'])
----> 3 print(d1['four'])
KeyError: 'four'
from collections import defaultdict
func = lambda: 'xiaoliu'
d2 = defaultdict(func)
d2['one'] = 1
d2['two'] = 2
print(d2['one'])
print(d2['four'])
1
xiaoliu
Counter
- 统计字符串个数
from collections import Counter
c = Counter('abcdefgabcdefabcdeabcdabcaba')
print(c)
Counter({'a': 7, 'b': 6, 'c': 5, 'd': 4, 'e': 3, 'f': 2, 'g': 1})
s = ['xiaoliu','love','love','love','love','love','love','xiaolong']
c = Counter(s)
print(c)
Counter({'love': 6, 'xiaoliu': 1, 'xiaolong': 1})