Python3 函数

一、定义函数

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号圆括号 (),内容以冒号 : 起始
  • 任何传入参数和自变量必须放在圆括号 () 中间,圆括号之间可以用于 定义参数
  • 函数的第一行语句可以 选择性 地使用文档字符串—用于 存放函数说明
  • 结束函数时,选择性 地返回一个值给调用方,不带表达式的相当于返回 None
# 一般形式
def function_name(parameter_list):
	function_body
	return [expression] 

二、调用函数

  • 函数的基本结构完成,可以通过另一个函数 调用执行 ,也可以直接从 Python 3.x 命令提示符执行
# 该实例演示计算面积函数
def area(width, height):
    return width * height


def print_hello(name):
    print("Hello", name)


print_hello("World")
w = 4
h = 5
print("width = %d, height = %d, area = %d" % (w, h, area(w, h)))
-> Hello World
-> width = 4, height = 5, area = 20

三、参数传递

  • Python 3.x 中一切都是对象,严格意义上 不能说 值传递还是引用传递,应该说 传不可变对象和传可变对象
  • 无论传递的参数是 可变 还是 不可变 ,只要 针对参数 使用 赋值语句,会在 函数内部 修改 局部变量的引用不会影响到 外部变量的引用
  • 如果传递的参数是 可变类型,在函数内部,使用 方法 修改了数据的内容,同样会影响到外部的数据
  • 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)
  • 不可变数据(3 个):Number(数字)、Tuple(元组)、String(字符串)
# 该实例演示传不可变对象
def ChangeInt( a ):
    a = 10
    print(a)
 
 
b = 2
ChangeInt(b)
print(b)
-> 10
-> 2
# 该实例演示传可变对象
def changeme(mylist):
    """修改传入的列表"""
    mylist.append([1, 2, 3, 4])
    print("函数内取值: ", mylist)
    return


mylist = [10, 20, 30]
changeme(mylist)
print("函数外取值: ", mylist)
-> 函数内取值:  [10, 20, 30, [1, 2, 3, 4]]
-> 函数外取值:  [10, 20, 30, [1, 2, 3, 4]]
  • 列表变量执行 += 时,本质上是在调用列表变量的 list.extend() 方法,不会修改变量的引用
def demo(num, num_list):

    print("函数内部代码")

    # num = num + num
    num += num
    
    # num_list.extend(num_list) 由于是调用方法,所以不会修改变量的引用
    # 函数执行结束后,外部数据同样会发生变化
    num_list += num_list

    print(num)
    print(num_list)
    print("函数代码完成")


gl_num = 9
gl_list = [1, 2, 3]
demo(gl_num, gl_list)
print(gl_num)
print(gl_list)
-> 函数内部代码
-> 18
-> [1, 2, 3, 1, 2, 3]
-> 函数代码完成
-> 9
-> [1, 2, 3, 1, 2, 3]

四、参数类型

  • 必需参数 ,需以正确的顺序传入函数,调用时的数量必须和声明时的一样
def printme(str):
    """打印任何传入的字符串"""
    print(str)
    return


printme("Hello")
-> Hello
  • 关键字参数 ,允许函数调用时参数的顺序与声明时不一致,因为解释器能够用参数名匹配参数值
def printinfo(name, age):
    """打印任何传入的字符串"""
    print("名字: ", name)
    print("年龄: ", age)
    return


printinfo(age=70, name="中国")
-> 名字:  中国
-> 年龄:  70
  • 缺省参数 ,必须保证 带有默认值的缺省参数 在参数列表 末尾 ,否则会报错
  • 在调用函数时,如果 没有 传递参数,则会使用 默认参数
  • 在调用函数时,如果 有多个 缺省参数,则需要 指定参数名
  • 常见的值 设置为参数的 缺省值,从而 简化函数的调用
  • 参数的值 不确定,则不应该设置默认值,具体的数值在调用函数时,由外界传递
gl_num_list = [6, 3, 9]

# 默认就是升序排序,因为这种应用需求更多
gl_num_list.sort()
print(gl_num_list)

# 只有当需要降序排序时,才需要传递 `reverse` 参数
gl_num_list.sort(reverse=True)
print(gl_num_list)
def printinfo(name, age=18):
    """打印任何传入的字符串"""
    print("名字: ", name)
    print("年龄: ", age)
    return


printinfo(age=50, name="中国")
print("-" * 24)
printinfo(name="中国")
-> 名字:  中国
-> 年龄:  50
-> ------------------------
-> 名字:  中国
-> 年龄:  18
  • 不定长参数(多值参数),一个函数能处理比当初声明时 更多的参数
