1.定义函数

函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。

任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。

函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。

函数内容以冒号起始,并且缩进。

return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

可以返回多个值,其实就是一个tuple。

defmy_abs(x):if x >=0:returnxelse:return -x

空函数:如果想定义一个什么事也不做的空函数,可以用来占位,可以用pass语句:

defnop():pass

2.参数传递

可更改(mutable)与不可更改(immutable)对象

在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。

不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变a的值,相当于新生成了a。

可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

python 函数的参数传递:

不可变类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。

可变类型:类似 c++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响

python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

3.参数

调用函数时可使用的正式参数类型:必需参数,关键字参数,默认参数,不定长参数等,参数类型也可以组合。

a. 必须参数,也叫位置参数

必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。

b.关键字参数

关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。

使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。

defprintinfo( name, age ):"打印任何传入的字符串"
print ("名字:", name)print ("年龄:", age)return
#调用printinfo函数
printinfo( age=50, name="runoob" )

c.默认参数

调用函数时,如果没有传递参数,则会使用默认参数。默认参数要放在最后。默认参数必须指向不可变对象。

def printinfo( name, age = 35):"打印任何传入的字符串"
print ("名字:", name)print ("年龄:", age)return
#调用printinfo函数
printinfo( age=50, name="runoob")print ("------------------------")
printinfo( name="runoob" )

d.不定长参数,可变参数,收集参数

需要一个函数能处理比当初声明时更多的参数。

加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。

果在函数调用时没有指定参数,它就是一个空元组。我们也可以不向函数传递未命名的变量。

如果* 后,还有参数,参数必须用关键字传入。

加了两个星号 ** 的参数会以字典的形式导入。允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

def printinfo( arg1, *vartuple ):"打印任何传入的参数"
print ("输出:")print(arg1)for var invartuple:print(var)return
#调用printinfo 函数
printinfo( 10)
printinfo(70, 60, 50 )
>>> nums = [1, 2, 3]
>>> calc(*nums)
14
def fun(**kwargs):
for x, y in kwargs.items():
print(x,y)
fun(city = "asdasd",beijing = "sadasdw")

4.匿名函数

python 使用 lambda 来创建匿名函数。

所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。

lambda 只是一个表达式,函数体比 def 简单很多。

lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。

lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。

虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

sum = lambda arg1, arg2: arg1 + arg2
two_sum= (lambda x, y: x+ y)(3,4)

匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:

>> f = lambda x: x *x>>>f at 0x101c6ef28>
>>> f(5)25

也可以把匿名函数作为返回值返回,

defbuild(x, y):return lambda: x * x + y * y

5.高阶函数

一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

defadd(x, y, f):return f(x) +f(y)defzheng(n):if n >0:returnnelse:return -n
x= -5y= 10f=zheng #变量指向函数,函数名就是变量print(add(x, y, f))

5.1 map()和reduce()函数

map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

>>> deff(x):
...return x *x
...>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])>>>list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25

作业:

利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:

摘选了一个答案:

defstr2float(str):
d= {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4,'5': 5, '6': 6, '7': 7, '8': 8, '9': 9}defs2n(s):returnd[s]deffn(a, b):return reduce(lambda x, y: x * 10 + y, map(s2n, a)) + reduce(lambda x, y: x * 10 + y, map(s2n, b)) * (10 ** -len(b))
theS= str.split('.')return fn(theS[0], theS[1])

5.2 filter

Python内建的filter()函数用于过滤序列。

和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

defis_odd(n):return n % 2 == 1list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))#结果: [1, 5, 9, 15]

用filter求素数

def_odd_iter(): #构造一个从3开始的奇数序列,这是一个生成器,并且是一个无限序列。
n= 1
whileTrue:
n= n + 2
yieldndef_not_divisible(n): #定义一个筛选函数return lambda x: x % n >0defprimes(): #定义一个生成器,不断返回下一个素数yield 2it= _odd_iter() #初始序列
whileTrue:
n= next(it) #返回序列的第一个数 #每次循环,it会改变,n取新序列的第一个数。
yieldn
it= filter(_not_divisible(n), it) #构造新序列 #循环一次,就将n及n倍数的值去掉返回新的Iterator
#将lambda函数写在filter中,筛选有问题,没搞懂是怎么回事,应该是闭包的问题。#打印1000以内的素数:
for n inprimes():if n < 1000:print(n)else:break

5.3 stored

sorted()函数也是一个高阶函数,可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

先将序列输入key函数,获取新序列,然后再排序。

>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True) #反向排序
['Zoo', 'Credit', 'bob', 'about']

key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。

序列中的元素是一个个传入key函数,再返回一个新序列。

max,min
salaries={'siry':3000,'tom':7000,'lili':10000,'jack':2000}#需求1:找出薪资最高的那个人=》lili#res=max([3,200,11,300,399])#print(res)
#res=max(salaries)#print(res)
salaries={'siry':3000,'tom':7000,'lili':10000,'jack':2000}#迭代出的内容 比较的值#'siry' 3000#'tom' 7000#'lili' 10000#'jack' 2000
#def func(k):#return salaries[k]
#========================max的应用#res=max(salaries,key=func) # 返回值=func('siry')#print(res)
#res=max(salaries,key=lambda k:salaries[k])#print(res)
#========================min的应用#res=min(salaries,key=lambda k:salaries[k])#print(res)

5.4 函数作为返回值

闭包

在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包

函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包

#形成闭包的条件

#1、必须要有一个内嵌函数

#2、内嵌函数中要对外层函数变量的引用

