Python自学入门笔记#函数.3(函数式编程以及内部函数等)

1. 函数式编程思想

【定义】
函数式编程式指使用一系列的函数解决问题,函数仅接受输入并产生输出,不包含任何能影响产生输出的内部状态,在任何情况下,使用相同的参数调用函数始终能产生同样的结果。
在一个函数式的程序中,输入的数据“流过”一系列的函数,每一个函数根据它的输入产生输出。函数式风格避免编写有“边界效应”(side effects)的函数:修改内部状态,或者是其他无反应在输出上的变化。 完全没有媒介效应的函数被称为“纯函数式的”(purely functional)。避免边界效应意味着不是用在程序运行时可变的数据结构,输出只依赖于输入。
可以认为函数式编程刚好站在了面向对象编程的对立面,对象通常包含内部状态(字段),和许多能修改这些状态的函数,程序则由不断修改状态构成:函数式编程则极力避免状态改动,并通过在函数间传递数据流进行工作。但这并不是说无法同时使用函数式编程和面向对象编程,事实上,复杂的系统一般会采用面向对象技术建模,但混合使用函数风格还能让你额外享受函数式风格的优点。

【有边界效应的函数(不可重入的函数)】
使用了全局变量,静态变量,或者处理的数据不全是来自函数的输入(比如来自硬件的)。

#Example_1
y=200
def myadd(x):
	return x+y

print myadd(10) #210
y=300
print myadd(10) #310

【纯函数(可重入函数)】
类似数学函数的映射,给定输入,就会得到指定输出,没有任何副作用。

#Example_2
def add2(x, y):
	return x+y

2. 函数式编程的好处

-逻辑可证
这是一个学术上的优点:没有边界效应使得更容易从逻辑上证明程序是正确的(而不是通过测试)。
-模块化
函数式编程推崇简单原则,一个函数只能做一件事情,将大的功能拆分成尽可能小的模块——小的函数更易于阅读和检查错误。
-组件化
小的函数更容易加以组合形成新的功能
-易于调试
细化的、定义清晰的函数使得调试更加简单。当程序不正常运行时,每一个函数都是检查数据是否正确的接口,能更快速地排除没有问题的代码,定位到出现问题的地方。
-易于测试
不依赖于系统状态的函数无需在测试前构造测试桩,使得编写单元测试更加容易。
-更高的生产率
函数式编程产生的代码比其他技术更少(往往是其他技术的一半左右),并且更容易阅读和维护。

2.1. 函数式编程的基础风格特征

【函数是一等公民】
函数能作为参数传递,或者是作为返回值返回。
【函数变量】
函数名是变量,它在创建函数时绑定一个函数对象。

#Example_3
def fn():
	print("hello world")
f1=fn
f1() # 等同于调用函数fn()

3. 高阶函数(Higer-Order-Function)

满足下列条件之一的函数即为高阶函数:
1、函数接受一个或多个函数作为参数传入
2、函数返回一个函数(类似于数学上复合函数的概念)

#Example_4
# 函数作为函数的返回值
def getfn():
	def print_hello():
		print("hello")
	return print_hello

fn = getfn()
fn()

4. 函数作为函数的参数传递

#Example_5
def tab(x, y):
	return "|" + x.center(13) + \
	"|" + y.center(13) + "|"

def string(x, y):
	return "姓名:" + x + "年龄:" + y

def myprint(fx, x, y): #fx为一个输入的参数
	s=fx(x, y)
	print(s)

myprint(tab, "Midu", "15")
myprint(tab, "小张", "18")

5. 偏函数

函数有很多参数时,如果固定住一些参数,可以利用functools模块Partial得到一个偏函数,方便以后的使用。

#Example_6
>>> int('12345')
>12345
>>> int('12345', base=8) #base为进制,不写则默认为十进制
>5349
>>> int('12345', 16)
>74565

#偏函数设定
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
>64
>>> int2('1010101')
>85

