python之函数详述

1.函数前言

1.1定义

函数是具有一定功能的代码容器。

1.2使用函数的好处

1.组织结构清晰,可读性增强
2.可重复使用,减少代码冗余
2.可扩展性提高

2.使用函数

将一段经常使用的代码封装成函数,在需时直接调用。

2.1使用规则

# 函数必须先定义后使用。

定义阶段:申请内存空间将函数体放进去,将内存地址绑定给函数名。
调用阶段:通过函数名得到函数体的地址,加上括号执行函数体代码。
定义阶段时只检测语法不执行代码,调用阶段才会执行函数体,逻辑错误才会被检查到。

2.2语法结构

def 函数名(参数1,···):  # 函数的定义
    """
    为函数说明
    """
    
    代码1...
    代码2...
    
    return 值

函数名(参数1,参数2)  # 调用阶段

2.3语法检测

# 函数定义阶段只检测语法
def func():
	qwe  # 语法上没有错误
         # 运行程序 进程已结束,退出代码为 0   
        
        
# 函数调用阶段解释代码并执行
func()   

"""
NameError: name 'qwe' is not define
没有定义 qwe 逻辑错误  不调用就不会出错,一调用就出错
"""

3.变量名与函数名

3.1区别

变量名指向变量值的内存地址
函数名指向函数体的内存地址

print(变量名)------>  被处理  ----->  显示内存地址存放的值 不能在加()
print(函数名)------>  没处理 ------>  显示值所在内存的地址 加()进行处理

函数可以加括号是语法定义的
函数名---> 内存地址 +()----> ,找到内存地址处理代码得到结果


def aa():
    print(1)
    
print(aa)   # <function aa at 0x00000000012F70D0>

3.2引用函数

将函数内存地址引用给其他变量。
其他的函数加上括号可以调用函数。

def aa():
    print(1)
    
bb = aa  # 内存地址绑定给 bb 这是一个没有被处理的程序
bb()     # 加上括号,找到内存地址处理代码得到结果
     # 显示为 1

4.函数的类型

4.1分类

# 函数的三种类型:
1.无参函数 函数在定义阶段括号内没有参数
2.有参函数 函数在定义阶段括号内有参数
3.空函数   函数在定义阶段括号内没有参数,函数体为空(pass 或 ···)

# 函数的 参数 与 返回值 取决于是否需要对函数传入值与需要返回值。

4.2无参函数

# 无参 --------> 不需要传入值
def aa():
    x = 10
    y = 10
    print(x + y)  
aa()

4.3有参函数

# 有参 --------> 需要传入值
def bb(x,y):
    print(x + y)
    
bb(10, 10)

4.4空函数

# 空函数
def cc():
    pass  # 第一种, 兼容python 2

cc()

def dd():
	...  # 第二种
    
dd()

4.5函数的调用

def aa():
	return 123

aa()  # 直接调用
res = aa() * 10  # 表达式
print(aa( ))  # 作为其他函数的参数

5.函数返回值

5.1返回值

# 关键字 return
一个函数中可以有多个return,只要执行一次,立刻结束当前所在函数的运行,
并将return 后的值设置为函数的返回值。

1.没有 return 关键字 返回值 默认为None 
2.只有 return 关键字 后面没有跟内容 返回默认值 默认为None
	-->等同  return None
3.return   值  #  返回单个值
4.return   值1,值2,值3  # 返回多个值(元组类型)

# return 结束当前函数
def aa():
    return  # 直接结束函数的运行
	print(666)	

aa()
      
# 举例
def bb():
    print(123)  # 123
    return

bb()   

5.2不写return

def aa():
	pass    

print(aa())  # None

5.3只写return

def aa():
	return   

print(aa())  # None

5.3返回单个值

def aa():
    return 1

print(aa())  # 1

5.4返回多个值

def aa():
    return 1, 2, 3

print(aa())  # (1, 2, 3)

5.5获取多个返回值

# 返回值 赋值给 变量
def ee():
    return 1, 2, 3

num1, num2, num3 = ee()  # 解压赋值

6.参数类型提示

6.1设置说明