#3、外部函数必须返回内嵌函数

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

defcount():deff(j):defg():return j*jreturng
fs=[]for i in range(1, 4):
fs.append(f(i))#f(i)立刻被执行,因此i的当前值被传入f()
return fs

5.5 装饰器

可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)

要先理解闭包。

def outer(func): #装饰函数
definner():print("认证成功!")
result=func()print("日志添加成功")returnresultreturninner
@outerdef f1(): #被装饰函数
print("业务部门1数据接口......")

a.程序开始运行,从上到下开始解释。读到def outer(func)时,发现这是一个函数的定义,将其函数体放入内存中,然后跳过。

b.跳到@outer时,程序被@这个python语法糖吸引住,知道这是个装饰器,按规矩要立即执行,于是程序开始运行@后面那个名字outer所定义的函数。

c.程序返回到outer函数,开始执行装饰器的语法规则。规则是:被装饰函数的名字会被当作函数形参传递给装饰函数。装饰函数执行它自己内部的代码后,会将它的返回值赋值给被装饰函数。原来f1函数被当作参数传递给了func,而f1这个函数名之后会指向inner函数。 注意:@outer和outer()是有区别的。没有括号时,outer函数会被立即执行。这个与传统的用括号才能调用函数不同。

d.程序开始执行outer函数内部的内容,一开始它又碰到了一个函数inner,inner函数定义块被程序观察到后不会立刻执行,而是读入内存中(这是默认规则)。

e.再往下,碰到return inner,返回值是个函数名,并且这个函数名会被赋值给f1这个被装饰函数,也就是f1 = inner。此时,f1函数被新的函数inner覆盖了(实际上是f1这个函数名更改成指向inner这个函数名指向的函数体内存地址,f1不再指向它原来的函数体的内存地址),再往后调用f1的时候将执行inner函数内的代码,而不是先前的函数体。那么先前的函数体去哪了?还记得我们将f1当做参数传递给func这个形参么?func这个变量保存了老的函数在内存中的地址,通过它就可以执行老的函数体,你能在inner函数里看到result = func()这句代码,它就是这么干的!

f.接下来,还没有结束。当业务部门,依然通过f1()的方式调用f1函数时,执行的就不再是旧的f1函数的代码,而是inner函数的代码。在本例中,它首先会打印个“认证成功”的提示,很显然你可以换成任意的代码,这只是个示例;然后,它会执行func函数并将返回值赋值给变量result,这个func函数就是旧的f1函数;接着,它又打印了“日志添加成功”的提示,这也只是个示例,可以换成任何你想要的;最后返回result这个变量。我们在业务部门的代码上可以用r = f1()的方式接收result的值。

g.以上流程走完后,你应该看出来了,在没有对业务部门的代码和接口调用方式做任何修改的同时,也没有对基础平台部原有的代码做内部修改,仅仅是添加了一个装饰函数,就实现了我们的需求,在函数调用前进行认证,调用后写入日志。这就是装饰器的最大作用。

f1._name_ 输出inner。Python内置functools.wraps(),可以解决它们的__name__已经从原来的'f1'变成了'inner'的问题
importfunctoolsdef log(text): ###now = log('execute')(now)
defdecorator(func):
@functools.wraps(func)def wrapper(*args, **kw):print('%s %s():' % (text, func.__name__))return func(*args, **kw)returnwrapperreturndecorator
@log('execute') ###decorator本身需要传入参数
defnow():print('2015-3-25')
#带参数
defouter(func):def inner(*args, *kwargs):print("认证成功")
result= func(*args, **kwargs)print("日志添加成功")returnresultreturninner
@outerdeff1(name, age):print("{}正在调用业务部门1的数据接口".format(name))#调用方法
f1("jack", 19)
#有多个装饰器
defouter1(func):def inner(*args,**kwargs):print("认证成功!")
result= func(*args,**kwargs)print("日志添加成功")returnresultreturninnerdefouter2(func):def inner(*args,**kwargs):print("一条欢迎信息。。。")
result= func(*args,**kwargs)print("一条欢送信息。。。")returnresultreturninner
@outer1
@outer2deff1(name,age):print("%s 正在连接业务部门1数据接口......"%name)#调用方法
f1("jack",18)
###装饰器中形参是函数
#认证函数
defauth(request,kargs):print("认证成功!")#日志函数
deflog(request,kargs):print("日志添加成功")#装饰器函数。接收两个参数,这两个参数应该是某个函数的名字。
defFilter(auth_func,log_func):#第一层封装,f1函数实际上被传递给了main_fuc这个参数
defouter(main_func):#第二层封装,auth和log函数的参数值被传递到了这里
defwrapper(request,kargs):#下面代码的判断逻辑不重要,重要的是参数的引用和返回值
before_result =auth(request,kargs)if(before_result !=None):returnbefore_result;
main_result=main_func(request,kargs)if(main_result !=None):returnmain_result;
after_result=log(request,kargs)if(after_result !=None):returnafter_result;returnwrapperreturnouter#注意了,这里的装饰器函数有参数哦,它的意思是先执行filter函数。然后将filter函数的返回值作为装饰器函数的名字返回到这里。所以,Filter(auth,log) = outer , @Filter(auth,log) = @outer
@Filter(auth,log)deff1(name,age):print("%s 正在连接业务部门1数据接口......"%name)#调用方法
f1("jack",18)

5.6 偏函数

functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数。

>>> importfunctools>>> int2 = functools.partial(int, base=2)>>> int2('1000000')64
>>> int2('1010101')85

固定参数,返回新的函数。