一木.溪桥学Python-09:函数的返回值、函数的作用域、作用域的优先级、递归函数、内置文件中常用方法、匿名函数lambda、高阶函数

本文介绍了Python编程中的函数概念,包括函数的返回值、作用域及其优先级、递归函数的使用。还探讨了内置文件方法如range()、zip()、map()和filter(),以及匿名函数lambda和高阶函数的应用。通过实例讲解了值类型与引用类型的差异,以及Python中函数的值传递和引用传递特性。

一木.溪桥 在Logic Education跟Amy学Python

12期:Python基础课
一木.溪桥学Python-09:函数的返回值、作用域、作用域的优先级、递归函数、内置文件中常用方法、匿名函数lambda、高阶函数
日期:2021年1月4日

学习目标:

  • 函数的返回值
  • 函数的作用域
  • 作用域的优先级
  • 递归函数
  • 内置文件中常用方法
  • 匿名函数lambda
  • 高阶函数

学习内容:

函数的返回值

  • 当两个函数之间,想要互相使用到内部变量时,就可以应用到函数的返回值。

  • 函数的返回值定义:

  • tips
    使用 return 关键字返回内容
    将内容 返回到 函数调用处
    函数体中没有 return 语句时,函数运行结束
    则默认返回 None , 也被称为隐含返回值

  • 表达式:
    def function_name(param):
    return param
    res = function_name(param)

  • eg. :

# 小栗子:
# 摄氏度与华氏度关系如:摄氏度/1.8 + 32 = 华氏度
# 需求:
# 定义函数1:用于输出摄氏度
# 定义函数2:通过摄氏度计算得出华氏度


def c_temp():
    c_t = -3
    print(f"今天摄氏度:{c_t}℃")
    return c_t


def f_temp(n):
    f_t = round(n/1.8) + 30
    print(f"今天华氏度:{f_t}℉")


if __name__ == "__main__":

    res = c_temp()

    f_temp(res)

Run:
今天摄氏度:-3℃
今天华氏度:28℉

函数多个返回值
  • 当执行函数体内代码时,遇到第一个 return 就将指定值返回到函数调用处,也就是执行到return这行代码,后面的都不执行了。
  • 多个返回值时,用逗号隔开,但默认为元组。
  • 格式:
    def function_name(param1,param2):
    return param1
    return param2
    res = function_name(param)

函数的作用域

作用域介绍
  • Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
  • 变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python 的作用域一共有4种,分别是:
    L(local):局部作用域,即函数中定义的变量;
    E(enclosing):嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
    G(global):全局变量,就是模块级别定义的变量;
    B(build-in):内建作用域,系统固定模块里面的变量,比如:int()等;
global 关键字
  • 当我们需要在函数内部直接修改全局变量时,我们可以将函数内部的局部变量通过 global 关键字声明为全局变量。

函数作用域的优先级

在这里插入图片描述

递归函数

递归的介绍

函数Func(args)直接或间接调用函数本身,则该函数称为递归函数。

tips:
  • 递归函数自身没有结束条件,所以需要我们自己设置结束条件,终止函数的调用。

  • 可以使用递归实现的循环都可以实现,并且递归效率很低,所以递归很少使用。

  • eg. :

# 小栗子:
# 阶乘本质:n! = 1 * 2 * 3 * ... * n


def get_nums(num):
    if num > 1:
        return num*get_nums(num - 1)
    else:
        return 1


res = get_nums(4)
print(res)

Run:
24

内置文件中常用方法

range()
  • range(start,stop,step) --> range object
  • 步长step不能为小数。(0.5 / 1.5 / …)
    在这里插入图片描述
    -eg. : 实现返回1-10的奇数列表
li = []

for i in range(1, 11, 2):
    li.append(i)

print(li)

Run:
[1, 3, 5, 7, 9]

zip()
  • zip()用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
  • zip(iter[,iter2 […]]) --> zip object
map()
  • map() 会根据提供的函数对指定序列做映射。
  • map(func, *iterables) --> map object
filter()
  • filter() 用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
  • filter(func, iterable) --> filter object
    在这里插入图片描述

匿名函数lambda

