定义与参数
基本概念
- 结构化编程对代码的最基本封装
- 封装是为了复用 减少代码冗余
- 函数可分为内建函数与库函数
定义与调用
def语句定义
def 函数名(参数列表):
函数体
[return 返回值]
- 函数名就是标识符
- 不写return语句会默认返回None
- 定义中的参数列表称为形参
调用
- 先定义 后调用
- 调用方法: 函数名加小括号 括号内写上参数
- 调用时写入的参数称为实参
- 函数是可调用对象 callable()
参数
位置参数
- 实参与形参通过位置相匹配 需保持数量一致
- 如 def f(x, y, z) f(1, 3, 5)
关键字参数
- 实参与形参通过关键字相匹配 可打乱顺序 需保持数量一致
- def f(x, y, z) f(y=3, z=5, x=1)
- 位置参数必须在关键字参数之前传入
因此 f(y=5, z=6, 2) 会报错
参数默认值
- 定义时 在形参变量后写入默认值
def add(x=4, y=5):
return x+y
- 形参有默认值 不必保持传参时数量一致
- 因为实参不够 会使用默认形参
- 用于参数较多时 简化用户输入与函数调用
def login(host='127.0.0.1',port='8080',username='python',password='******'):
print('{}:{}@{}/{}'.format(host, port, username, password))
login()#不写参数 全部使用默认参数
login(username='root')#加入关键字 指定某个参数 其余使用默认参数
login('localhost', port='80',password='com')#注意位置参数放在关键字参数前面
可变位置参数
def add(*nums):
sum = 0
print(type(nums))
for x in nums:
sum += x
print(sum)
add(3, 6, 9)
<class 'tuple'>
18
- 形参前加*为可变位置参数
- 收集多个实参为一个元组
可变关键字参数
def showconfig(**kwargs):
for k,v in kwargs.items():
print('{} = {}'.format(k, v))
showconfig(host='127.0.0.1',port='22',username='python',password='******')
host = 127.0.0.1
port = 22
username = python
password = ******
- 形参前加**为可变关键字参数
- 收集多个实参为一个字典
可变参数总结
- 可变位置参数在形参前加* 收集多个实参为一个元组
- 可变关键字参数在形参前加** 收集多个实参为一个字典
- 各类参数混合使用时 普通参数放最前 可变参数放最后
- 可变位置参数放前 可变关键字参数放后
错误例子:
def showconfig(username, password, **kwargs, *args)
以上两条可简单理解为(越)复杂参数(越)往后放
- 位置参数中已实用的变量 关键字参数中不可再次出现 否则会报multiple values的错误
错误例子:
def fn(x, y, *args, **kwargs):
pass
fn(7,9,y=5,x=3,a=1,b='python')
\#TypeError: fn() got multiple values for argument 'y'
Keyword-only参数
- 可变位置参数后加入的”位置参数”
def fn(*args, x, y, **kwargs):
print(x)
print(y)
print(args)
print(kwargs)
fn(7,9,y=5,x=3,a=1,b='python')
3
5
(7, 9)
{'a': 1, 'b': 'python'}
可简写为
def fn(*, x,y):
print(x,y)
fn(x=5,y=6)
- 不可放可变关键字参数之后
直接报语法错误
def fn(**kwargs, x):
^
SyntaxError: invalid syntax
- 必须用关键字方式传参 否则会报 missing required keyword-only arguments错误
def fn(*args, x, y, **kwargs):
print(x)
print(y)
print(args)
print(kwargs)
fn(7,9,x=3,a=1,b='python')
TypeError: fn() missing 1 required keyword-only argument: 'y'
可变参数与默认参数
def fn(y, *args, x=5):
print('x={}, y={}'.format(x, y))
print(args)
- 以上代码中
- y为位置参数
- agrs为可变位置参数
- x为Keyword-only参数 且指定了默认值为5
因此在传参时 至少要传入y值 fn(3) 或者fn(y=3)都可以
- 若要传入可变位置参数
对y的传值不可用关键字传参法
因为关键字参数必须放位置参数后面
因此fn(y=17,2,3,x=10) 是错误的
可改为 fn(17,2,3,x=10)
def fn(x=5, **kwargs):
print('x={}'.format(x))
print(kwargs)
- 以上代码中
- x为位置参数 且指定了默认值为5
- kwagrs为可变关键字参数
注意关键字x已被位置参数x占用 关键字中任然传入x会报multiple values的错误
不同参数的一般顺序为: 普通位置 默认 可变位置 Keyword-only(可带默认值) 可变关键字
def fn(x, y, z=3, *arg, m=4, n, **kwargs):
pass
- 以上代码中
- x y z 为位置参数 且z指定了默认值为3
- agrs为可变位置参数
- m n为Keyword-only参数 且m指定了默认值为4
- kwargs为可变关键字参数
参数解构
lst=[1,2,3]
print(lst)--> [1,2,3]
print(*lst)---> 1 2 3
- 给函数提供实参时 可在集合类型前使用*或** 把集合类型的数据解开 提取出所有元素作为函数的实参
- 字典类型使用** 非字典类型使用*
def add(*iterable):
result = 0
for x in iterable:
result += x
return result
add(1,2,3)
add(*[1,2,3])
add(*range(10))
返回值
- 用return语句返回”返回值”
- 无return语句 则隐式调用 return None
- return语句以后的内容不被执行
- 函数只能返回一个值 多个值可存入元组字典等容器再返回
==作用域==
- 作用域是指 标识符/变量的可见范围
- 全局作用域: 在程序的整个运行环境都可见
x=5
def fn():
print(x)
fn() --> 5
# x是全局变量 函数内可见
- 局部作用域
- 在函数 类 内部可见
- 局部变量的使用范围不超过其所在的局部作用域
def outer2(): #
o = 65
def inner():
o = 97
print("inner {}".format(o))
print(chr(o))
print("outer {}".format(o))
inner()
outer2()
- 以上函数中 o97只在inner函数内起作用 不会影响inner函数之外的o65的值
- 调用outer2()函数后
- 执行outer2()内部的print语句 此作用域下o的值为65
- 调用inner()函数
- inner()函数执行两条打印语句 inner()内的作用域下 o的值为97 不受外层o65的影响
- 外层变量在内层作用域可见
- 内部变量在外层作用域不可见
x=5
def fn():
x+=1
print(x)
fn() --> UnboundLocalError: local variable 'x' referenced before assignment
# x+=1等价于x=x+1
# x=x+1表示(用x+1的结果)对x赋值 赋值则是定义x 既然内部作用域定义了x 就不去外部作用域找x
# 可简单理解为只要出现x=?? 就是定义x
# 等号右边 未定义x就使用x 报错
闭包
- 自由变量 : 未在本地作用域定义的变量
- 闭包 : 在嵌套函数中 内层函数引用了外层函数的自由变量
def counter():
c=[0] #c是元组
def inc():
c[0]+=1 #c[0]是c的内部元素 改变内部元素不是重新定义c元组 此时去外部访问c
return c[0]
return inc #counter函数返回标识符inc inc实质是counter()函数的内部函数对象
foo=counter()#counter()函数返回inc函数对象 并赋给foo
print(foo(),foo()) --> 1 2#每执行一次foo()函数 c[0]就增加1
c=100 #函数内有c变量 不会读取函数外面的c
print(foo()) --> 3
\###以上为python2中闭包的实现方式
nonlocal关键字
将变量标记位不在本地作用域定义 而在向上的某一局部作用域定义 但不在全局作用域定义
def counter():
count=0
def inc():
nonlocal count
count+=1
return count
return inc
foo=counter()
foo() --> 1
foo() --> 2
###python3 使用nonlocal关键字实现对普通变量闭包 从而实现直接修改外层局部变量
默认值的作用域
def foo(xyz=[]):
xyz.append(1)
print(xyz)
foo() --> [1]
foo() --> [1,1]
print(xyz) --> NameError: name 'xyz' is not defined
- 为什么第二次调用foo()函数时输出的是[1,1]
指定过默认值的参数可以看作函数的局部变量 - foo.__defaults__ 可查看位置参数的默认值
- foo.__kwdefaults__ 可查看Keyword-only参数的默认值
def foo(xyz=[], s=123, *,u='abc'):
xyz.append(1)
return xyz,s,u
print(foo.__defaults__) --> ([],123)
print(*foo(), id(foo)) --> [1] 123 abc 2344518331792
print(foo.__defaults__) --> ([1],123)
print(*foo(), id(foo)) --> [1, 1] 123 abc 2344518331792
print(foo.__kwdefaults__) --> {'u': 'abc'}
- 两次调用函数地址未改变 则函数对象未改变
- xyz是引用类型 列表内元素改变 列表未变
- 但调用函数后默认值确实变了
- __defaults__属性 __kwdefaults__ 分别使用元组 字典 保存位置参数 keyWord-only参数的默认值 默认值不会因重新赋值而改变
默认值是否可变
def foo(xyz=[], u='abc', z=123):
xyz = xyz[:] #影子拷贝
xyz.append(1)
print(xyz)
foo() --> [1]
print(foo.__defaults__) --> ([], 'abc', 123)
foo() --> [1]
print(foo.__defaults__) --> ([], 'abc', 123)
foo([10]) --> [10,1]
print(foo.__defaults__) --> ([], 'abc', 123)
foo([10,5]) --> [10,5,1]
print(foo.__defaults__) --> ([], 'abc', 123)
- 最后的返回值是原列表的拷贝
- 永远不会修改默认值
def foo(xyz=None, u='abc', z=123):
if xyz is None:
xyz = []
xyz.append(1)
print(xyz)
foo() --> [1]
print(foo.__defaults__) --> (None, 'abc', 123)
foo() --> [1]
print(foo.__defaults__) --> (None, 'abc', 123)
foo([10])--> [10,1]
print(foo.__defaults__) --> (None, 'abc', 123)
foo([10,5])--> [10,5,1]
print(foo.__defaults__) --> (None, 'abc', 123)
- 默认值为不可变类型 若使用默认值None就创建列表,以修改默认值
- 通过传入值灵活判断是否修改默认值 推荐使用
变量名解析原则LEGB
- Local 本地作用域
- Enclosing 嵌套函数外部空间(闭包)
- Global 全局作用域
- Build-in 内置模块命名空间
- 本地->闭包->全局->内置模块(LEGB)
递归 Recursion
- 函数直接或间接调用自身就是递归
- 递归一定要有边界条件
斐波那契数列
- 数学表达式: F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2)
- 代码实现:
def fib(n):
return 1 if n < 2 else fib(n-1) + fib(n-2)
for i in range(5):
print(fib(i), end=' ')
- 递归一定要有退出条件 否则就是无限调用的死循环
- Python默认限制最大递归深度为1000
- 可用sys.getrecursionlimit()与sys.setrecursionlimit()命令查看或修改最大递归深度
- 纯递归效率太低 应该做优化
- 绝大多数递归都可通过循环实现 因此尽量不使用递归
斐波那契函数的优化:
pre = 0
cur = 1
print(pre, cur, end=' ')
def fib(n, pre=0,cur=1):
pre, cur = cur, pre + cur
print(cur, end=' ')
if n == 2:
return
fib(n-1, pre, cur)
fib(n)
匿名函数
Lambda表达式
- 格式 lambda 参数列表:表达式
- lambda x,y:x+y+1 等价于
def add1(x,y):
return x+y+1
- 调用 (lambda x,y:x+y+1)(4,5)
- lambda表达式只能写在一行上 被称为单行函数
- 在高阶函数传参时 lambda表达式可以简化代码
[x for x in (lambda *args: map(lambda x: (x+1,args), args))(*range(5))]
maps=map(lambda x: (x+1,args), args) #map函数用于映射 将agrs内的元素作为参数依次传入前面的函数 返回由函数结果(元素值+1与args组成的元组)组成的迭代器
lams=(lambda *args:maps) #最外侧为生成器表达式 内侧将args参数解包 返回maps
[x for x in lams] #最后一步用列表解析式将lams的元素依次加入列表
输出结果为:
[(1, (0, 1, 2, 3, 4)),
(2, (0, 1, 2, 3, 4)),
(3, (0, 1, 2, 3, 4)),
(4, (0, 1, 2, 3, 4)),
(5, (0, 1, 2, 3, 4))]
生成器
生成器
- 生成器是指生成器对象 可由生成器得到
- 或使用yield关键字得到生成器函数 调用该函数得到生成器对象
生成器函数
- 函数体中包含yield语句 返回生成器对象
- 生成器对象 是一个可迭代对象 也是迭代器
- 生成器对象 延迟计算 惰性求值
def inc():
for i in range(5):
yield i
print(type(inc)) --> <class 'function'>
print(type(inc())) --> <class 'generator'>
x = inc()
print(type(x)) --> <class 'generator'>
print(next(x)) --> 0
for m in x:
print(m, '*') -->1 *
2 *
3 *
4 *
for m in x:
print(m, '**') # 生成器只能遍历一次
def gen():
print('line 1')
yield 1
print('line 2')
yield 2
print('line 3')
return 3
next(gen()) --> line 1
next(gen()) --> line 1
g = gen() #g是gen的一个实例
print(next(g)) --> line 1
print(next(g)) --> line 2
print(next(g)) --> StopIteration
print(next(g, 'End')) --> 相当于设置了默认值 遍历完毕不可next 就返回end
- 生成器函数中使用多个yield语句 每执行一次next就返回一个值(写在yield关键字后面的值 类似于遇到yield就暂停运行 并返回一个值)
- return语句可用于终止函数 但return后面的值无法得到
- 调用next 遇到return会报StopIteration异常
- 可用next(g, ‘End’) 的方法为StopIteration异常指定默认返回值
生成器应用:
def counter():
i = 0
while True:
i += 1
yield i
def inc(c):
return next(c)
c = counter()
print(inc(c)) --> 1
print(inc(c)) --> 2
修改版:
def inc(c):
def counter():
i = 0
while True:
i += 1
yield i
c = counter()
def _inc():
return next(c)
foo=inc()
print(foo()) --> 1
print(foo()) --> 2
优化版:
def inc():
def counter():
i = 0
while True:
i += 1
yield i
c = counter()
return lambda : next(c)
foo = inc()
print(foo())
print(foo())
斐波那契数列:
def fib():
x = 0
y = 1
while True:
yield y
x, y = y, x+y
foo = fib()
for _ in range(5):
print(next(foo))
for _ in range(100):
next(foo)
print(next(foo))
yield from
def inc():
for x in range(1000):
yield x
等价于
def inc():
yield from range(1000)