[toc]
python 函数(一)
函数
数学定义: y=f(x),y是x的函数,x是自变量
- python 函数
由苦干语句组成的语句块、函数名称、参数列表构成,它是组织代码的最小单元格
完成一定的功能
- 函数的作用
结构化编程对代码的最基本的封装,一般按照功能组织一段代码;
封装的目的为了复用,减少冗余代码
代码更加简洁美化、可读易懂
- 函数的分类
内建的函数,如max()、reversed()等
库函数,如math.ceil()等
函数定义、调用
def语句定义函数
def 函数名(参数列表):
函数体(代码块)
[return 返回值]函数名就是标识符,命名要求一样:
- 语句块必须缩进,约定4个空格;
- Python 的函数没有return语句,隐式返回 一个None值
定义中的参数列表成为形式参数,只是一种符号表达,简称形参
调用
函数定义,只是声明了一个函数,它不会被执行,需要调用;
调用的方式,就是函数名加上小括号,括号内写上参数;
调用时写的参数是实际参数,是实实在在传的值,简称实参
- 函数举例
In [3]: def add(x,y):
...: result = x+y
...: return result
...:
...:
In [4]: out = add(100,255)
In [5]: out
Out[5]: 355
上面只是一个函数的定义,有一个函数叫做add,接收2个参数
计算的结果,通过返回值返回
调用通过函数名add加2个参数,返回值可使用变量接收;
定义需要在调用前,也就是说调用时,已经被定义过了,否则抛NameError异常
函数是可调用的对象,callable()
函数参数
- 参数调用时传入的参数要和定义的个数相匹配(可变参数例外)
- 位置参数
def f(x, y, z) 调用使用 f(1, 3, 5)
按照参数定义顺序传入实参
关键字参数
def f(x, y ,z) 调用使用f(x=1,y=3,z=5)
使用形参的名字来出入实参的方式,如果使用了形象名字,那么传参顺序就可以定义顺序不同
- 传参
f(z=None, y=10, x=[1])
f((1,), z=6, y=4.1)
f(y=5,z=6.2)
# 要求位置参数必须在关键字参数之前传入,位置参数是按位置对应的
函数参数默认值
- 定义时,在形参后跟上一个值
In [6]: def add(x=1,y=5):
...: return x+y
In [7]: callable(add)
Out[7]: True
In [8]: add()
Out[8]: 6
In [9]: add(6)
Out[9]: 11
In [10]: add(6,7)
Out[10]: 13
In [11]: add(y=8,x=1)
Out[11]: 9
- 作用
参数的默认值可以在未传入足够的实参的时候,对没有给定的参数赋值为默认值
参数的非常多的时候,并不需要用户每次都输入所有的参数,简化函数调用
In [12]: def add(x,y=5):
...: return x+y
In [13]: add(3)
Out[13]: 8
In [14]: add(3,6)
Out[14]: 9
- 举例
定义一个函数login,参数名称为host、port、username、passwd
def login(host='127.0.0.1',port='8080',username='rj',password='test'):
print('{}:{}@{}/{}'.format(host,port,username,password))
login()
login('127.0.0.1',80,'tom','tom')
login('127.0.0.1',username='root')
login('localhost', port=80,password='com')
login(port=80,password='test',host='web')
可变参数
- 问题
有多个数,需要求累加求和
In [15]: def add(nums):
...: sum = 0
...: for x in nums:
...: sum += x
...: return sum
In [16]: add([1,2,5])
Out[16]: 8
In [17]: add((2,4,6))
Out[17]: 12
- 可变参数
一个形参可以匹配任意个参数
有多个数,需要累加求和
In [22]: def add(*nums):
...: sum = 0
...: print(type(nums))
...: for x in nums:
...: sum += x
...: print(sum)
...:
In [23]: add(3,6,9)
<class 'tuple'>
18
在形参前使用*表示该形参是可变参数,可以接收多个实参;
收集的实参名称和值组成一个tuple;
可变参数
关键字参数的可变参数
- 配置信息打印
In [9]: def showconfig(**kwargs):
...: for k,v in kwargs.items():
...: print('{} = {}'.format(k,v))
...:
In [10]:
In [10]: showconfig(host='127.0.0.1',port='8080',username='asjin',password='ssjinyao')
host = 127.0.0.1
port = 8080
username = asjin
password = ssjinyao
- 形参前使用**符号,表示可以接收多个关键字参数
- 收集的实参名称和值组成一个字典
混着写,关键的默认定义好,其它的用kwargs
def showconfig(username, password, **kwargs)
def showconfig(username, *args, **kwargs)
def showconfig(username, password,**kwargs, *args)
总结
- 有位置可变参数和关键字可变参数;
- 位置可变参数在形参前使用一个星号*;
- 关键字可变参在形参前使用两个星号**;
- 位置可变参数和关键字可变参数都可以收集若干个实参,位置可变参数收集形成一个tuple,关键字可变参数收集形成一个dict;
- 混合使用参数的时候,可变参数要放到参数列表的最后,普通参数要放到参数列表前面,位置可变参数需要在关键字可变参数之前。
举例
def fn(x, y, *args, **kwargs):
print(x)
print(y)
print(args)
print(kwargs)
fn(3, 5, 7, 9, 10,a=1,b='python')
fn(3, 5)
fn(3, 5, 7)
fn(3, 5, a=1,b='python')
举例
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' )
keyword-only 参数
- keyword-only 参数(Python3 加入)
如果在一个星号参数后,或者一个位置可变参数后,出现的普通参数,实际上已经不是普通的参数了,而是keyword-only参数
def fn(*args, x):
print(x)
print(args)
fn(3, 5, x=7)
args 可以看做已经截获了所有的位置参数,x不使用关键字参数就可能拿到实参
- keyword-only 参数另一种形式
def fn(*, x, y):
print(x,y)
fn(x=5, y=6)
*号之后,普通形参都变成了必须给出的keyword-only参数
- 可变参数和参数默认值
举例
def fn(*args, x=5):
print(x)
print(args)
fn() # 等价于fn(x=5)
fn(5)
fn(x=6)
fn(1 , 2, 3, x=10)
x 是keyword-only 参数
举例
def fn(x=5, **kwargs):
print('x={}'.format(x))
print(kwargs)
fn()
fn(5)
fn(x=6)
fn(y=3,x=10)
fn(3,y=10)
函数参数
- 参数规则
参数列表参数一般顺序是,普通参数、缺省参数、可变位置参数、keyword-only参数(可带缺省值)、可变关键字参数
def fn(x, y, z=3, *arg, m=4, n, **kwargs):
print(x, y, z, m,n)
print(args)
print(kwargs)
def connect(host='localhost',port='3306',user='admin',password='admin',**kwargs):
print(host,port)
print(user,passowrd)
print(kwargs)
connect(db='cmdb')
connect(host='172.16.0.8',db='cmdb')
connect(host='172.160.0.9',db='cmdb',password='mysql')
参数解构
举例: 加法函数
def add(x, y):
return x+y
add(4, 5)
add((4, 5))
t = (4, 5)
add(t[0], t[1])
# add(*t) 或 add(*(4, 5)) add(*[4,5]) add(*{4,6})
# add(*range(1,3))
In [1]: def add(x, y):
...: return x+y
In [3]: add((1,2)[0],[3][0])
Out[3]: 4
In [8]: def add(x, y):
...: return x+y
In [9]: lst = [1,3]
In [10]: add(*lst)
Out[10]: 4
# 当给定一个集合
In [12]: add(*{5,6})
Out[12]: 11
例:
In [28]: def add(*keys):
...: sumnum = 0
...: for i in keys:
...: sumnum = sumnum + i
...: return sumnum
In [29]: add(*range(1,101))
Out[29]: 5050
- 参数解构
给函数提供实参的时候,可以在集合类型前使用* 或者** , 把集合类型的结构解开,提取所有元素做为函数的实参
非字典类型使用*解构成位置参数
字典类型使用*解构成位置参数
提取出来的元素数王要和参数的要求匹配,也要和参数的类型匹配
def add(x, y):
return x+y
add(*(4 ,5))
add(*[4,5])
add(*{4,6})
d = {'x': 5, 'y':6}
add(**d)
add(**{'a':5, 'b':6})
In [39]: def add(x, y):
...: return x+y
In [40]: dct = {'x': 1, 'y':6}
In [41]: add(*dct.values())
Out[41]: 7
练习
编写一个函数,能够接受至少2个参数,返回最小值和最大值。
In [49]: def mums(x, y, *args):
...: print(min(x,y,*args))
...: print(max(x,y,*args))
In [50]: mums(*range(1,100001))
1
100000
方法二
In [51]: import random
In [52]: def double_values(*nums):
...: print(nums)
...: return max(nums), min(nums)
In [53]: print(*double_values(*[random.randint(10,20) for _ in range(10)]))
(14, 11, 19, 11, 17, 12, 13, 18, 12, 13)
19 11
编写 一个函数,接受一个参数n, n为正整数, 左右两种打印方式, 要求数字必须对齐
In [59]: def trangle_print(n):
...: for i in range(1, n+1):
...: for j in range(n, 0, -1):
...: if i < j:
...: print(' '*len(str(j)), end=' ')
...: else:
...: print(j, end=' ')
...: print()
...:
In [60]: trangle_print(16)
1
2 1
3 2 1
4 3 2 1
5 4 3 2 1
6 5 4 3 2 1
7 6 5 4 3 2 1
8 7 6 5 4 3 2 1
9 8 7 6 5 4 3 2 1
10 9 8 7 6 5 4 3 2 1
11 10 9 8 7 6 5 4 3 2 1
12 11 10 9 8 7 6 5 4 3 2 1
13 12 11 10 9 8 7 6 5 4 3 2 1
14 13 12 11 10 9 8 7 6 5 4 3 2 1
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
方法二
In [61]: def show(n):
...: tail = " ".join([str(i) for i in range(n,0,-1)])
...: print(tail)
...: width = len(tail)
...: for i in range(1,n):
...: print("{:>{}}".format(" ".join([str(j) for j in range(i,0,-1)]), width))
...: print(tail)
...:
In [62]: show(12)
12 11 10 9 8 7 6 5 4 3 2 1
1
2 1
3 2 1
4 3 2 1
5 4 3 2 1
6 5 4 3 2 1
7 6 5 4 3 2 1
8 7 6 5 4 3 2 1
9 8 7 6 5 4 3 2 1
10 9 8 7 6 5 4 3 2 1
11 10 9 8 7 6 5 4 3 2 1
12 11 10 9 8 7 6 5 4 3 2 1
In [63]: def showtail(n):
...: tail = " ".join([str(i) for i in range(n,0,-1)])
...: print(tail)
...: for i in range(len(tail)):
...: if tail[i] == ' ':
...: print(' '*i, tail[i+1:])
...:
In [64]: showtail(12)
12 11 10 9 8 7 6 5 4 3 2 1
11 10 9 8 7 6 5 4 3 2 1
10 9 8 7 6 5 4 3 2 1
9 8 7 6 5 4 3 2 1
8 7 6 5 4 3 2 1
7 6 5 4 3 2 1
6 5 4 3 2 1
5 4 3 2 1
4 3 2 1
3 2 1
2 1
1
直接插入排序
在未排序序列中,构建一个子排序序列,直至全部数据排序完成;
将待排序的数,插到已经排序的序列中合适的位置;
增加一个哨兵,放入待比较值,让它和后面已经排好的序列比较,找到适合的插入点;
- 增加一个哨兵位,每轮比较将待比较数入;
- 哨兵依次和待比较数的前一个数据比较,大数靠右移动,找到哨兵中值的插入位置;
- 每一轮结束后,得到一个从开始到待比较数位置的一个有序序列
实例
m_list = [[1, 9, 8, 5, 6, 7, 4, 3, 2 ], [1, 2, 3, 4, 5, 6, 7, 8, 9],
[9, 8, 7, 6, 5, 4, 3, 2, 1 ], [1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 2]]
nums = [0] + m_list[0]
sentinel, *origin = nums #哨兵位,待比较数字
count_swap = 0
count_iter = 0
length = len(nums)
for i in range(2,length): #从2开始
nums[0] = nums[i] # 放置哨兵
j = i -1
count_iter +=1
if nums[j] > nums[0]: # 大数右移,找到插入位置
while nums[j] > nums[0]:
nums[j+1] = nums[j] # 依次右移
j -= 1
count_swap +=1
nums[j+1] = nums[0] # 将哨兵插入,注意挺好入的右侧要+1
print(nums, count_swap, count_iter)
作用域
- 一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域
- 举例,对比左右2个函数
In [4]: x = 50
In [5]: def show():
...: print(x)
In [7]: show()
50
看似是可见的,但尝试以下语句
In [8]: x = 5
In [9]: def foo():
...: x += 1
...: print(x)
In [10]: foo()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-10-c19b6d9633cf> in <module>()
----> 1 foo()
<ipython-input-9-69a7705e0064> in foo()
1 def foo():
----> 2 x += 1
3 print(x)
4
UnboundLocalError: local variable 'x' referenced before assignment
In [11]:
全局作用域
在整个程序运行环境中都可见局部作用域
在函数、类等内部可见
局部变量使用范围不能超过期所有的局部作用域
def fn1():
x = 1 # 局部作用域,在fn1内
def fn2():
print(x)
print(x)
In [16]: def outer():
...: o = 65
...: def inner():
...: print("inner {}".format(o))
...: print(chr(o))
...: print("outer {}".format(o))
...: inner()
In [18]: outer()
outer 65
inner 65
A
对比
In [19]: def outer2():
...: o = 65
...: def inner():
...: o = 97
...: print("inner {}".format(o))
...: print(chr(o))
...: print("outer {}".format(o))
...: inner()
...:
In [20]: outer2()
outer 65
inner 97
a
全局变量global
x = 5
def foo():
global x
x += 1
- 使用global关键字的变量,将foo内的x声明为使用外部的全局作用域定义的x
- 全局作用域中必须有x的定义
x = 100
def foo():
global x
x = 10
x += 1
print(x)
foo()
print(x)
11
11
- 但是, x=10 赋值即定义, x 在内部作用域为一个外部作用域的变量赋值, 所以x+=1会报错。
- 注意,这里的x的作用域还是全局的
global 总结
- x+=1这种是特珠形式产生的错误的原因? 先引用后赋值,而python动态语言是赋值才算定义,才能被引用。解决办法,在这条语句前加x=0之类的赋值语句,或者使用global告诉内部作用域,去全局作用域查变量定义
- 内部作用域作用x =5 之类的赋值语句会重新定义局部作用域使用的变量x,但是,一旦这个作用域中使用global声明x为全局的,那么x=5相当于在为全局作用域的变量x赋值
- global的使用原则
- 外部作用域亦是会内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离
- 如果函数需要使用外部全局变量,请使用函数的形参传参解决
闭包
- 自由变量: 未在本地作用域中定义的变量。例如定义在内存函数外的外层函数作用域中的变量
- 闭包: 就是一个概念,出现在嵌套函数中,指的内层函数引用到了外层函数的自由变量就形成了闭包。很多语言都有这个概念,最熟悉就是JavaScript
In [8]: def counter():
...: c = [0]
...: def inner():
...: c[0] +=1
...: return c[0]
...: return inner
In [9]: foo = counter()
In [11]: print(type(foo))
<class 'function'>
In [12]: print(type(foo()))
<class 'int'>
In [13]: print(foo(),foo())
2 3
In [15]: print(foo())
4