python函数——自定义函数详解

python函数内容参考

文章目录

  • python函数
  • 自定义函数
    • 返回值
    • 分类
      • 递归函数
      • 嵌套函数(内部函数)
    • 变量的作用域
      • 全局变量:
      • 局部变量:
    • Python函数参数
      • 参数的传递
        • 可变对象-不可变对象
        • 传递不可变对象包含的子对象是可变的情况
      • 参数的类型
        • 位置参数(关键字参数)
        • 默认参数 int(object, base)
        • 命名参数
        • 可变参数
        • 强制命名参数
        • 可变关键字参数
        • 传递多个参数
    • 其他
      • 函数对象
      • lambda表达式和匿名函数
      • eval()函数
      • nonlocal关键字
      • LEGB规则
      • 拷贝

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()打印*

要点:

  1. Python执行def时,会创建一个函数对象,并绑定到函数名变量上。

  2. 参数列表

​ (1) 圆括号内是形式参数列表,有多个参数则使用逗号隔开

​ (2) 形式参数不需要声明类型,也不需要指定函数返回值类型

​ (3) 无参数,也必须保留空的圆括号

​ (4) 实参列表必须与形参列表一一对应

​ 例:

def print_star(a,b,c):print(a+b+c)
print_star(1,2,3)

​ 运行结果:

  1. 调用函数之前,必须要先定义函数,即先调用def创建函数对象

​ (1)内置函数对象会自动创建

​ (2)标准库和第三方库函数,通过import导入模块时,会执行模块中的def语句

返回值

return返回值

(1)如果函数体中包含return语句,则结束函数执行并返回值;

(2)如果函数体中不包含return语句,则返回None值

(3)要返回多个返回值,使用列表、元组、字典、集合将多个值“存起来”即可。

返回多个值 tuple类型

例:

def data_of_square(side):C = 4 * sideS = side * sidereturn C, SC, S = data_of_square(16)
print('周长 = {}'.format(C)) # ==> 周长 = 64
print('面积 = {}'.format(S)) # ==> 面积 = 256

没有返回值->None

分类

递归函数

理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。

在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

def fact(n):if n==1:return 1return 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 numprint(num)num += 1print(num)
test()

运行结果:

例2:未使用global

num = 0
def test():#global numprint(num)num += 1print(num)
test()

提示错误:

局部变量:

1.在函数体中(包含形式参数)声明的变量。

2.局部变量的引用比全局变量快,优先考虑使用。

3.如果局部变量和全局变量同名,则在函数内隐藏全局变量,只使用同名的局部变量

#全局变量和局部变量同名测试

a=100
def print_():a=3print(a)
print_()
print(a)

运行结果:

def f1(a,b,c):print(a,b,c)print(locals())#打印输出的局部变量print("#"*20)print(globals())#打印输出的全局变量
f1(1,2,3)

运行结果:

局部变量和全局变量效率测试

局部变量的查询和访问速度比全局变量快,优先考虑使用

#测试局部变量、全局变量的效率

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.sqrtstart=time.time()for i in range(10000000):b(30)end=time.time()print("耗时{0}".format((end-start)))
test01()
test02()

运行结果:

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)

运行结果:

a=100
def f1(n):print("n:",id(n)) #传进a对象的地址n=n+2 #由于a是不可变对象,因此创建新的对象nprint("n:",id(n))#n已经变成了新的对象print(n)
f1(a) #a是不可变对象
print("a:",id(a))

运行结果:

传递不可变对象包含的子对象是可变的情况

传递不可变对象时,不可变对象里面包含的子对象是可变的。则方法内修改了这个可变对象,源对象也发生了变化。

a=(10,20,[5,6])
print("a:",id(a))
def test(m):print("m:",id(m))m[2][0]=888print(m)print("m:",id(m))
test(a)
print(a)

运行结果:

参数的类型

位置参数(关键字参数)

函数调用时,实参默认按位置顺序传递,需要个数和形参匹配,按位置传递的参数称为“位置参数”

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_ = 0for item in args:sum_ += itemavg = sum_ / len(args)return avg
average(1, 2, 2, 3, 4) # ==> 2.4
average()

报错:

强制命名参数

再带*的可变参数后面增加新的参数,必须在调用的时候“强制命名参数”

>>> 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)

运行结果:

传递多个参数

如果需要传入的实际参数有多个,我们在定义形式参数的时候,可以有两种形式,一是*parameter,二是**parameter

*parameter形式 表示接收任意多个实际参数并将其放到一个元组中,类似于传递地址的形式,将多个数据一次性传入。

def printcoff(*para):for item in para:print(item)
printcoff("karl","inter","killer")
plist = [1,2,3]
printcoff(plist)
printcoff(*plist)

运行结果:

**parameter形式 表示接受任意多个类似关键字参数一样显示赋值的实际参数,并将其放到一个字典中。

def printcoff(**para):for key, value  in para.items():print(key,value)
pdict ={"1":"karl","2":"inter","3":"killer","4":"python"}
printcoff(**pdict)

运行结果:

其他

函数对象

函数也是对象,内存底层分析

>>> 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 = 100def outer () :b = 10def inner () :nonlocal bprint("iner b:",b)b=20global aa=1000inner()print("outer b:",b)outer()
print("a:",a)

运行结果:

LEGB规则

Python在查找“名称”时,是按照 LEGB 规则查找的:

Local --> Enclosed --> Global --> Builtin

Local 指的就是函数或者类的方法内部

Enclosed 指的是嵌套函数(一个函数包裹另一个函数,闭包)

Global 指的是模块中的全局变量

Builtin 指的是Python为自己保留的特殊名称。

如果某个name映射在局部(local)命名空间中没有找到,接下来就会在闭包作用域(enclosed)进行搜索,如果闭包作用域也没有找到,Python就会到全局(global)命名空间中进行查找,最后会在内建(built-in)命名空间搜索(如果一个名称在所有命名空间中都没有找到,就会产生一个NameError)

拷贝

浅拷贝(copy)

不拷贝子对象的内容,只是对拷贝子对象的引用

深拷贝(deepcopy)

会连子对象的内容也全部拷贝一份,对子对象的修改不会影响源对象

import copydef 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()

运行结果:

本文链接:https://my.lmcjl.com/post/2652.html

展开阅读全文

4 评论

留下您的评论.