Python函数详解

函数的定义及其应用

所谓函数,就是把具有独立功能的代码块组织成为一个小模块,在需要的时候调用 函数的使用包含两个步骤

1.定义函数–封装独立的功能
2.调用函数–享受封装的成果
函数的作用:在开发时,使用函数可以提高编写的效率以及代码的重用‘’

定义函数
def 函数名():
    函数封装的代码

1.def是英文define的缩写
2.函数名称应该能够表达函数封装代码的功能,方便后续的调用
3.函数名称的命名应该符合标识符的命名规则
	可由字母,下划线和数字组成
	不能以数字开头
	不能与关键字重名

在这里插入图片描述

函数变量

局部变量和全局变量

局部变量是在函数内部定义的变量,只能在函数内部使用
全局变量是在函数外部定义的变量,(没有定义在某一个函数内),所有函数内部都可以使
用这个变量

局部变量

局部变量是在函数内部定义的变量,只能在函数内部使用
函数执行结束后,函数内部的局部变量,会被系统回收
不同的函数,可以定义相同的名字的局部变量,但是各用各的不会产生影响

局部变量的作用

在函数内部使用,临时保存函数内部需要使用的数据

变量的引用

变量和数据都是保存在内存中的
在python中函数的参数传递以及返回值都是靠引用传递的
在python中
变量和数据是分开存储的
数据保存在内存中的一个位置
变量中保存的是数据在内存中的地址
变量中记录数据的地址,就叫做引用
使用id()函数可以查看变量中保存数据所在内存的地址

在这里插入图片描述

函数参数

参数简介

参数的作用

函数,把具有独立功能的代码块组织成为一个小模块,在需要的时候调用
函数的参数,增加函数的通用性,针对相同的数据处理逻辑,能够适应更多的数据
1.在函数内部,把参数当作变量使用,进行需要的数据处理
2.函数调用时,按照函数定义的参数顺序,把希望在函数内部处理的数据,通过

形参和实参

形参:定义函数时,小括号中的参数,是用来接收参数用的,在函数内部作为变量使用
实参:调用函数时,小括号中的参数,是用来把数据传递到函数内部用的

参数传递

函数的参数传递本质上就是:从实参到形参的赋值操作。 Python 中“一切皆对象”, 所有的赋值操作都是“引用的赋值”。所以,Python 中参数的传递都是“引用传递”,不 是“值传递”。具体操作时分为两类:

  1. 对“可变对象”进行“写操作”,直接作用于原对象本身。
  2. 对“不可变对象”进行“写操作”,会产生一个新的“对象空间”,并用新的值填充这块空间。(起到其他语言的“值传递”效果,但不是“值传递”)
  1. 可变对象有: 字典、列表、集合、自定义的对象等
  2. 不可变对象有: 数字、字符串、元组、function 等
  1. 传递可变对象的引用:传递参数是可变对象(例如:列表、字典、自定义的其他可变对象等),实际传递的还是对象的引用。在函数体中不创建新的对象拷贝,而是可以直接修改所传递的对象。
  2. 传递不可变对象的引用传递参数是不可变对象(例如:int、float、字符串、元组、布尔值),实际传递的还是对象的引用。在”赋值操作”时,由于不可变对象无法修改,系统会新创建一个对象。

浅拷贝和深拷贝

为了更深入的了解参数传递的底层原理,我们需要了解一下“浅拷贝和深拷贝”。我们可以使用内置函数:copy(浅拷贝)、deepcopy(深拷贝)。

  1. 浅拷贝:不拷贝子对象的内容,只是拷贝子对象的引用。
  2. 深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象

浅拷贝

import copy 
def testCopy(): 
'''测试浅拷贝''' 
    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) 
testCopy() 

 a [10, 20, [5, 6]] 
 b [10, 20, [5, 6]] 
 浅拷贝...... 
 a [10, 20, [5, 6, 7]] 
 b [10, 20, [5, 6, 7], 30] 
def testDeepCopy():   
   '''测试深拷贝''' 
   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) 

深拷贝

testDeepCopy()
 a [10, 20, [5, 6]] b [10, 20, [5, 6]] 
 深拷贝...... 
 a [10, 20, [5, 6]] 
 b [10, 20, [5, 6, 7], 30]

参数类型

位置参数

#计算任意n次方
def power(x, n):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

修改后的power(x, n)函数有两个参数:x和n,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数x和n。

默认参数

由于我们经常计算x2,所以,完全可以把第二个参数n的默认值设定为2:

def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

