Python中的函数
一、函数的定义与使用
1). Python中的函数
- 使用之前必须先定义,解释性语言的特点决定。
- python里面的函数支持嵌套调用
2). 函数定义
-
def
为函数定义的关键字 - 返回值返回一个返回值,如果有多个则处理成tuple再返回。
- 没有返回值,可以看成返回
None
def test(x, y):
pass
return Object
3). 关键字传参
- 关键字传参顺序没有需求,但是个数必须一样
- 如果和普通传参混合使用,普通参数位置必须在关键字参数左边
# type为默认值,静态变量! 可选项,按照需求传值
def handle(x, type='mysql'):
print(x)
print(type)
handle('hello')
handle('hello',type='sqlite')
handle('hello','sqlite')
4). 参数组传参:
-
*
:列表参数
# *args 处理成元组
def test(x, *args):
print(x)
print(args)
test(1) # 第二个传入空
test(1,2,3,4,5) # 非固定长度参数传值,后面当做列表传值
test(1,{'name':'alex'}) # 将字典当成元组的第一个元素
test(1,['x','y','z']) # 将列表当成元组的第一个元素
test(1,*['x','y','z']) # 遍历操作传值,处理成元组
test(1,*('x','y','z')) # 遍历操作传值,处理成元组
-
**
:字典参数
# **kwargs 处理成字典
def test(x, **kwargs):
print(x)
print(kwargs)
test(1,y=2,z=3) # y=2相当于kye:value,一个*就不能处理了,这个传递给**字典
test(1,1,2,2,2,2,2,y=2,z=3) # 会报错
test(1,y=2,z=3,z=3) # 会报错 :一个参数不能传两个值
- 元组参数与字典参数联合使用
- 顺序:元组参数永远在字典参数的左边
- 个数:每个类型至多一个
def test(x, *args, **kwargs): # 如果连在一起写,顺序不能变,否则会违反规则,这个可以传任何长度的参数
print(x)
print(args,args[-1])
print(kwargs,kwargs.get('y'))
test(1,1,2,1,1,11,1,x=1,y=2,z=3) # 报错,x重复传值
test(1,1,2,1,1,11,1,y=2,z=3)
test(1,*[1,2,3],**{'y':1})
二、全局变量和局部变量
约定俗成:全局变量变量名大写,局部变量变量名小写
1). 命名空间
- 存放变量名字的内存地址,其中绑定着对应值的映射关系,字典的方式,管理者变量的作用域。
- 命名空间分类
-
locals
:函数内的名称空间,局部变量和形参。 -
globals
:全局变量的命名空间,比如函数定义所在模块的名字空间。 -
builtins
:内置模块的名字空间 比如len
-
2). 作用域的查找顺序
- L:locals 当前
- E:enclosing 相邻的上一级
- G:globls
- B:builtins
总结:从小到大
3). 全局变量与局部变量
- 一般顶头写,没有缩进,整个文件里面都生效
- 在最外面循环、条件等里面定义的也为全局
name = 'lhf'
li = '全局变量'
def change_name():
# 声明name是全局变量,可读取和赋值。
# 如果没有声明直接赋值,就默认为局部变量
global name
lists = [18]
# 全局变量global或者上级变量nonlocal,申明必须写在对应赋值语句前面
def inner_name():
nonlocal name # nonlocal,指定上一级变量,如果没有就继续往上直到找到为止
# 如果没有申明就对变量内容追加、修改或者删除操作,会反复向上级寻找,直到找到再操作。
lists.append(20)
li = "哈哈" # 局部变量,里面调用的时候屏蔽了全局变量
name = '帅了' # 全局变量
print(name, li)
# 以下情况会报错
li = [1,2,3,4,5]
def printMyLi():
print(li)
li = ['a','b','c']
printMyLi()
# UnboundLocalError: local variable 'li' referenced before assignment
4). 函数使用注意
-
Python为解释性语言,函数的调用必须在声明之后 ,
-
案例,错误的写法
def foo():
print('from foo')
bar()
foo() # 报错
def bar():
print('from bar')
- 正确的写法
def bar():
print('from bar')
def foo():
print('from foo')
bar()
foo() # 不报错
- 或者如下写法
def foo():
print('from foo')
bar()
def bar():
print('from bar')
foo() # 不报错
三、递归
1). 递归的条件
- 必须有一个明确的结束条件
- 每次进入更深一层的递归时,问题规模都应有所减小
- 递归效率不高,层次过多会导致栈溢出
2). 案例:汉诺塔问题
将a上的盘子移动到c盘,可通过b盘,每次只移动一次,不管何时每个柱子上面小盘永远在上面
import time
count = 0
a = "A"
b = "B"
c = "C"
def towerOfHanoi(n, a, b, c):
global count
if n == 0:
return
else:
towerOfHanoi(n - 1,a,c,b) # 之前的人想法将A上当前之上所有的盘子通过C转到B
print("Move %s to %s" % (a, c)) # 当前人移动A盘到C
towerOfHanoi(n - 1,b,a,c) # 之后的人想法将B上所有的盘子通过A转到C
count += 1
n = int(input("请输入需要移动的盘子个数\n>>>"))
towerOfHanoi(n, a, b, c)
print("总共移动步骤:",count)
res=time.sleep(10) # 休眠功能,默认按照秒计算
print('----------->')
3). 尾调用递归优化
在函数的最后一步调用另外一个函数(不一定是最后一行),由于本函数内后续没有执行逻辑,所以不必将本函数状态保存入栈,直接使用下一次调用的函数。
def test():
print("from test")
def test1():
print('from test1')
test() # 当调用这个函数的时候(最后一步),接下来也没有操作,所以当前函数状态不保存
def test2():
print('from handle')
return test1() #!!这种情况当前函数也会被释放
def test3():
print("")
return test1() + 1 # 这种情况不会释放
四、函数的作用域
- Python中的函数可以嵌套定义,函数也存放于某一个固定的内存中,处理函数作用域时可以类比于变量的作用域
def test1():
print('in the test1')
def test():
print('in the test')
return test1 # 返回函数的内存地址(加括号是运行)
print(test) # test的地址
res = test() # 执行test,并获得test1的地址
res() # 执行test1()
五、lambda 匿名函数(函数式编程)
1). lambda语法
lambda 形参 : 返回值
- lambda一般和其他函数一起使用,不会单独使用
- 只能返回一个值
t = lambda x : x + "_sb"
print(t("a"))
2). 函数式编程特点
- 不可变数据:函数中不用变量保存状态,不修改变量
- 函数接收的参数是一个函数名
- 返回值中包含函数
def foo(n): # n=bar
print(n)
def bar(name):
print('my name is %s' % name)
foo(bar) # 把函数当作参数传给另外一个函数
foo(bar('alf'))
3). map函数
- map函数处理可迭代的数据类型, 类比Java中的
- map函数对于可迭代数据类型的每一个元素进行 lambda指定的操作
- 格式
new_variable = map(函数名或者lambda指定处理步骤, 可迭代对象)
- 案例
# 实现里面的元素的平方
num_l=[1,2,10,5,3,7]
num_a = map(lambda x : x ** 2, num_l)
for i in num_a:
print(i)
map函数处理序列中的每个元素,得到的结果是一个"列表",该"列表"元素个数及位置与原来一样
4). filter函数
- filter函数是过滤函数,同样处理可迭代数据类型
# 打印偶数
numbers = filter(lambda x : x % 2 == 0, range(0, 101))
for i in numbers:
print(i)
filter遍历序列中的每个元素,判断每个元素得到布尔值,如果是True则留下来
5). reduce函数
用的少!
- 类比Java函数式编程中的Consumer接口
- 使用方法
reduce(lambda x, y: x与y的运算, 可迭代数据类型, 初始值)
- 案例
from functools import reduce # 需要导入模块
num_l=[1,2,3,100]
reduce(lambda x,y:x+y,num_l,1)
reduce(lambda x,y:x+y,num_l)
reduce:处理一个序列,然后把序列进行合并操作
六、常见内置函数
1). 面向语言特性的编程
- 打印当前程序所有变量
dir()
- 打印一个对象中的所有属性名
dir(dict)
2). 进制转换
- 10进制转16进制
hex(b)
- 十进制转八进制
oct(b)
- 十进制转二进制
bin(b)
- 找出数字相关的ascii符号
chr(3)
- 显示ascii对应的数字
ord('a')
- 求除数的整数部分和小数部分
print(divmod(2,3)) # Return the tuple (x//y, x%y)
3). 排序
- 要么全是数字:默认从小道大
- 要么全是字符串:默认字典序与字符位置
d = {}
for i in range(5):
d[i] = -i
d.items() # items将字典里面的键值对弄成一个元组
sorted(d.items()) # sorted() # 排序,默认从小到大,字典序?
sorted(d.items(), key = lambda x : x[1]) # 指定key进行排序
sorted(d.items(), key = lambda x : x[1], reverse=True) # 指定key进行排序并反转
网友评论