# args 是 arguments 的缩写,有变量的含义
# kwargs 可以记忆 键值对参数, kw 是 keyword 的缩写
def functionname(var, *args, **kwargs):
    """函数_文档字符串"""
    function_suite
    return [expression]
  • 星号 * 的参数会以元组 (tuple) 的形式导入,存放所有未命名的变量参数
def printinfo(arg1, *args):
    """打印任何传入的参数"""
    print("输出: ")
    print(arg1)
    print(args)


printinfo(70, 60, 50)
-> 输出: 
-> 70
-> (60, 50)
  • 如果在函数调用时,没有指定参数,它就是 一个空元组 ,可以 不传递 未命名的变量
def printinfo(arg1, *args):
    """打印任何传入的参数"""
    print("输出: ")
    print(arg1)
    for var in args:
        print(var)
    return


printinfo(70, 60, 50)
printinfo(70, (60, 50))
-> 输出: 
-> 70
-> 60
-> 50
-> 输出: 
-> 70
-> (60, 50)
  • 星号 ** 的参数会以字典 (dictionary) 的形式导入,存放所有未命名的变量参数
def printinfo(arg1, **kwargs):
    """打印任何传入的参数"""
    print("输出: ")
    print(arg1)
    print(kwargs)


printinfo(1, a=2, b=3)
-> 输出: 
-> 1
-> {'a': 2, 'b': 3}
  • 在调用带有 多值参数 的函数时,如果希望:
    • 将一个 元组变量,直接传递给 args
    • 将一个 字典变量,直接传递给 kwargs
  • 就可以使用 拆包,简化参数的传递,拆包 的方式是:
    • 元组变量前,增加 一个 *
    • 字典变量前,增加 两个 *
def demo(*args, **kwargs):
    print(args)
    print(kwargs)


# 需要将一个元组变量/字典变量传递给函数对应的参数
gl_nums = (1, 2, 3)
gl_xiaoming = {"name": "小明", "age": 18}

# 会把 num_tuple 和 xiaoming 作为元组传递个 args
# demo(gl_nums, gl_xiaoming)
demo(*gl_nums, **gl_xiaoming)
  • 如果单独出现星号 * / ** 后的参数必须用 关键字传入
>>> def f(a, b, *, c):
...     return a + b + c
... 
>>> f(1, 2, 3)   # 报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 2 positional arguments but 3 were given
>>> f(1, 2, c=3) # 正常
6

五、匿名函数

  • lambda 只是一个表达式,函数体比 def 简单很多
  • lambda 的主体是 一个表达式 ,而不是一个代码块,仅仅能在 lambda 表达式中封装有限的逻辑进去
  • lambda 函数拥有自己的命名空间,且 不能访问 自己参数列表之外或全局命名空间里的参数
  • lambda 函数看起来只能写一行,却不等同于 C OR C++ 的内联函数,后者的目的是调用小函数时 不占用 栈内存从而增加运行效率
# 一般形式
# 可以使用关键字参数
# 也可以设定默认参数
lambda [arg1 [,arg2,.....argn]]:expression
# 该实例演示两数相加
sum = lambda arg1, arg2: arg1 + arg2

print("相加后的值为 : ", sum(10, 20))
print("相加后的值为 : ", sum(20, 20))
-> 相加后的值为 :  30
-> 相加后的值为 :  40

六、返回函数

  • 选择性地向调用方返回一个表达式,不带参数值返回 None
  • 返回值 是函数执行完成后,最后 给调用者的 一个结果
  • 调用函数,可以使用变量来 接收函数 的返回结果
  • 变量的数量需要和元组中的元素数量 保持一致
# 一般形式
# 可以返回多个值,以元组的方式返回
return [expression]
# 该实例演示两数相加
def sum(arg1, arg2):
    """返回2个参数的和"""
    total = arg1 + arg2
    print("函数内:", total)
    return total


total = sum(10, 20)
print("函数外:", total)
-> 函数内: 30
-> 函数外: 30

七、递归函数

  • 函数内部可以 调用其他函数 ,当然在函数内部也可以 调用自己
  • 函数内部的 代码 是相同的,只是针对 参数 不同,处理的结果不同
  • 参数满足一个条件 时,函数 不再 执行,通常被称为 递归的出口,否则 会出现死循环
def sum_numbers(num):

    print(num)
        
    # 递归的出口很重要,否则会出现死循环
    if num == 1:
        return
        
    sum_numbers(num - 1)