函数的参数可以是任意类型但是,但传入的值不符合要求,就会报错
参数设置提示,
:参数提示    ->   返回值提示
def 函数名 (参数1:int, 参数2:‘必须传入字符串’)-> “返回值是整型”

6.2文字说明

def aa(x: '传入字符串', y: '传入整型') -> '返回值为字符串':
    z = x * y
    return z

print(aa('asd', 5))

6.3简易提示

def aa(x:str, y: int) -> str:
    z = x * y
    return z

print(aa('asd', 5))

7.实参与形参

7.1定义

# 形参:函数定义阶段,括号内指定的参数(变量名)
  def func(a, b):    # a和b就是func的形参
    pass

# 实参:函数调用阶段,括号内传入的值(变量值)
	func(1, 2)    # 数据1和2就是func的实参

# 两者之间的关系:
函数调用阶段 ----> 实参的值绑定给形参名
函数调用完毕 ----> 解除绑定

# 形参与实参的表现形式:
形参的表现形式只有一种就是变量名
实参的表现形式有很多种(但是把握核心 就是 数据值)

img

7.2实例

def aa(x, y):  #  x, y   是形参
    z = x + y

aa(10, 20)     #  10, 20 是实参

a = 10
b = 20
aa(a, b)       #  a, b   是实参

8.位置形参与位置实参

8.1定义

# 位置形参:定义函数阶段,按照从左到右的顺序依次定义的形参
特点:形参必须被传值

# 位置实参:在函数调用阶段,按照从左到右的顺序依次传入的值
特点:按顺序依次为形参赋值

# 举例:定义一个可以比较大小的函数(传入两个值,返回比较大的那个)
  def my_max(a, b):  # a,b即位置形参
      print(a, b)  
     if a > b:
          return a
      else:
          return b
  res = my_max(111, 222)  # 111,222 即为位置实参
  print(res)

image-20220419154345685

8.2实例

def aa(x, y, z):  # (不能多,不能少,一个萝卜一个坑)
    print(x, y, z)
 
aa(1, 2, 3)  # 实参与形参的个数相匹配
aa(3, 2, 1)

9.关键字实参

9.1定义

# 关键字实参:在函数调用阶段,按 key = value 的格式传入值
特点:不用记住形参的顺序,可打乱顺序赋值 

# 举例:
  def my_max(a, b):
      print(a, b)
      if a > b:
          return a
      else:
          return b
  res1 = my_max(a=222, b=111)
  print(res1)

image-20220419154456142

9.2实例

def bb(x, y, z):  # 指定赋值
    print(x, y, z)
   
bb(x=1,z=3,y=2)  # 顺序可乱
bb(z=3,x=1,y=2)            

9.3混用

# 位置实参与关键字实参在一起混用时:
1.关键字实参不能位置参数前
2.不能为同一个形参多次赋值

# 记忆口诀:越简单的格式写在前面,越复杂的格式写在后面


# 例子1
def aa(x, y, z):
    print(x, y, z)

aa(x=1, 2, z=3)  # 关键字 在 位置实参 前面
# SyntaxError: positional argument follows keyword argument

# 例子2
def aa(x, y, z):
    print(x, y, z)

aa(1, 2, 3, z=3)  # z 被赋值两次
# TypeError: aa() got multiple values for argument 'z'

10.默认形参

10.1定义

# 在函数定义阶段,为形参赋值,称为默认形参,如果传值,以实参值为准。
特点:定义阶段已经为形参赋值,在调用阶段可以不用为其传值

# 位置形参和默认形参混用:
1.位置形参在前,位置实参在后面
2.默认形参只在函数定义阶段赋值一次
3.默认形参的值推荐指向不可变类型

def register(name, age, sex='male'):  # 默认形参性别男
    print('%s:%s:%s' % (name, age, sex))
    
register('jason', 18)  # 默认形参在调用阶段不给值就使用默认值
register('tony', 20)  
register('lili', 19, 'female')  # 默认形参在调用阶段也可以给值,给值就用值

10.2使用场景

# 默认参数使用场景

def func(name, age, gender='male'):
    print(name, age, gender)


func('kid', 18)
func('qz', 19)
func('qq', 19, 'female')


