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

要点:

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

  2. 参数列表

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

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

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

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

​ 例:

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

​ 运行结果:

6

  1. 调用函数之前,必须要先定义函数,即先调用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]

  • 20
    点赞
  • 123
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值