6. 迭代器&生成器–暂时略过,后续讲

【迭代器】
可用for进行循环变量的可迭代对象。

【生成器】
生成器(generator)是一种创建迭代器的方法。
例如:range

7. 封装控制结构的内部模版函数

【Python中内置(builtins)的高阶函数】
map, filter, sorted

7.1.【map函数】

map(func, *iterate)

用函数func和可迭代对象iterable中每个元素作为参数计算出新的可迭代对象,当最短一个可迭代对象完成迭代后,此迭代生成结束。

#Example_7
def power2(x):
	return x**2
# 生成一个迭代器,此迭代器可以生成1-9的自然数平方
# 1 4 9 16 。。。
mit = map(power2, range(1, 10))
for x in mit:
	print(x, end=' ')

#Example_8
# 生成一个迭代器,此迭代器可以生成1*4, 2*3, 3*2, 4*1这样的数
def mymul(x, y):
	return x*y
mit = map(mymul, [1,2,3,4], [4,3,2,1])
[x for x in mit] #[4, 6, 6, 4]

pow(x, y, z =None) 

7.2.【reduce函数】

reduce(function, iterable[, initializer])

作用:
reduce() 函数会对参数序列中元素进行累积。
函数讲一个数据集合(链表,元组等)中的所有数据进行下列操作:用穿个reduce中的函数function(有两个参数)先对集合中的第1、2个元素进行操作,得到的结果再与第三个数据用function函数运算,最后得到一个结果。
说明:
参数解释:
function – 函数,有两个参数
iterable – 可迭代对象
initializer – 可选,初始参数

#Example_9
#注意import
#from functools import reduce
def add(x,y):
	return x+y

>>> reduce(add, [1,2,3,4,5]) #计算列表和:1+2+3+4+5
>15

7.3. 【filter函数】

filter(function or None, iterable)

作用:
筛选iterable中的数据,返回一个可迭代对象,此可迭代对象对iterable进行筛选。
说明:
function将对iterable中每一个元素进行求值,返回Fasle则将此数据丢弃,返回True,则保留此数据。

#Example_10
def is_odd(n):
	return n%2==1
newlist = filter(is_odd, [1,2,3,4,5,6,7,8,9,10])
print(newlist)

#Example_11
L=[x for x in range(10)] #[0,1,2,3,...9]
def isodd(x): #如果为奇数返回True
	return x % 2 ==1
L2 = [x for x in filter(isodd, range(10))]
print(L2)

7.4. 【sorted函数】

作用:
将原可迭代对象数据进行排序,生成排序后的列表
格式:

sorted(iterable, key=None, reverse=False)

说明:
key函数用来提供一个值,这个值将作为排序的依据。

#Example_11
L=[5, -2, -4, 0, 3, 1]
L2=sorted(L) #[-4, -2, 0, 1, 3, 5]
L2=sorted(L, reverse=True) #[5, 3, 1, ...]
L2=sorted(L, key=abs) #[0, 1, -2, 3, -4, 5]
names=['Tom', 'Jerry', 'Spike', 'Tyke']
sorted(names)
sorted(names, key=len)

7.5. 【lambda函数(匿名函数)】

作用:
创建一个匿名(无名)函数对象
同def类似,但不提供函数名
语法:

lambda [参数1, 参数2, …] :表达式 注:[]内的部分表示可以忽略

#Example_12
def myadd(x, y):
	return x+y
#可以改写为:
myadd = lambda x,y : x+y
print("20+30=", myadd(20,30))

8. 内置不可变数据结构

为了避开边界效应,不可变的数据结构是函数式编程中不可或缺的部分。不可变的数据结构保证数据的一致性,极大地降低了排查问题的难度。
例如,Python中的元组(tuple)就是不可变的,所有对元组的操作都不能改变元组的内容,所有试图修改元组内容的操作都会产生异常。
函数式编程语言一般会提供数据结构的两种版本(可变和不可变),并推荐使用不可变的版本。