# 在不对z传值的情况下的利用
def func(key, value, is_dict=True):
    if is_dict:  # is_dict值不变
        dic = {}  # dic 为一个空字典
    dic[key] = value 
    print(dic)

func('西瓜', 15)

11.注意点

11.1共用空间

# 关键字参数为一个空的列表。

def aa(x=[]):
    x.append(1)
    print(x)


aa()  # 第一次调用函数,创建一个空列表,1被添加进y的列表内    [1]
aa()  # 第二次调用函数,不在创建空列表,1被添加进原来的列表内  [1, 1]
aa()  # 第三次调用函数,不在创建空列表,1被添加进原来的列表内  [1, 1, 1]


a = [1, 2, 3]  # 申请一个内存空间   把值[1, 2, 3]放进去  绑定给 变量名 a
b = a  # 将a 的内存空间地址给 b 引用   a b 同时指向 内存地址
a = 200  # 申请一个内存空间   把值200放进去  绑定给 变量名 a , 原来的绑定断开
print(b)  # b 不受影响

k = [1, 2, 3]           # 申请一个内存空间   把值[1, 2, 3]放进去  绑定给 变量名 k
v = k                   # 将k 的内存空间地址给 v 引用   k v 同时指向 内存地址
k.append(200)           # 在内存空间将值200添加进去
print(v)                # v 也更随着变量

11.2值的引用

x = 100  # 申请一个内存空间   把值100放进去  绑定给 变量名 x
y = x  # 将x 的内存空间地址给 y 引用,   x y 同时指向 内存地址


def func():
    print(y)  # y 不受影响


x = 200  # 申请一个内存空间   把值200放进去  绑定给 变量名 x , 原来的绑定断开

func()

12.可变长参数

12.1*与**

# 在位置形参中带 * 
* 将溢出的位置实参汇总成元组,赋值给紧跟其后的变量。

# 在关键字形参中带 **
** 将溢出的位置实参汇总成字典,赋值给紧跟其后的变量。

12.2*的使用

def aa(x, *y):
    print(x, y)

aa(1)  # 1 ()     没有溢出就输出空元组
aa(1, 2)  # 1 (2,)   
aa(1, 2, 3)  # 1 (2, 3)

12.3**的使用

# 实参与形参值长度相等

def aa(x, **y):      # 字典是无序的
    print(x, y)

aa(1)  # 1 {}
aa(1, a=1, b=2)  # 1 {'a': 1, 'b': 2}

12.4实参中使用

# 在实参中带*  
*   能被for循环的  字符串 ,元组,列表等都使用
在关实参中带 **:**  key=value 类型

# 实参与形参值长度相等

def aa(x, y, z):
    print(x, y, z)

aa(*[1, 2, 3])   # 1 2 3   
aa(*'qwe')       # q w e


# 实参与形参值长度相等
def aa(x, y, z):
    print(x, y, z)  

aa(**{'x': 5, 'y': 4.5, 'z': 5.2})

12.5合用

# 约定俗成:
将 * 赋值给 args
将 ** 赋值 给 kwargs 

参数的长度指定参数的个数,长度固定。
可变长参数的长度不固定。

(*args, **kwargs)接收任意长度的的参数。


def num(*args, **kwargs):
    x1, x2 = args  # 元组的值解压给 x1 x2
    y1, y2 = kwargs  # 字典的值解压复制给 y1 y2
    print(x1, x2, y1, y2)  # 10 20 30 40


num(*(10, 20), **{'y': 30, 'a': 40})

12.6混用

# 混用,位置实参*args在前,位置实参 **kwargs 在后。
def num(x, *args, **kwargs):
    print(x, args, kwargs)


num(10, *(20, 30), **{'西瓜': 1})

13.命名关键字参数(基本不用)

# 在位置参数形参后面看起来像位置形参的参数。(自己不会这样去写代码,一般出现在面试题中)

# 几乎用不上
def num(x, *args, y):  # *args 后面的y 就不能能是位置形参,y在这为命名关键字参数
    print(x, args, y)  # 通过位置实参,赋值给 y


num(10, *(20, 30), y=40)  # 10 (20, 30) 40
posted @ 2021-11-15 21:31  90啊  阅读(100)  评论(0编辑  收藏  举报