匿名函数介绍
  • 当我们在传入函数时,有些时候,不需要重复调用该函数,直接传入匿名函数更方便,也无需担心函数名冲突,并且还可以将匿名函数赋值给一个变量,再利用变量来调用该函数。
    语法 :lambda x:x*x
    在这里插入图片描述
匿名函数应用
# lambda 形参:返回值
f = lambda x, y: x * y
print(f(2, 5))
# n!
from functools import reduce
print(reduce(lambda x, y: x*y, range(1, 4)))
# 匿名函数作为返回值
def fx(i, j):
    # 返回的匿名函数
    return lambda :i*j


f = fx(6, 6)    # lambda :i*j
print(f())      # 函数只有在调用时才会执行
# 匿名函数作为实参
def test(a, b, func):
    res = func(a, b)    # 11 + 22
    return res


nums = test(11, 22, lambda x, y: x+y)
print(nums)

Run:33

  • sort 与 sorted 区别:
    sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
    list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
# [4,-2,3,1] --> [1,-2,3,4]
# [4,2,3,1] --> [1,2,3,4] --> [1,-2,3,4]
li = [4, -2, 3, 1]

# key:指定排序规则,排序之后返回原元素
li.sort(key=abs)
print(li)

高阶函数

  • 高阶函数需满足条件:
    函数名 作为 参数传入
    函数名 作为 返回值
拓展1:值类型与引用类型,传值调用与传引用调用的区别

本段引用来至:值类型与引用类型,传值调用与传引用调用的区别

值类型
  • 指基本类型
  1. 整型: byte,short,int,long
  2. 浮点型:float,double
  3. 字符型:char
  4. 逻辑型:boolean
  • 引用类型
    除了四类八种基本类型外,所有的类型都称为引用类型(如:数组,类,接口,字符串)

  • 值传递
    基本数据类型赋值都属于值传递;
    值传递传递的是实实在在的变量值,是传递原参数的拷贝,值传递后,实参传递给形参的值,形参发生改变而不影响实参。

  • 特点:
    此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。

  • 引用传递
    引用类型之间赋值属于引用传递;
    引用传递传递的是对象的引用地址,即它的本身;

  • 特点:
    在方法执行中,形参和实参内容相同,指向同一块内存地址,方法执行中对引用的操作将会影响到实际对象。

  • 传值调用
    方法调用时,实际参数把它的值的副本传递给对应的形式参数,形参的变化与实参无关。

  • 引用调用
    方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,函数接收的是原始值的内存地址;允许调用函数修改原始变量的值。

拓展2:python中函数值传递与引用传递

本段引用来至:python中函数值传递与引用传递

  • 第一个问题:值传递与引用传递
    简单理解,值传递就是在一个参数传入到函数中后,函数中对该参数的操作不会影响函数外该参数的变量的值;而引用传递,则是参数传递进来的相当于内存地址,对该参数的操作会直接影响到外部指向其值的变量。

  • 结论
    python中的变量没有类型,变量相当于一个指针,可以指向任何类型的对象,也就是变量引用了某个对象;python对象是有类型的,一般看变量是什么类型需要看其引用的对象是什么类型。

  • python中没有严格的定义值传递与引用传递,总的看来,函数传递参数都可以看做是引用传递的(因为python变量相当于指针,传递指针就相当于传递了地址的引用),只不过因为python中的有些对象是不可变的,因此让有些值传递的过程中又像是值传递的。

  • 当python中的函数传递过来的是不可变的值时(比如数字常量、元组和字符串),相当于 C语言中的值传递的;若传递过来的参数是可变的(如列表、字典等),则相当于引用传递。

不可变的对象作参数
看个例子:

x = 10
print("xid=",id(x))
def A(x):
    print("axid=",id(x))
    return x
def B(x):
    x += x
    return x
print("ax=",A(x))
print("bx=",B(x))
print("x=",x)

结果为:

xid= 140718279271088
axid= 140718279271088
ax= 10
bx= 20
x= 10
  • 当x传递进函数的时候,会被当作一个局部变量,也就是会新开辟一个空间存放变量,该变量引用了参数的引用。因为原参数引用的是一个不可变的对象,所以局部变量会与函数外的变量指向同一个内存区域;该局部变量不会影响函数外的变量,但在函数内给x重新赋值的时候,会重新生成一个对象,并让局部变量x指向新的对象,而外部变量x不变。因此相当于值传递。

