函数¶
# 关键字def用于声明函数
# 函数在被调用前都是不存在的
def func_name(param1, param2):
pass
# 返回值
# 可以参略,省略则默认返回None
# 也同时返回多个,多个则以元组形式返回
return param1, param2
# 调用函数
func(1,2)
参数类型¶
首先要区分形参和实参,形参就是定义函数时候的参数,实参是调用函数时传入的参数。
# x,y,z都是形参
def f1(x,y,z):
pass
# 参数可以传递给内部函数
def f2(x):
pass
# 1,2,3是实参
func(1,2,3)
形参可分为固定的和动态的两种。
固定参数¶
固定形参的位置/顺序和个数都是确定的,必须要一对一的传入实参,所以也被叫做位置/顺序/必传参数。
# x,y,z都是固定的
def func(x,y,z):
pass
# 实参与形参的位置和个数都要一一对应
func(1,2,3)
# 如果以关键字方式传入,位置可以随意
func(y=2,z=3,x=1)
# 可以给固定参数设置默认值
# 必须要位于没有默认值参数的后面
# 如果有多个时,建议将不易变的置后
# 且建议不要设置可变的默认值
def func(x,y,z=3):
pass
# 设了默认值的形参,可不传实参
func(1,2)
# 设了默认值的形参,可传入其它实参覆盖默认值
func(1,2,8)
# 可以让固定参数强制以关键字的方式传入,前面加一个:*,
def func(x,y,*,z=3):
pass
# 必须以关键字的方式给参数z传值
func(1,2,z=8)
动态参数¶
动态参数的个数是不固定的,位置也随个数的变化而不确定。
有两种接受实参的形式:
*args
¶
# 以tuple的形式接受实参
def func(*args):
pass
# 可以传入任意个数的实参,会被以元组的形式传入
func(1,2,3) # args=(1,2,3)
# 还可以通过解包的方式传入任意可迭代对象
func(*X)
_str="123" # args=('1', '2', '3')
_tuple=(1,2,3) # args=(1, 2, 3)
_list=[1,2,3] # args=(1, 2, 3)
_set={1,2,3} # args=(1, 2, 3)
_dict={"a":1, "b":2, "c":3} # args=('a', 'b', 'c')
**kwargs
¶
# 以dict的形式接受实参
def func(**kwargs):
pass
# 可以用关键字的方式传入
func(a=1,b=2,c=3) # kwargs={'a': 1, 'b': 2, 'c': 3}
# 还可以通过解包字典的方式传入
_dict={"a":1, "b":2, "c":3}
func(**_dict) # kwargs={'a': 1, 'b': 2, 'c': 3}
参数混合使用¶
动态参数必须要在固定参数后面
# *args后的固定参数,都被视作关键字参数,且可以省略*号
def func(x,*args,z=0):
pass
func(1,2,3,4,z=8)
# **kwargs必须在*args后面
# **kwargs之后不允许再设置任何参数
def func(x,*args,**kwargs):
return x, args, kwargs
func(1,2,3) # (1, (2, 3), {})
func(1,2,3,a=4,b=5) # (1, (2, 3), {'a': 4, 'b': 5})
参数的多态性¶
由于Python函数的参数没有类型限制,所实际使用中可能会带来一些问题,所以必要时建议在函数开头加上数据类型检查
def func():
if not isinstance(args, args_type):
pass
return
pass
作用域¶
局部变量¶
函数内定义的变量默认都是局部变量,在函数外部无法访问函数内部变量和函数
# 全局变量
a = 1
def f1():
# 局部变量
a = 2 # 可以和全局变量名字相同,但不建议这样
print(a) # 1,全局变量没有被重新赋值
全局变量¶
函数内部可以访问全局变量
a = 1
def f1():
print(a) # a=1
x = a
return x # x=1
但函数内无法对全局变量重新赋值
# 全局变量
a = 1
b = {}
def f1():
a = a + 1 # 定义局部变量a,"a" is unbound,然后+1赋值给自己,这样是有语法错误的
# 函数参数的传递是赋值传递,即形参copy了实参的值,并不会改变指向实参的变量
def f2(a):
a = a + 1 # 定义局部变量a,形参a+1,赋值给局部变量a,没问题
def f3():
b["k1"] = "v1" # dict是可变的,增加元素不算重新赋值
函数内如果想改变全局变量,需要使用global
关键字
# 全局变量
a = 1
def f1():
global a # 这里声明了a是全局变量a
a = a + 1 # 这样就没问题了
嵌套的内层函数如果想改变外层函数的变量,需要使用nonlocal
关键字
# 全局变量
a = 1
def f1():
# 局部变量
a = 2
def f2():
nonlocal a # 这里声明了a是外层函数a,即局部变量a
a = a + 1
r = f2()
return r # a=3
类型注解¶
通过类型注解可以提高代码的可读性和易用性
"""
变量类型注解:用 :type 表示
函数返回值类型注解:用 ->type 表示
"""
def func(x:int, y:int) -> int:
"""
:x:xxx
:y:xxx
"""
return x + y
对于复杂的数据结构需要借助 typing 模块来表达这些数据结构
from typing import List, Tuple, Dict
names: List[str] = ['lily', 'tom']
version: Tuple[int, int, int] = (6, 6, 6)
operations: Dict[str, bool] = {'sad': False, 'happy': True}
匿名函数(lambda表达式)¶
f = lambda 输入 : 输出
f = lambda x:x+1
f(2) # 3
# 等价于
def f(x):
return x+1
f(2) # 3
# 也可以直接写成
(lambda x:x+1)(2) # 3
# 没有参数时
f = lambda :2 # f() 返回2,可以用来返回另一个函数等
函数也是对象¶
函数式编程:
函数式编程是一种抽象程度很高的编程范式(如何编写程序的方法论),属于"结构化编程"的一种,鼻祖语言:Lisp。
纯粹的函数式编程语言编写的函数是没有变量的,而Python允许使用变量,所以Python不是纯函数式编程语言,只是部分支持。
参考:函数式编程初探·廖雪峰
函数可以赋值给变量¶
def f1(x):
print(x)
# 引用,而没有直接调用,有点像类的实例化
r = f1
r('hello world')
函数的内部可以是函数¶
函数内可以嵌套函数,使用嵌套函数,能保证数据的隐私性(不能被外界直接调用),提高程序运行效率。
# 外层函数
def print_msg():
msg = "zen of python"
# 嵌套函数
def printer():
print(msg)
printer() # 调用内部函数
another = print_msg() # 直接输出msg
递归函数¶
如果嵌套的是自己,则称之为递归函数。
不同性能的计算机允许的最大递归次数也不同。过深的调用会导致栈溢出。
函数的返回可以是函数¶
函数可以返回一个外部的函数,也可以返回一个内部的函数。
闭包¶
如果返回了内部的函数,且内部函数使用了外部函数的变量,那么称之为闭包。相当于间接实现了从外部访问函数的局部变量
可以简化程序的复杂度,提高可读性。如果类只有一个方法时,可以考虑使用闭包替代。
# 外层函数
def func_wai():
msg = "hello wold"
# 内层函数
def func_nei():
# 使用了外部函数的变量,形成闭包
print(msg)
# 返回内部函数,不要加()
return func_nei
r = func_wai()
r()
函数的参数可以是函数¶
map(f, Iterable)
用法:将可迭代对象中的每个元素,都作为参数传入函数执行,最后返回一个新的可迭代对象。
_list = [1,2,3]
r = map(lambda x:x**2, _list) # 返回一个可迭代对象:<map object at 0x032A0DD0>
print(list(r)) # [1, 4, 9]
filter(f, Iterable)
用法:可迭代对象中的每个元素,经过函数判断,返回True或False,最后将返回True的元素组成一个新的可迭代对象。
_list = [1, 2, 3, 4, 5]
def f(x):
return x % 2 == 0
r = filter(f, _list) # 返回一个可迭代对象:<filter object at 0x02FE0DD0>
print(list(r)) # [2, 4]
reduce(f, Iterable)
用法:将一个集合传入函数做一些累积操作。
# 需要先引入
from functools import reduce
# 集合的元素个数没有要求,可以是1个
_str = "123" # "1234"
_list = [1,2,3] # 6
_tupe = (1,2,3) # 6
_set = {1,2,3} # 6
_dict = {"a":1, "b":2} # "ab"
r = reduce(lambda x,y:x+y, _list) # 直接返回结果
装饰器 decorator¶
装饰器是一种语法糖,可以使代码松耦合,能够在不修改原有业务逻辑的情况下对代码进行扩展。
一个函数前面可以加若干装饰器,类也有装饰器
# 装饰器函数,参数,内容,返回都是函数
def decorator(func):
# 闭包
def wrapper(*args, **kwargs):
print("前置扩展内容,调用装饰器,实现一些权限校验、用户认证等功能")
demo(*args, **kwargs) # 使用装饰器的函数被传入到这里,使用可变参数让其更加灵活
print("后置扩展内容,调用装饰器,实现一些日志记录、性能测试、事务处理、缓存等")
return wrapper
# 调用装饰器
"""最本质的方式"""
def demo(x):
pass
decorator(demo_func)(x)
"""语法糖"""
@decorator
def demo(x):
pass
demo(x)
迭代器¶
构建方式1:iter(Iterable)¶
iter("123") # <str_iterator object at 0x10de4aa10>
iter([1,2,3]) # <list_iterator object at 0x10de4aa10>
iter((1,2,3)) # <tuple_iterator object at 0x10de4a950>
iter({1,2,3}) # <set_iterator object at 0x10de4bf50>
iter({"a":1,"b":2}) # <dict_keyiterator object at 0x10de469b0>
构建方式2:生成器¶
生成器是一种特殊的迭代器,但是不需要像迭代器一样实现__iter__()和__next__()方法
- 构建方式1:列表推导式的[]换成()
g = (x for x in range(5))
- 构建方式2:函数的return换成yield
def fib(max):
n = 0
a,b = 0,1
while n < max:
if (counter > n):
return # 函数执行完return语句就结束了
yield b # 但函数执行完yield语句只是暂停
# 执行到此暂停,对象存储到迭代器数据流中
# 调用一次next(),将生成器对象返回,然后继续执行后面的代码,循环到此,再一次暂停
a, b = b, a + b
n = n + 1
g = fib(5) # <generator object demo at 0x100d55a50>
next(g) # 获取生成器对象的值,['...']
# 消费者
def consumer():
message = "start"
while True:
receiver = yield message # yield语句执行完,返回message值,然后暂停,可以接收send发送的消息,下一轮启动然后赋值给receiver
print("receiver: ", receiver) # 等待下一次启动才会执行赋值语句
if not receiver:
return "end"
message = "200 OK" # 执行完这里,会继续返回执行 yield message,也就是说此函数每一轮返回的都是message的值
if __name__ == '__main__':
gen = consumer() # <generator object consumer at 0x100d55a50>
print(next(gen)) # 预激
"""
在一个生成器函数未启动之前,是不能传递值进去
也就是说在使用gen.send(x)之前,必须先使用 next(gen) 或者 gen.send(None) 来返回生成器的第一个值
send() 类似 next(),但可以传递参数,发送消息:gen.send("message")
"""
# 模拟生产者
print(gen.send(1)) # 200 OK
print(gen.send(2)) # 200 OK
print(gen.send(3)) # 200 OK
print(gen.send(None)) # StopIteration: end
调用方式¶
# 每调用一次,则迭代一次,无可迭代最后会抛出StopIteration错误
r = next(g)
# 遍历
r = [i for i in g]
list(gen) # 也可以将生成器转为list