sum_numbers(3)
# 该实例演示计算数字累加
def sum_numbers(num):

    if num == 1:
        return 1
    
    # 假设 sum_numbers 能够完成 num - 1 的累加
    temp = sum_numbers(num - 1)

    # 函数内部的核心算法就是 两个数字的相加
    return num + temp

print(sum_numbers(2))

八、变量作用域

  • 程序的变量 并不是 在哪个位置都可以访问的,访问权限 取决于 这个变量是在哪里赋值的

  • 变量的作用域 决定 在哪一部分程序可以访问哪个特定的变量名称

  • 变量引用:

    • 变量 和 数据 是分开存储的
    • 数据 保存在内存中的一个地址
    • 变量 保存着数据在内存中的地址,记录数据的地址叫做 引用
    • 函数的 实参 / 返回值 都是是靠 数据引用 来传递的
    • 使用 id() 函数可以查看变量中保存数据所在的 内存地址
    • 如果变量已经被定义,继续给该变量赋值的时候,本质上是 修改变量的数据引用
    • 变量 不再 对之前的数据引用,变量 改为 对新赋值的数据引用
  • 局部变量:

    • 局部变量 是在 函数内部 定义的变量,只能在函数内部使用
    • 不同的函数 可以定义 相同名称 的局部变量,彼此之间不会产生任何影响
    • 作用:在函数内部使用,临时 保存 函数内部需要使用的数据
    • 生命周期变量 从执行函数 被创建 到 执行结束 被系统回收 的过程
  • 全局变量:

    • 全局变量 是在 函数外部定义 的变量,所有函数 内部 都可以使用这个变量
    • 在其他的开发语言中,大多 不推荐使用全局变量,因为 可变范围太大,导致程序不好维护!
    • 为了保证所有的函数都能够 正确使用 到全局变量,应该将全局变量 定义在其他函数的上方,因为程序是自顶向下进行
  • 变量细节:

    • 在函数内部,可以通过全局变量的引用获取对应的数据,但不允许使用赋值语句直接修改全局变量的引用
    • 在函数内部,可以定义一个局部变量与全局变量的名称相同,根据就近原则,优先引用函数内部匹配的局部变量,再引用函数外部匹配的全局变量
    • 变量作用域优先级:L (Local)局部作用域 –> E (Enclosing)闭包函数外的函数中 –> G (Global)全局作用域 –>B (Built-in)内置作用域
    • 在函数内部,如果需要修改全局变量,可以使用 global 进行变量声明
    • 为了避免局部变量和全局变量出现混淆,在定义全局变量时,某些公司会有些特殊的开发要求,例如:全局变量名前应该增加 g_ 或者 gl_ 的前缀
    • 对于全局变量的格式,各公司可能会有些差异,建议主动询问领导公司内部的代码格式有没有什么特殊要求?
代码结构示意图
Shebang
导入模块
全局变量
函数定义
执行代码
g_count = 0  # 全局作用域
def outer():
    o_count = 1  # 闭包函数外的函数中
    
    def inner():
        i_count = 2  # 局部作用域
# 该实例演示访问全局变量和局部变量
total = 0  # 这是一个全局变量


def sum(arg1, arg2):
    """返回2个参数的和"""
    total = arg1 + arg2  # total在这里是局部变量.
    print("函数内是局部变量:", total)
    return total


sum(10, 20)
print("函数外是全局变量:", total)
-> 函数内是局部变量: 30
-> 函数外是全局变量: 0
# 当内部作用域想修改外部作用域的变量时,就要用到 global 和 nonlocal 关键字
# 该实例演示修改全局变量,需要 global 关键字
num = 1


def fun():
    global num  # 使用 global 关键字声明
    print(num)
    num = 123
    print(num)


fun()
print(num)
-> 1
-> 100
-> 100
# 该实例演示修改嵌套作用域 (enclosing 作用域,外层非全局作用域),需要 nonlocal 关键字
def outer():
    num = 10
    
    def inner():
        nonlocal num  # 使用 nonlocal 关键字声明
        num = 100
        print(num)
         
    inner()
    print(num)
    

outer()
-> 100
-> 100

九、简单装饰器

  • 不改变 当前函数的情况下, 给其 增加 新的功能,回调函数和返回函数的实例就是装饰器
def log(pr):  # 将被装饰函数传入
    
    def wrapper():
        print("-" * 24)
        return pr()  # 执行被装饰的函数

    return wrapper  # 将装饰完之后的函数返回(返回的是函数名)


@log
def pr():
    print("Hello World")


pr()
-> ------------------------
-> Hello World
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值