Python3 函数
1.定义函数
函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
函数内容以冒号起始,并且缩进。
return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
可以返回多个值,其实就是一个tuple。
def my_abs(x): if x >= 0: return x else: return -x
空函数:如果想定义一个什么事也不做的空函数,可以用来占位,可以用pass语句:
def nop(): 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 解释器能够用参数名匹配参数值。
def printinfo( 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 in vartuple: 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
请参考:https://www.cnblogs.com/kaishirenshi/p/8611358.html
two_sum
=
(
lambda
x, y: x
+
y)(
3
,
4
)
匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:
>> f = lambda x: x * x >>> f <function <lambda> at 0x101c6ef28> >>> f(5) 25
也可以把匿名函数作为返回值返回,
def build(x, y): return lambda: x * x + y * y
5.高阶函数
一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
def add(x, y, f): return f(x) + f(y) def zheng(n): if n > 0: return n else: return -n x = -5 y = 10 f = zheng #变量指向函数,函数名就是变量 print(add(x, y, f))
5.1 map()
和reduce()
函数
map()
函数接收两个参数,一个是函数,一个是Iterable
,map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回。
>>> def f(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
:
摘选了一个答案:
def str2float(str): d = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} def s2n(s): return d[s] def fn(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
决定保留还是丢弃该元素。
def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 结果: [1, 5, 9, 15]
用filter求素数
def _odd_iter(): #构造一个从3
开始的奇数序列,这是一个生成器,并且是一个无限序列。 n = 1 while True: n = n + 2 yield n
def _not_divisible(n): #定义一个筛选函数 return lambda x: x % n > 0
def primes(): #定义一个生成器,不断返回下一个素数 yield 2 it = _odd_iter() # 初始序列 while True: n = next(it) # 返回序列的第一个数 #每次循环,it会改变,n取新序列的第一个数。 yield n it = filter(_not_divisible(n), it) # 构造新序列 #循环一次,就将n及n倍数的值去掉返回新的Iterator
#将lambda函数写在filter中,筛选有问题,没搞懂是怎么回事,应该是闭包的问题。
# 打印1000以内的素数: for n in primes(): 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 函数作为返回值
闭包
https://blog.csdn.net/qq_37616069/article/details/79646905
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
函数lazy_sum
中又定义了函数sum
,并且,内部函数sum
可以引用外部函数lazy_sum
的参数和局部变量,当lazy_sum
返回函数sum
时,相关参数和变量都保存在返回的函数中,这种称为“闭包
#形成闭包的条件
#1、必须要有一个内嵌函数
#2、内嵌函数中要对外层函数变量的引用
#3、外部函数必须返回内嵌函数
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count(): def f(j): def g(): return j*j return g fs = [] for i in range(1, 4): fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f() return fs
5.5 装饰器
可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)
要先理解闭包。
def outer(func): # 装饰函数 def inner(): print("认证成功!") result = func() print("日志添加成功") return result return inner @outer def 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'的问题
import functools def log(text): ###now = log('execute')(now) def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator @log('execute') ###decorator本身需要传入参数 def now(): print('2015-3-25')
#带参数 def outer(func): def inner(*args, *kwargs): print("认证成功") result = func(*args, **kwargs) print("日志添加成功") return result return inner @outer def f1(name, age): print("{}正在调用业务部门1的数据接口".format(name)) # 调用方法 f1("jack", 19)
#有多个装饰器 def outer1(func): def inner(*args,**kwargs): print("认证成功!") result = func(*args,**kwargs) print("日志添加成功") return result return inner def outer2(func): def inner(*args,**kwargs): print("一条欢迎信息。。。") result = func(*args,**kwargs) print("一条欢送信息。。。") return result return inner @outer1 @outer2 def f1(name,age): print("%s 正在连接业务部门1数据接口......"%name) # 调用方法 f1("jack",18)
###装饰器中形参是函数 # 认证函数 def auth(request,kargs): print("认证成功!") # 日志函数 def log(request,kargs): print("日志添加成功") # 装饰器函数。接收两个参数,这两个参数应该是某个函数的名字。 def Filter(auth_func,log_func): # 第一层封装,f1函数实际上被传递给了main_fuc这个参数 def outer(main_func): # 第二层封装,auth和log函数的参数值被传递到了这里 def wrapper(request,kargs): # 下面代码的判断逻辑不重要,重要的是参数的引用和返回值 before_result = auth(request,kargs) if(before_result != None): return before_result; main_result = main_func(request,kargs) if(main_result != None): return main_result; after_result = log(request,kargs) if(after_result != None): return after_result; return wrapper return outer # 注意了,这里的装饰器函数有参数哦,它的意思是先执行filter函数。然后将filter函数的返回值作为装饰器函数的名字返回到这里。所以,Filter(auth,log) = outer , @Filter(auth,log) = @outer @Filter(auth,log) def f1(name,age): print("%s 正在连接业务部门1数据接口......"%name) # 调用方法 f1("jack",18)
5.6 偏函数
functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数。
>>> import functools >>> int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85
固定参数,返回新的函数。
参考:菜鸟教程和廖雪峰Python3