这样,当我们调用power(5)时,相当于调用power(5, 2):
而对于n > 2的其他情况,就必须明确地传入n,比如power(5, 3)。

从上面的例子可以看出,默认参数可以简化函数的调用。设置默认参数时,有几点要注意:

一是必选参数在前,默认参数在后,否则Python的解释器会报错(思考一下为什么默认参数不能放在必选参数前面);

二是如何设置默认参数。

当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。

使用默认参数有什么好处?最大的好处是能降低调用函数的难度。

默认参数降低了函数调用的难度,而一旦需要更复杂的调用时,又可以传递更多的参数来实现。无论是简单调用还是复杂调用,函数只需要定义一个。

有多个默认参数时,调用的时候,既可以按顺序提供默认参数也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。比如调用enroll(‘Adam’,
‘M’, city=‘Tianjin’),意思是,city参数用传进去的值,其他默认参数继续使用默认值。

默认参数很有用,但使用不当,也会掉坑里。默认参数有个最大的坑,演示如下:

先定义一个函数,传入一个list,添加一个END再返回:

def add_end(L=[]):
    L.append('END')
    return L
当你正常调用时,结果似乎不错:

>>> add_end([1, 2, 3])
[1, 2, 3, 'END']
>>> add_end(['x', 'y', 'z'])
['x', 'y', 'z', 'END']
当你使用默认参数调用时,一开始结果也是对的:

>>> add_end()
['END']
但是,再次调用add_end()时,结果就不对了:

>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
很多初学者很疑惑,默认参数是[],但是函数似乎每次都“记住了”上次添加了'END'后的list。

原因解释如下:

Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。

 定义默认参数要牢记一点:默认参数必须指向不变对象!
要修改上面的例子,我们可以用None这个不变对象来实现:

def add_end(L=None):
    if L is None:
        L = []
    L.append('END')
    return L
现在,无论调用多少次,都不会有问题:

>>> add_end()
['END']
>>> add_end()
['END']
为什么要设计str、None这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

可变参数

在Python函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数

关键字参数

可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:',kw)

person('Adam', 45, gender='M', job='Engineer')

它可以扩展函数的功能。比如,在person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。

和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去


dict1 = ('001’: 'python', '002’: ’ java', '003’: ’c语言’,’004' :”go语言” }

def func(**kwargs): 
  print(kwargs)

func(**dict1)

#底层实现过程


1. 将字典进行拆包,拆成单个键值对,将字典拆包成关键字参数的形式
2. 此时func里面的参数都是关键字参数
3.将关键字参数再进行一次装包动作

命名关键字参数

def person(name, age, *, city, job):
    print(name, age, city, job)

和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。比如只希望检查是否有city和job参数

person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了

def person(name, age, *args, city, job):
    print(name, age, args, city, job)

命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:

参数组合

在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

在这里插入图片描述

函数的调用

函数的使用分为定义阶段与调用阶段,定义函数时只检测语法,不执行函数体代码,函数名加括号即函数调用,只有调用函数时才会执行函数体代码

调用函数发生的事

1、通过函数名找到函数的内存地址
2、然后加口号就是在触发函数体代码的执行

在这里插入图片描述

在函数内引用全局变量,对于可变变量,则需要在函数内部声明才能更改

在这里插入图片描述

函数的返回值

在程序开发中,有时候,会希望一个函数执行结束后(举例子:温度计函数,要返回给我>们当前的温度),告诉调用者一个结果,以便调用者针对具体的结果作出后续的处理
返回值是函数完成工作后,最后给调用者的一个结果
在函数中使用return关键字可以返回结果
调用函数一方,可以使用变量来接收函数的返回结果
如果函数没有返回值,接收的变量的值为None

在这里插入图片描述

扩展

函数调用被提出支持任意次数的的拆包,而不仅仅只进行一次:

>>> print(*[1], *[2], 3)
1 2 3
>>> dict(**{'x': 1}, y=2, **{'z': 3})
{'x': 1, 'y': 2, 'z': 3}

拆包操作被提出在元组、列表、集合和字典形式内允许。

>>> *range(4), 4
(0, 1, 2, 3, 4)
>>> [*range(4), 4]
[0, 1, 2, 3, 4]
>>> {*range(4), 4}
{0, 1, 2, 3, 4}
>>> {'x': 1, **{'y': 2}}
{'x': 1, 'y': 2}

在字典中,对于相同的键,其后面对应的值会覆盖(override)之前对应的值

>>> {'x': 1, **{'x': 2}}
{'x': 2}

>>> {**{'x': 2}, 'x': 1}
{'x': 1}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值