python函数——自定义函数详解
python自定义函数 用法总结
文章目录
python函数
函数是可重用的程序代码块
不仅可以实现代码的复用,还能实现代码的一致性( 只要修改函数的代码,则所有调用该函数的地方都能得到体现)
1)一个程序由一个个任务组成,函数就是代表一个人物或者一个功能
2)函数是代码复用的通用机制
函数分类
1)内置函数
2)标准库函数
通过import语句导入库
3)第三方库函数
通过import语句导入库
4)用户自定义函数
自定义函数
def 函数名([参数列表]):
‘’‘文档字符串’‘’
函数体/ 若干语句
使用help()函数可打印出’‘’ 文档字符串’‘’(即函数注释以查看函数功能)
>>> def printf_star():
... '''打印*'''
... print('*')
...
>>> help(printf_star)
Help on function printf_star in module __main__:
printf_star()
打印*
要点:
-
Python执行def时,会创建一个函数对象,并绑定到函数名变量上。
-
参数列表
(1) 圆括号内是形式参数列表,有多个参数则使用逗号隔开
(2) 形式参数不需要声明类型,也不需要指定函数返回值类型
(3) 无参数,也必须保留空的圆括号
(4) 实参列表必须与形参列表一一对应
例:
def print_star(a,b,c):
print(a+b+c)
print_star(1,2,3)
运行结果:
6
- 调用函数之前,必须要先定义函数,即先调用def创建函数对象
(1)内置函数对象会自动创建
(2)标准库和第三方库函数,通过import导入模块时,会执行模块中的def语句
返回值
return返回值
(1)如果函数体中包含return语句,则结束函数执行并返回值;
(2)如果函数体中不包含return语句,则返回None值
(3)要返回多个返回值,使用列表、元组、字典、集合将多个值“存起来”即可。
返回多个值 tuple类型
例:
def data_of_square(side):
C = 4 * side
S = side * side
return C, S
C, S = data_of_square(16)
print('周长 = {}'.format(C)) # ==> 周长 = 64
print('面积 = {}'.format(S)) # ==> 面积 = 256
没有返回值->None
分类
递归函数
理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。
在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
嵌套函数(内部函数)
函数内部定义函数
>>> def f1():
... def f2():
... print('***')
... f2()
...
>>> f1()
***
一般在什么情况下使用嵌套函数?
1.封装-数据隐藏
外部无法访问“嵌套函数”。
2.贯彻 DRY(Don’t Repeat Yourself) 原则
嵌套函数,可以让我们在函数内部避免重复代码。
变量的作用域
全局变量:
1.在函数和类定义之外声明的变量。作用域为定义的模块,从定义位置开始直到模块结束。
2.全局变量降低了函数的通用性和可读性。应尽量避免全局变量的使用。
3.全局变量一般做常量使用。
4.函数内要改变全局变量的值,使用global声明一下
例1:
num = 0
def test():
global num
print(num)
num += 1
print(num)
test()
运行结果:
0
1
例2:未使用global
num = 0
def test():
#global num
print(num)
num += 1
print(num)
test()
提示错误:
UnboundLocalError: local variable ‘num’ referenced before assignment
局部变量:
1.在函数体中(包含形式参数)声明的变量。
2.局部变量的引用比全局变量快,优先考虑使用。
3.如果局部变量和全局变量同名,则在函数内隐藏全局变量,只使用同名的局部变量
#全局变量和局部变量同名测试
a=100
def print_():
a=3
print(a)
print_()
print(a)
运行结果:
3
100
def f1(a,b,c):
print(a,b,c)
print(locals())#打印输出的局部变量
print("#"*20)
print(globals())#打印输出的全局变量
f1(1,2,3)
运行结果:
1 2 3
{‘a’: 1, ‘b’: 2, ‘c’: 3}
####################
{‘name’: ‘main’, ‘doc’: None, ‘package’: None, ‘loader’: <class ‘_frozen_importlib.BuiltinImporter’>, ‘spec’: None, ‘annotations’: {}, ‘builtins’: <module ‘builtins’ (built-in)>, ‘file’: ‘E:\hello.py’, ‘f1’: <function f1 at 0x000001F9EB895BD0>}
局部变量和全局变量效率测试
局部变量的查询和访问速度比全局变量快,优先考虑使用
#测试局部变量、全局变量的效率
import math
import time
def test01():#全局变量
start=time.time()
for i in range(10000000):
math.sqrt(30)
end=time.time()
print("耗时{0}".format((end-start)))
def test02():#局部变量
b=math.sqrt
start=time.time()
for i in range(10000000):
b(30)
end=time.time()
print("耗时{0}".format((end-start)))
test01()
test02()
运行结果:
耗时0.9960923194885254
耗时0.5852029323577881
Python函数参数
参数的传递
参数传递本质上就是:从实参到形参的赋值操作
python中一切皆对象,所有的赋值操作都是引用的赋值
可变对象-不可变对象
1 对可变对象进行”写操作“直接作用于对象本身
2 对不可变对象进行"写操作"会产生一个新的“对象空间”并用新的值填充这块空间
可变对象:字典,列表,集合,自定义的对象
不可变对象:数字,字符串,元组,function
b=[10,20]
def f2(m):
print("m:",id(m))#b和m是同一个对象
m.append(30)#由于m是可变对象,不创建对象拷贝,直接修改这个对象
f2(b)
print("b:",id(b))
print(b)
运行结果:
m: 1751177178176
b: 1751177178176
[10, 20, 30]
a=100
def f1(n):
print("n:",id(n)) #传进a对象的地址
n=n+2 #由于a是不可变对象,因此创建新的对象n
print("n:",id(n))#n已经变成了新的对象
print(n)
f1(a) #a是不可变对象
print("a:",id(a))
运行结果:
n: 1403680853328
n: 1403680853392
102
a: 1403680853328
传递不可变对象包含的子对象是可变的情况
传递不可变对象时,不可变对象里面包含的子对象是可变的。则方法内修改了这个可变对象,源对象也发生了变化。
a=(10,20,[5,6])
print("a:",id(a))
def test(m):
print("m:",id(m))
m[2][0]=888
print(m)
print("m:",id(m))
test(a)
print(a)
运行结果:
a: 2616074214528
m: 2616074214528
(10, 20, [888, 6])
m: 2616074214528
(10, 20, [888, 6])
参数的类型
位置参数(关键字参数)
函数调用时,实参默认按位置顺序传递,需要个数和形参匹配,按位置传递的参数称为“位置参数”
def f1(a,b,c):
print(a,b,c)
>>> f1(1,2,3)
1 2 3
>>> f1(1,2) #报错,位置参数不匹配
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f1() missing 1 required positional argument: 'c'
默认参数 int(object, base)
例:int()函数
>>> int('123',8) #八进制
83
可以为某些函数设置默认值,默认值参数放到位置参数后面
>>> def f1(a,b,c=10,d=20): #c d 为默认参数
... print(a,b,c,d)
...
>>> f1(1,2)
1 2 10 20
>>> f1(1,2,3,4)
1 2 3 4
命名参数
也称“关键字参数”
按照形参的名称传递参数
例:
>>> def f1(a,b,c):
... print(a,b,c)
...
>>> f1(c=20,a=10,b=20)
10 20 20
可变参数
*param(一个星号),将多个参数收集到元组对象
**param(两个星号),将多个参数收集到字典对象
>>> def f1(a,b,*c):
... print(a,b,c)
...
>>> f1(1,2,3,4,5,6,7,8,9)
1 2 (3, 4, 5, 6, 7, 8, 9)
>>> def f2(a,b,**c):
... print(a,b,c)
...
>>> f2(1,2,name='Tom',age='18')
1 2 {'name': 'Tom', 'age': '18'}
自定义函数中无参数:若求参数长度len,报错
def func(*args):
print('args length = {}, args = {}'.format(len(args), args))
func('a') # ==> args length = 1, args = ('a',)
func('a', 'b', 'c') # ==> args length = 3, args = ('a', 'b', 'c')
def average(*args):
sum_ = 0
for item in args:
sum_ += item
avg = sum_ / len(args)
return avg
average(1, 2, 2, 3, 4) # ==> 2.4
average()
报错:
Traceback (most recent call last):
ZeroDivisionError: division by zero
强制命名参数
再带*的可变参数后面增加新的参数,必须在调用的时候“强制命名参数”
>>> del f1
>>> def f1(*a,b,c):
... print(a,b,c)
...
>>> f1(1,2,3,b=5,c=6)
(1, 2, 3) 5 6
可变关键字参数
dict,Python会把可变关键字参数当作dict去处理;对于可变关键字参数,一般使用kwargs来表示
def info(**kwargs):
print('name: {}, gender: {}, age: {}'.format(kwargs.get('name'), kwargs.get('gender'), kwargs.get('age')))
info(name = 'Alice', gender = 'girl', age = 16)
运行结果:
name: Alice, gender: girl, age: 16
传递多个参数
如果需要传入的实际参数有多个,我们在定义形式参数的时候,可以有两种形式,一是*parameter,二是**parameter
*parameter形式 表示接收任意多个实际参数并将其放到一个元组中,类似于传递地址的形式,将多个数据一次性传入。
def printcoff(*para):
for item in para:
print(item)
printcoff("karl","inter","killer")
plist = [1,2,3]
printcoff(plist)
printcoff(*plist)
运行结果:
karl
inter
killer
[1, 2, 3]
1
2
3
**parameter形式 表示接受任意多个类似关键字参数一样显示赋值的实际参数,并将其放到一个字典中。
def printcoff(**para):
for key, value in para.items():
print(key,value)
pdict ={"1":"karl","2":"inter","3":"killer","4":"python"}
printcoff(**pdict)
运行结果:
1 karl
2 inter
3 killer
4 python
其他
函数对象
函数也是对象,内存底层分析
>>> print(id(printf_star))
2204766780960
()意味着调用函数,没有 (),python会将函数当作普通对象
>>> id(printf_star)
2204766780960
>>> c=printf_star()
*
>>> id(c)
140713325135864
>>> d=printf_star
>>> id(d)
2204766780960
lambda表达式和匿名函数
lambda表达式可以用来声明匿名函数。
lambda函数是一种简单的、在同一行中定义函数的方法。
lambda函数实际生成了一个函数对象。
lambda表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数的返回值
lambda表达式的基本语法如下:
lambda arg1 , arg2 , arg3... : <表达式>
arg1 /arg2 / arg3 为函数的参数
<表达式>相当于函数体。
运算结果是:表达式的运算结果
>>> f1(1,2,3,b=5,c=6)
(1, 2, 3) 5 6
>>> f=lambda a,b,c:a+b+c
>>> print(f)
<function <lambda> at 0x000002C27599A4D0>
>>> print(f(2,3,4))
9
eval()函数
功能:将字符串str当成有效的表达式来求值并返回计算结果。
语法:eval(source[,globals[,locals]])->value
参数:
source:一个Python表达式或函数compile()返回的代码对象
globals:可选。必须是
dictionarylocals:可选。任意映射对象
>>> s="print('hello!')"
>>> eval(s)
hello!
>>> dict1=dict(a=100,b=200)
>>> d=eval("a+b",dict1)
>>> print(d)
300
nonlocal关键字
nonlocal用来声明外层的局部变量
global用来声明全局变量
a = 100
def outer () :
b = 10
def inner () :
nonlocal b
print("iner b:",b)
b=20
global a
a=1000
inner()
print("outer b:",b)
outer()
print("a:",a)
运行结果:
iner b: 10
outer b: 20
a: 1000
LEGB规则
Python在查找“名称”时,是按照 LEGB 规则查找的:
Local --> Enclosed --> Global --> Builtin
Local 指的就是函数或者类的方法内部
Enclosed 指的是嵌套函数(一个函数包裹另一个函数,闭包)
Global 指的是模块中的全局变量
Builtin 指的是Python为自己保留的特殊名称。
如果某个name映射在局部(local)命名空间中没有找到,接下来就会在闭包作用域(enclosed)进行搜索,如果闭包作用域也没有找到,Python就会到全局(global)命名空间中进行查找,最后会在内建(built-in)命名空间搜索(如果一个名称在所有命名空间中都没有找到,就会产生一个NameError)
拷贝
浅拷贝(copy)
不拷贝子对象的内容,只是对拷贝子对象的引用
深拷贝(deepcopy)
会连子对象的内容也全部拷贝一份,对子对象的修改不会影响源对象
import copy
def test_copy():
'''测试浅拷贝'''
a = [10,20,[5,6]]
b = copy.copy(a)
print("a",a)
print("b",b)
b.append(30)
b[2].append(7)
print("浅拷贝......")
print("a",a)
print("b",b)
def test_deepcopy():
'''测试深拷贝'''
a = [10,20,[5,6]]
b = copy.deepcopy(a)
print("a",a)
print("b",b)
b.append(30)
b[2].append(7)
print("深拷贝......")
print("a",a)
print("b",b)
test_copy()
print('*************')
test_deepcopy()
运行结果:
a [10, 20, [5, 6]]
b [10, 20, [5, 6]]
浅拷贝…
a [10, 20, [5, 6, 7]]
b [10, 20, [5, 6, 7], 30]
a [10, 20, [5, 6]]
b [10, 20, [5, 6]]
深拷贝…
a [10, 20, [5, 6]]
b [10, 20, [5, 6, 7], 30]
更多推荐
所有评论(0)