当传递的对象是可变对象时

x = [10]
print("xid=",id(x))
def A(x):
    print("axid=",id(x))
    return x
def B(x):
    x += x
    return x
print("ax=",A(x))
print("bx=",B(x))
print("x=",x)

结果为:

xid= 2769750468232
axid= 2769750468232
ax= [10]
bx= [10, 10]
x= [10, 10]

  • 原理与上面的类似,但有区别,x传递进函数仍然可以看作生成了一个局部变量x,其引用了外部变量x的引用,与不变的对象的运算不同的是,此时的x += [10] 操作,不会生成新的列表对象,而是修改了原来存储空间中的列表对象,因此外部的x的值也会跟着改变。
    下面的例子与传递不可变参数时是一样的:
x = [10]
print("xid=",id(x))
def A(x):
    print("axid=",id(x))
    return x
def B(x):
    x = [10,10]
    print("bxid", id(x))
    return x
print("ax=",A(x))
print("bx=",B(x))
print("x=",x)

结果为:

xid= 2769750458632
axid= 2769750458632
ax= [10]
bxid 2769750460616
bx= [10, 10]
x= [10]

这里是因为赋值号=生成了新的列表导致了局部变量x指向了新的对象,原对象不变。

总结
python中的值传递与引用传递是一个相对的概念,原值有没有变化关键在原来的变量引用的对象有没有发生改变,而这些需要根据变量引用的对象来判断。


作业:

在这里插入图片描述

  • 作业3
    在这里插入图片描述

作业答案:

题1:

# 前一次做了!

  • Run:
  • Amy的答案:
def get_sum(num_a, num_b, num_c):
    return num_a + num_b + num_c


def get_avg(num_sums, len_li):
    num_avg = round(num_sums / len_li, 2)       # round(object, 2),2代表小数点后精确到位数为2,默认为0.
    print(f"和为:{num_sums}\n均值为:{num_avg}")


if __name__ == "__main__":
    ipt_li = list(map(float, input("请输入:").split()))       # split():拆分字符串。通过指定分隔符对字符串进行切片,并返回分割后的字符串列表(list),默认分隔符为空格
    num_sums = get_sum(*ipt_li)
    get_avg(num_sums, len(ipt_li))

-Run:
请输入:45 63 63
和为:171.0
均值为:57.0

题2:

法2:

tips:
  • Amy提示的isinstance()真好用呀!get到了!
  • 重新认识了 *args 和 *。
  • 尝试了用递归没成功,等答案吧!
def opt_tu(*args):      # 此处的* 代表可变长度参数前的*

    print(*args, sep="\n")


if __name__ == "__main__":

    li = ["jack", ("tom", 23), "rose", (14, 55, 67)]

    for i in range(0, len(li)):		# for i in range li:这样更好

        if isinstance(li[i], tuple):        # 测试了也可以这样写 if type(li[i]) == type(()):

            opt_tu(*li[i])      # 此处的* 代表拆包

        else:

            print(li[i])

  • Run:
    jack
    tom
    23
    rose
    14
    55
    67

  • Amy的答案:递归!

def get_ele(l):
    if isinstance(l, (int, str)):
        print(l)
    else:
        for item in l:
            get_ele(item)

l = ["jack", ("tom", 23), "rose", (14, 55, 67)]

get_ele(l)

Run:
jack
tom
23
rose
14
55
67

题3:

infors = [{'name': 'qian', 'age': 28}, {'name': 'amy', 'age': 20}, {'name': 'james', 'age': 25}]

print(sorted(infors, key=lambda s: s['name']))

  • Run:
    [{‘name’: ‘amy’, ‘age’: 20}, {‘name’: ‘james’, ‘age’: 25}, {‘name’: ‘qian’, ‘age’: 28}]

  • Amy的答案:

infors = [{'name': 'qian', 'age': 28}, {'name': 'amy', 'age': 20}, {'name': 'james', 'age': 25}]

infors.sort(key=lambda x: x["name"])

print(infors)
  • Run:
    [{‘name’: ‘amy’, ‘age’: 20}, {‘name’: ‘james’, ‘age’: 25}, {‘name’: ‘qian’, ‘age’: 28}]

End !
Best wishes for you!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值