欢迎来到 wabc.cc 官方网站!

python学习中语法中关于函数的介绍

来源:推荐文章 / 时间:2025-12-21

  函数

  一. 引言

  a) 概念:函数是完成指定或者特定任务的一组代码。在面向对象编程的类中函数通常被称为方法。不同的函数在程序中会扮演不同的角色完成不同的功能。

  b) 作用:

  i. 函数的使用可以提高代码的复用性,省去重复代码的编写,提升程序代码重复利用率。

  ii. 函数能封装内部的实现,保护内部的数据。通常,我们可以将函数看做成一个“黑盒子”。往往函数的使用者并不是函数的编写者,函数的使用者并不需要对函数内部实现和行为进行考虑,可以将精力更多投入到自身业务逻辑的设计中。只有函数的编写者才需要考虑函数内部的实现细节,如何暴露对外的接口,返回什么样的数据值,也就是API的设计。

  iii. 提高程序的可读性。使得程序模块化。从一盘散散沙变成整齐队列。

  iv. 提高程序的可维护性。案例:打印一个不同字符组成的分隔符。

  二. 函数基础

  a) 可以将函数抽象成现实生活中的工具。工具需要先制作出来才可以使用。函数也是一样的需要先进行函数的定义,然后方可进行函数调用。

  b) 函数定义语法结构:

  def 函数名(参数):

  # 内部代码

  return 表达式

  c) Return:return不是必须要写的,如果需要则写,不需要可不写。Return后面什么都不跟表示return None。一旦函数执行过程中遇到了return语句,那么函数中return后面的所有语句将不会执行,函数执行结束。

  d) 函数调用:

  i. 语法结构:函数名(参数值)

  ii. 注意事项:

  1. 参数之前使用逗号隔开

  2. 由于python动态语言的特点,函数调用时填写的参数,python不会对参数类型进行检查,如果函数调用中的参数不符合函数内部运行机制的话,会报错。

  e) 参数:现实函数和调用者之间的交互

  i. 函数-》工具:使用ATM取钱时需要传入密码数据。

  ii. 实参和形参的概念。

  iii. 函数调用时,实参会传值给形参

  1. 注意:

  a) Python的函数参数传递实际上传递的是实参的地址

  b) Python中的参数类型分为可变数据类型和不可变数据类型。

  2. Test:使用可变数据类型和不可变数据类型的数据作为参数

  a = 1

  def func(a):

  print("在函数内部修改之前,变量a的内存地址为: %s" % id(a))

  a = 2

  print("在函数内部修改之后,变量a的内存地址为: %s" % id(a))

  print("函数内部的a为: %s" % a)

  print("调用函数之前,变量a的内存地址为: %s" % id(a))

  func(a)

  print("函数外部的a为:%s" % a)

  打印结果为:

  调用函数之前,变量a的内存地址为: 1401140288

  在函数内部修改之前,变量a的内存地址为: 1401140288

  在函数内部修改之后,变量a的内存地址为: 1401140320

  函数内部的a为: 2

  函数外部的a为:1

  解释:作为参数,a被传入函数时,将数字对象1的地址传递给了函数内部的a。执行第一句内部代码时,此时内部的a和外面的a其实是一个东西,因此打印出了同样的内存地址。而当a=2被执行后,由于整数是不可变的数据类型,所以创建了一个新的内部变量a,并赋值2,将数字对象2的内存地址赋给变量a。我们知道,首先,赋值语句具有创建新变量的功能。

  刚才说的是不可变类型参数,如果是可变类型的,比如列表呢?

  a = [1, 2, 3]

  def func(b):

  print("在函数内部修改之前,变量b的内存地址为: %s" % id(b))

  b.append(4)

  print("在函数内部修改之后,变量b的内存地址为: %s" % id(b))

  print("函数内部的b为: %s" % b)

  print("调用函数之前,变量a的内存地址为: %s" % id(a))

  func(a)

  print("函数外部的a为:%s" % a)

  执行结果是:

  调用函数之前,变量a的内存地址为: 34875720

  在函数内部修改之前,变量b的内存地址为: 34875720

  在函数内部修改之后,变量b的内存地址为: 34875720

  函数内部的b为: [1, 2, 3, 4]

  函数外部的a为:[1, 2, 3, 4]

  三. 参数类型:python函数的参数定义灵活度很大,可以定义位置参数,默认参数,动态参数,关键字参数等。

  a) 位置参数

  i. 概念:也叫做必传参数,在函数调用时必须明确提供的参数,切参数顺序个数必须和形参保持一致。但是如果在函数调用的时候给实参指定参数名,那么位置参数的顺序可以不同。

  b) 默认参数:如果给某个参数提供一个默认值,该参数就是默认参数。在函数调用时,可以给默认参数传递一个值,也可以使用默认值。

  i. Test:

  def power(x, n = 2):

  return x**n

  ret1 = power(10) # 使用默认的参数值n=2

  ret2 = power(10, 4) # 将4传给n,实际计算10**4的值

  ii. 使用默认参数的注意事项:

  1. 默认参数必须在顺序参数后面

  2. 默认参数尽量指向不变的对象

  a) Test:

  下面是国内某上市互联网公司Python面试真题:

  def func(a=[]):

  a.append("A")

  return a

  print(func())

  print(func())

  print(func())

  不要上机测试,仅凭代码,你能说出打印的结果吗?

  很多同学可能会说,这还不简单,肯定是下面的结果啊:

  ['A']

  ['A']

  ['A']

  真的是这样吗?错了!真正的结果是:

  ['A']

  ['A', 'A']

  ['A', 'A', 'A']

  Why?为什么会这样?

  因为Python函数体在被读入内存的时候,默认参数a指向的空列表对象就会被创建,并放在内存里了。因为默认参数a本身也是一个变量,保存了指向对象[]的地址。每次调用该函数,往a指向的列表里添加一个A。a没有变,始终保存的是指向列表的地址,变的是列表内的数据!我们可以测试一下:

  def func(a=[]):

  print("函数内部a的地址为:%s" % id(a))

  a.append("A")

  return a

  b = func()

  print('此时b的值为:%s' % b)

  print("函数外部b的地址为:%s" % id(b))

  print("-------------")

  c = func()

  print('此时c的值为:%s' % c)

  print("函数外部c的地址为:%s" % id(c))

  print("-------------")

  d = func()

  print('此时d的值为:%s' % d)

  print("函数外部d的地址为:%s" % id(d))

  打印结果是:

  函数内部a的地址为:39287880

  此时b的值为:['A']

  函数外部b的地址为:39287880

  -------------

  函数内部a的地址为:39287880

  此时c的值为:['A', 'A']

  函数外部c的地址为:39287880

  -------------

  函数内部a的地址为:39287880

  此时d的值为:['A', 'A', 'A']

  函数外部d的地址为:39287880

  那么如何避免这个问题呢?

  使用不可变的数据类型作为默认值!

  def func(a=None):

  # 注意下面的if语句

  if a is None:

  a = []

  a.append("A")

  return a

  print(func())

  print(func())

  print(func())

  将默认参数a设置为一个类似None,数字或字符串之类的不可变对象。在函数内部,将它转换为可变的类型,比如空列表。这样一来,不管调用多少次,运行结果都是['A']了。

  iii. 动态参数

  1. 概念:函数调用时传入的参数可以是动态数量的,可以为任意个,甚至是0个。

  2. 分类:重点是*的个数,*后的名字任意。动态参数必须放在所有位置参数和动态参数后面。

  a) *args:可以接受任意个参数。调用时,会将实参打包成一个元组传给形参。如果参数是一个列表,则会将整个列表当做一个参数传入。

  i. Test

  def func(*args):

  for arg in args:

  print(arg)

  func('a', 'b', 'c')

  li = [1, 2, 3]

  func(li)

  ii. 上述案例中,我们本意是将li这个列表中的元素作为参数进行传值,但是实际却是将列表本身作为一个整体进行了传递。如果想要将一个序列类型的对象(元组,列表,字符串,字典)的元素依次作为参数进行传递,则可以在序列对象前加一个*即可。如果参数是字典,则会将字典所有的key逐一传递进去。

  1. Test:

  def func(*args):

  for arg in args:

  print(arg)

  li = [1, 2, 3]

  func(*li)

  b) **kwargs:表示接受键值对的动态参数,数量任意。调用时,会将参数打包成一个字典。

  i. Test:

  def func(**kwargs):

  for kwg in kwargs:

  print(kwg, kwargs[kwg])

  print(type(kwg))

  func(k1='v1', k2=[0, 1, 2])

  运行结果是:

  k1 v1

  

  k2 [0, 1, 2]

  

  ii. 而如果我们这样传递一个字典dic呢?我们希望字典内的键值对能够像上面一样被逐一传入。

  1. Test:

  def func(**kwargs):

  for kwg in kwargs:

  print(kwg, kwargs[kwg])

  dic = {

  'k1': 'v1',

  'k2': 'v2'

  }

  func(**dic)

  iv. 关键字参数:关键字参数前面需要一个特殊分隔符*和位置参数及默认参数分隔开来。*后面的参数被视为关键字参数。在函数调用时,关键字参数必须传入参数名。

  1. Test:

  def student(name, age, *, sex):

  pass

  student(name="jack", age=18, sex='male')

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

  def student(name, age=10, *args, sex, classroom, **kwargs):

  pass

  student(name="jack", age=18, sex='male', classroom="202", k1="v1")

  总结:参数定义顺序:位置参数,动态参数,关键字参数,动态参数


相关产品

在线客服
微信联系
客服
扫码加微信(手机同号)
电话咨询
返回顶部