1.函数的定义
使用def关键字和缩进语法来定义一个函数:
def funcname(args):
<code statement>
2.函数的参数
参数类型 | 描述 |
位置参数 (必须参数) | 如果不输入参数名称,则必须按照指定顺序输入参数;若输出参数名称,则可以不按照指定顺序输入参数 |
默认参数 | 在设置参数时已设定默认值,故输入参数时可忽略,但若要重新配置该参数值,则按照位置参数规则执行 |
可变参数 | 使用*,则连续输入多个的个体参数,会被自动封装为tuple,但输入类容器结构数据时,在前类容器前加*进行拆包 |
关键字参数 | 使用**,可以输入任意的key=value键值对形式的参数,会自动封装为dict,但若输入dict为参数,则需要在dict加**进行拆包 |
def func(name,age,weight=65):
print('%s is %s years old and his weight is %s kiligrams'%(name,age,weight))
func('grant',18)
func('lisa',20,50)
func(age=30,name='peter')
func(weight=100,age=60,name='wood')
#以上都能正常输出
def func2(*L):#定义一个可变长的函数
for i in L:
print(i)
func2(1,2,3)
List=[4,5,6]
func2(*List)#在列表前加*拆包
#以上都能正常输出
def func3(**kw):#定义一个关键字式函数
print(kw)
func3(name='grant',age=18)
func3(**{'name':'grant','age':18})#在字典前加**拆包
#以上都能正常输出
3. 作用域
Python中,变量的作用域一共分四种:
- 局部-Local
- 闭包函数的外层函数-Enclosing
- 全局-Global
- 内建-Built-in
Python解释器按照L-E-G-B的原则进行变量查找
变量类型 | 描述 |
不可变:int,str,tuple | 内层函数仅对变量有查看权,无修改权,除非使用global,nonlocal 进行作用域声明 |
可变:list,set,dict | 内层函数可以对变量进行查看和修改 |
4.匿名函数
使用lambda关键字来创建匿名函数
匿名函数只能是一行的表达式,所以一般只能封装比较简单的逻辑,但好处是因为没有函数名不用担心名称冲突,而且执行完毕既释放内存空间.
lambda x:x**2
5.递归函数
一个函数在内部调用其自身被称为递归函数,所有的递归函数都可以改写为循环.
由于函数调用都是通过栈的方式实现的,而栈的大小是有限的,所有当递归次数过多时,会导致栈溢出,从而抛出错误
RuntimeError: maximum recursion depth exceeded in comparison
使用递归函数的原则:
- 每一次的递归都能使问题规模减小
- 必须设置递归的退出条件
def recursion(n):
n=n-2
print(n)
if n<1:
print('end')
else:
return recursion(n)
recursion(10)
输出结果:
8
6
4
2
0
end
6.yield和return
- return
函数使用return作为结果输出,若不指定输出结果,则默认返回None,同时return None也可以简写为return
函数一旦执行return语句,则整个函数执行结束
在不指定格式的前提下,若用return同时返回多个结果,则会默认把结果封装成一个tuple
def func(x,y,z):
return x,y,z
print(func(1,2,3))
>>>(1,2,3)
- yield 生成器
函数中一旦出现yield,则该函数不在是函数,而是生成器(generator).
def func(num):
for i in range(num):
yield i
t=func(5)#此时t为生成器对象
print(type(t))
#输出结果:
>>><class 'generator'>
>>>
生成器是惰性的,可以用list/tuple/set直接接收生成器函数的全部输出结果,也可以用for循环对生成器函数进行遍历.
t1=func(5)
print(list(t1))
t2=func(4)
print(set(t2))
t3=func(3)
print(tuple(t3))
t4=func(2)
for i in t4:
print (i,end='//')
------------------------------------
#输出结果:
------------------------------------
[0, 1, 2, 3, 4]
{0, 1, 2, 3}
(0, 1, 2)
0//1//
生成器需要被一个变量对象接收,如果每次重新去调用生成器函数,每次都会是生成器函数初始化的结果.
def func(num):
for i in range(num):
yield i
print(next(func(3)))
print(next(func(3)))
print(next(func(3)))
--------------------------------------
#输出结果:
>>>0
>>>0
>>>0
生成器好比单行道,只能向前不能后退,直到抛出StopIteration的异常.
t1=func(3)
print(next(t1),end='/')
print(next(t1),end='/')
print(next(t1))
print(next(t1))
---------------------------------------
#输出结果:
0/1/2
Traceback (most recent call last):
File "D:/Pycharm/TEST/0410.py", line 14, in <module>
print(next(t1))
StopIteration
yield 实现协程
def consumer():
r1 = '111'
r2 = '222'
while True:
n1 = yield r1
print('n1:',n1)
if n1:
print('None')
print('[CONSUMER] Consuming %s...' % n1)
n2 = yield r2
print('n2:',n2)
r = '200 OK'
def produce(c):
k=c.send(None)
print('k:',k)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
t = c.send(n)
print('t:',t)
print('[PRODUCER] Consumer return: %s' % t)
c.close()
c = consumer()
produce(c)
-------------------------------------------------------------------------------
#执行结果:
k: 111
[PRODUCER] Producing 1...
n1: 1
None
[CONSUMER] Consuming 1...
t: 222
[PRODUCER] Consumer return: 222
[PRODUCER] Producing 2...
n2: 2
t: 111
[PRODUCER] Consumer return: 111
[PRODUCER] Producing 3...
n1: 3
None
[CONSUMER] Consuming 3...
t: 222
[PRODUCER] Consumer return: 222
[PRODUCER] Producing 4...
n2: 4
t: 111
[PRODUCER] Consumer return: 111
[PRODUCER] Producing 5...
n1: 5
None
[CONSUMER] Consuming 5...
t: 222
[PRODUCER] Consumer return: 222
以上为典型的生产者消费者模型
在该模型中, yield的原则为左接收(先),右发生(后),而send是先发送,后接收
第一步,使用send(None)命令激活(只能使用该固定格式),和该命令对应的yield执行右发生
当第二次及以后的send时,yield执行左接收后,执行其他代码,直到遇到下一个yield执行右发送,从而完成一次循环
当yield完成一次循环后,send接收yield的发送值并执行其他代码,直到下一次send
最后,使用close()命令,关闭整个流程
yield和return的位置关系
若yield之后还存在return关键词,则return之后携带的内容只会出现在生成器函数抛出异常时的StopIteration中:
def func():
yield 1
yield 2
return 'end'
t=func()
print(next(t))
print(next(t))
print(next(t))
---------------------------------------
#输出结果:
1
2
Traceback (most recent call last):
File "D:/Pycharm/TEST/0410.py", line 11, in <module>
print(next(t))
StopIteration: end
7.闭包函数
闭包函数本质上就是函数内部再嵌套函数,并且内层函数需要调用外出函数的变量,最后内层函数作为结果输出.
闭包函数输出的是一个函数,所以要拿到最终的结果,需要对输出的函数再次进行调用.
闭包函数的使用最需要注意以下两点:
- 内外层函数变量的作用域问题,切记内层函数对外出函数的变量只有查看权,无修改权
- 由于闭包函数输出的是一个还未被执行的函数,只在在该函数最终执行时才会去查找调用相关变量,所以这个过程中的变量需要时稳定的,若为不稳定的变量则会影响输出结果.
def out():
count=0#此为不可变类型,故内层函数要修改的话需要声明,用[0]代替则不需要声明
def inner():
nonlocal count
count+=1
return count
return inner
t=out()
print(t(),t(),t())
---------------------------------------
#输出结果:
>>>1 2 3
def out(n):
f=[]
for i in range(1,n):
def inner():
return i*i
f.append(inner)
return f
f1,f2,f3=out(4)
print(f1(),f2(),f3())
---------------------------------------
#输出结果:
>>>9 9 9 #由于闭包返回的函数在没有在调用时并没有执行,所以在最后执行再去查找变量时,变量i已经是3,所以输出结果都是9,并不是1,4,9!
def out(n):
f=[]
for i in range(1,n):
def inner(x=i):
return x*x
f.append(inner)
return f
f1,f2,f3=out(4)
print(f1(),f2(),f3())
---------------------------------------
#输出结果:
>>>1 4 9 #由于在内层函数每次都用参数x绑定了变量i,所以最终输出结果为1,4,9
8.装饰器
装饰器类似于闭包,本质上都是用函数是'包装'函数.
def dec(func):
def wrapper(*args,**kwargs):
print('This is a decorator')
func(*args,**kwargs)
return wrapper
def func1():
print('run func1')
func1()
print('=======================================')
#用函数dec修饰func1后
func1=dec(func1)
func1()
---------------------------------------
#输出结果:
run func1
=======================================
This is a decorator
run func1
为了简洁性和代码的复用性,Python可以采用@语法糖的形式,@dec的语法是等同于func2=dec(func2)
@dec
def func2():
print('func2 is running')
func2()
-----------------------------------------
#输出结果:
This is a decorator
func2 is running
从形式上来说,确实达到了在不改动原代码的前提下,为原函数增加的新的功能,但其实原函数的元信息已经改变
def func2():
print('func2 is running')
print(func2.__name__)
@dec
def func2():
print('func2 is running')
print(func2.__name__)
---------------------------------------
#输出结果:
func2
wrapper
但Python 用functools模块中的wraps方法解决了这个问题,这才是真正的装饰器
import functools
def dec(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
print('This is a decorator')
func(*args,**kwargs)
return wrapper
def func2():
print('func2 is running')
print(func2.__name__)
@dec
def func2():
print('func2 is running')
print(func2.__name__)
---------------------------------------
#输出结果:
func2
func2
以上是不带参数的装饰器,若需要增加参数的话,就得再增加一层包含的层级
import functools
def dec(text):
print(text)
def wrap(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
print('decorator with parameter')
return func(*args,**kwargs)
return wrapper
return wrap
@dec('parameter')
def func():
print('func is running')
func()
---------------------------------------
#输出结果:
parameter
decorator with parameter
func is running
装饰器也是可以多层嵌套的:等同于a装饰(b装饰(c装饰func)),既 func=a(b(c(func)))
@a
@b
@c
def func():
pass
在执行函数前打印start,执行函数后打印end的装饰器例子:
import functools
def dec(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
print('start')
func(*args,**kwargs)
print('end')
return wrapper
@dec
def func():
print('func is running!')
func()
---------------------------------------
#输出结果:
start
func is running!
end