8.1. 递归&递归函数

递归是一种取代循环的方法。
【递归函数recursion】
函数直接或间接地调用自身。

#Example_13
def f():
	print("hello")
	f()

f()  #调用函数
print("递归完成")

以计算阶乘为例,看循环和递归的不同实现。

#Example_14
#循环实现
def myfac(n):
	if n==0:
		return 1
	r=1
	for x in range(1, n+1):
		r *= x
	return r
print("5的阶乘是:", myfac(5))

#用递归实现:
def myfac(n):
	if n ==0:
		return 1
	if n ==1:
		return 1
	return n*myfac(n-1)
print("5!=", myfac(5))

8.2. 闭包closure

将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象称为闭包。

说明:
如果一个内嵌函数访问函数外部作用域的变量,则这个函数就是闭包。

#Example_15
def make_power(x):
	def fn(arg):
		return arg ** x
	return fn

f2 = make_power(2)
# (make_power(2))(100)

y=f2(100) #10000

f3 = make_power(3)
f3(3) #27

f4 = make_power(4)
f4(2) #16

8.3. 装饰器 decorator

装饰器是一个函数,主要作用是用来包装另一个函数或类

包装的目的是在不改变原函数名的情况下,改变被包装函数(对象)的行为

8.3.1【装饰器函数】

def 装饰器函数名(参数): 函数快 return 函数

#Example_16
def deco(fn):
	def _deco():
		print("装饰器函数被调用,并返回原函数 ")
		fn()
	return _deco()

#以下的等同于上面的
# def deco(fn):
#	def _deco():
#		print("装饰器函数被调用,并返回原函数 ")
#		fn()
#	return _deco()
@deco
def myfunc():
	print("函数myfunc被调用!")
# 以上@deco等同于此语句
# myfunc = deco(myfunc)
# 一次调用打两行print

# 执行
myfunc()

在这里插入图片描述

8.3.2.【被装饰函数带有参数的装饰器 】

#Example_17
def msg_service(fn):
	def savemoney2(name, x):
		print("欢迎", name, "来北京银行,请取号!!!")
		fn(name, x)
		print(name, "办理了存", x, "元钱的业务,短信发送中。。。")
	return savemoney2

@msg_service
def savemoney(name, x):
	print(name, "存钱", x, "元")

savemoney("张三", 200)
savemoney("李四", 500)

!!!和闭包使用过程很相似,注意联系起来!!!

8.3.3.【被装饰函数带有不定参数的装饰器】

#Example_18
def deco(func):
	def _deco(*args, **kwargs):
		print("before %s called." % func.__name__)
		ret=func(*args, **kwargs)
		print("  after %s called. result: %s" % (func.__name__, ret))
		return ret
	return _deco

@deco
def myfunc(a, b):
	print(" myfunc(%s,%s) called." % (a, b))
	return a+b 

@deco
def myfunc2(a, b, c):
	print(" myfunc(%s,%s,%s) called." % (a, b, c))
	return a+b+c

myfunc(1,2)
myfunc(3,4)
myfunc2(1,2,3)
myfunc2(3,4,5)

8.3.4. 【带参数的装饰器函数语法】

@ 装饰器函数名[(装饰器函数传参)]<换行> def 函数名(参数列表): 语句块

#Example_19
def deco(arg):
	def _deco(func):
		def __deco():
			print("before %s called [%s]." % (func.__name__, arg))
			func()
			print("  after %s called [%s]." % (func.__name__, arg))
		return __deco
	return _deco

@deco("mymodule")
def myfunc():
	print(" myfunc() called.")

@deco("module2")
def myfunc2():
	print("	myfunc2() called.")

myfunc()
myfunc2()

9. 函数的属性

@例1

name 属性

作用:用来记录函数名
说明:
以双下划线开头,以双下划线结尾的标识符通常代表python的特殊属性等。
@例2

doc 属性

用于记录文档字符

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值