Python基础Day5/6—函数和集合

问:为什么总是把简书笔记写的那么长这样没人看的
其实我在简书上记笔记只是方便我日后想不起来的时候 Ctrl+f,把很多知识点写在一起虽然阅读体验不好,可是Ctrl+f起来超级方便,能够避免开很多窗口~

1.函数的定义和调用

函数:实现某个功能的代码块(其实就是一段代码)

学习函数目的:提高代码的复用性,减少代码的冗余

# 定义函数
def show():
    # 实现某个功能的代码要放到函数里面
    for _ in range(3):
        print("人生苦短")

# 调用函数
show()


############## 输出结果 ###############
人生苦短
人生苦短
人生苦短

提示:定义函数的时候,函数里面的代码不会执行,当调用函数的时候函数里面的代码才会执行。
调用函数的格式: 函数名()

2. 函数的文档说明

函数的文档说明: 解释说明函数的功能的。

def show():
    """实现两个数字和计算"""
    result = 1 + 2

    print(result)

show()


############## 输出结果 ###############
3

查看函数的文档说明,使用help()函数。

执行help(show)代码可以看到show函数的文档说明:

help(show)

############## 输出结果 ###############
Help on function show in module __main__:

show()
    实现两个数字和计算

2.带有参数的函数

函数带有参数的语法格式 :

def 函数名(形参1, 形参2, .....):
    实现某个功能的具体代码

函数名(实参1, 实参2, ....)

定义带有参数的函数:

# 定义带有参数的函数
def sum_num(num1, num2):
    result = num1 + num2
    print(result)

#调用函数,并给函数传递参数
sum_num(5, 3)

############## 输出结果 ###############
8

函数调用流程:

  • 调用函数先回到函数的定义,给函数的参数传参;
  • 给函数参数传参完成以后,执行函数里面的代码;
  • 当函数里面的代码执行完成以后,会回到函数之前调用的地方,然后代码可以再继续往下执行。

需求:从屏幕接受三个数字,计算第一个数加第二个数减去第三个数字的结果。

def sum_num(num1, num2, num3):
    result = num1 + num2 - num3
    print(result)


num1 = int(input("请输入第一个数字:"))
num2 = int(input("请输入第二个数字:"))
num3 = int(input("请输入第三个数字:"))

sum_num(num1, num2, num3)

3. 函数的返回值

函数的返回值:当调用函数的时候,函数内部的数据想要让函数外界使用,需要通过return关键字把函数内部的数据进行返回。
所谓“返回值”,就是程序中函数完成一件事情后,最后给调用者的结果

# 定义一个函数,实现两个数字和的计算,并把计算后的结果返回出去

def sum_num(num1, num2):
    result = num1 + num2
# 把函数内部的数据通过return返回给函数的调用者
    return result


# 调用函数
value = sum_num(1, 2)

# 打印出函数的返回值
print(value)

函数返回值的扩展

扩展1:return可以结合None使用
定义一个函数,该函数可以查找任意一个参数是否在字符串hello中,如果在返回该字母,如果不在则返回None

def search_str(params):
    my_str = "hello"
    for value in my_str:
        if value == params:
            return value
    else:
        return None

我们给search_str()函数传递参数做个测试:

result = search_str("x")
print(result)

############## 输出结果 ###############
None

扩展2:多层循环结合return关键字使用,可以让多层循环终止循环。

首先,定义一个for循环遍历列表的函数

# 定义函数
def show():
    my_list = ["Life", "is", "short", "you", "need", "Python"]
    for value in my_list:
        print(value, end=" ")

# 调用函数
show()

############## 输出结果 ###############
Life is short you need Python

结合return关键字,改变函数:当遍历到short时,终止循环:

def show():
    my_list = ["Life", "is", "short", "you", "need", "Python"]
    for value in my_list:
        print(value, end=" ")
        if value == "short":
            return 

show()


############## 输出结果 ###############
Life is short

扩展3: 函数没有提供返回值,那么调用函数的时候如果使用变量接收返回结果,那么返回的结果是None。

# 定义一个函数
def show():
    my_name = "huangjing"
    print("my name's", my_name)

# 调用函数
result = show()
print("result is", result)


############## 输出结果 ###############
my name's huangjing
result is None

总结:

  • 当函数执行了return表示函数执行结束,即使后面有更多的代码,也不会执行。
  • return关键字只能在函数或者方法内部使用。

4. 函数的嵌套调用

函数的嵌套调用:在函数里面又调用其他函数,这种格式就是函数的嵌套调用。

写一个嵌套函数task(),其中task()中嵌套有task1()task2()

def task1():
    print("好好学习")

def task2():
    print("天天向上")

def task():
    print("悄悄咪咪的告诉你:")
    task1()
    task2()

task()


############## 输出结果 ###############
悄悄咪咪的告诉你:
好好学习
天天向上

嵌套函数的应用

需求:定义一个可以打印一条横线的函数,然后再利用该函数写一个嵌套函数使嵌套函数可以打印自定义行数的横线。

可以打印一条横线的函数:

# 定义一个可以打印一条横线的函数
def print_one_line():
    print("-" * 20)

# 调用函数
print_one_line()

############## 输出结果 ###############
--------------------

打印自定义行数的横线:

def print_custom_line(rows):
    for value in range(rows):
        print_one_line()

看下整体效果:

def print_one_line():
    print("-" * 20)

def print_custom_line(rows):
    for value in range(rows):
        print_one_line()

print_custom_line(4)

############## 输出结果 ###############
--------------------
--------------------
--------------------
--------------------

5. 局部变量和全局变量

局部变量

局部变量:在函数内定义的变量称为局部变量。

def show():
    # 定义了一个局部变量
    num1 = 100
    # 局部变量的作用域:只能在函数内部使用,无法在函数外使用
    print("函数内的变量:", num1)

# 调用函数
show()

测试:在函数外部使用局部变量

print(num1)

######## 报错信息为:########
NameError: name 'num1' is not defined

总结:

  • 局部变量的作用:临时存储函数内的数据,当函数执行结束时,局部变量保存的数据会销毁,在内存中进行释放。
  • 局部变量的作用域:只能在函数内部使用,无法在函数外使用。

全局变量

全局变量:在函数外定义的变量称为全局变量。

# 定义全局变量
g_num = 1000


def show():
    # 全局变量可以在不同函数内使用
    print("show函数使用的全局变量:", g_num)


def show_info():
    # 全局变量可以在不同函数内使用
    print("show_info函数使用的全局变量:", g_num)

show()
show_info()

# 测试:全局变量还可以在函数外部使用
print("函数外使用的全局变量为:", g_num)


############## 输出结果 ###############

show函数使用的全局变量: 1000
show_info函数使用的全局变量: 1000
函数外使用的全局变量为: 1000

总结:

  • 全局变量的作用域(使用范围): 可以在不同函数内或者函数外使用这个全局变量。
  • 全局变量的作用:可以在不同函数内共享数据(全局变量数据)

6.修改全局变量

在函数内修改全局变量需要使用关键字 global

我们先看一下,在函数内如果不使用关键字 global来修改全局变量会出现什么情况:

# 定义全局变量
g_num = 100

def modify_value():
#其实这里没有对全局变量进行修改,而是定义了一个局部变量,只不过局部变量的名字和全局变量的名字相同而已
    g_num = 200
    print(g_num)

modify_value()
print(g_num)


############## 输出结果 ###############
200
100

我们发现全局变量并没有发生改变。还是之前的100
使用关键字 global来修改全局变量:

g_num = 100

def modify_value():

    global g_num
    g_num = 200
    print(g_num)

modify_value()
print(g_num)


############## 输出结果 ###############
200
200

7. 多个函数共享数据

多个函数共享数据的操作:

  • 全局变量:全局变量可以在不同函数内共享数据(全局变量)
  • 返回值(return 数据):通过返回值也可以在不同函数内共享数据(返回的数据)

全局变量共享数据的操作:

g_num = 1

def modify_value():
    # 修改全局变量的数据
    global g_num
    g_num = 2

def show():
    print("show:", g_num)

modify_value()
show()


############## 输出结果 ###############
show: 2

以上可以看出,全局变量可以在modify_value()函数和show()函数中同时使用。

返回值共享数据的操作:

def return_value():
    msg = "嘻嘻"
    # 通过return把数据返回给函数的调用者,可以完成数据的共享操作
    return msg


def show_info():
    # 调用带有返回值得函数
    value = return_value()
    print("show_info:", value)


def show_msg():
    msg = return_value()
    print("show_msg:", msg)


show_info()
show_msg()


############## 输出结果 ###############
show_info: 嘻嘻
show_msg: 嘻嘻

8. return返回多个值

return返回多个数据的解决方案:把多个数据封装到一个容器类型里面,这样通过return可以一次性返回多个数据。

返回列表:(返回元组同理)

# 定义函数
def return_more_value():
    #一次性返回多个数据
    return [1, 3, 5]

# 调用函数
result = return_more_value()
print(result, type(result))


############## 输出结果 ###############
[1, 3, 5] <class 'list'>

返回字典:

# 定义函数
def return_more_value():
    #一次性返回多个数据
    return {"name": "林黛玉", "age": 20}

# 调用函数
result = return_more_value()
print(result, type(result))


############## 输出结果 ###############
{'name': '林黛玉', 'age': 20} <class 'dict'>

9. 函数的缺省参数:

缺省参数:形参设置了默认值,那么这个形参就是缺省参数。

缺省参数的特点:

  • 当调用函数时如果给缺省参数传值了,那么使用传入的数据;
  • 当调用函数时如果没有给缺省参数传值,那么使用缺省的参数默认值。

缺省参数的注意点:

  • 缺省参数后面不能再定义普通参数;
  • 缺省参数后面还可以再次定义缺省参数

不给缺省参数传值,缺省参数使用默认值:

# 此时的age参数就是缺省参数
def show(name, age=18, sex="男"): 
    print("姓名:", name, "年龄:", age, "性别:", sex)

# 不给缺省参数传值,缺省参数使用默认值
show("贾宝玉")


############## 输出结果 ###############
姓名: 贾宝玉 年龄: 18 性别: 男

给缺省参数传值了,那么使用传入的数据:

def show(name, age=18, sex="男"): # 此时的age参数就是缺省参数
    print("姓名:", name, "年龄:", age, "性别:", sex)

show("林黛玉", 20, "女")

############## 输出结果 ###############
姓名: 林黛玉 年龄: 20 性别: 女

10. 调用函数传参的三种方式

  • 按照位置参数的方式进行传参, 注意点:强调的是调用函数时位置参数的顺序要和函数定义时参数的顺序保持一致;
  • 按照关键字参数的方式进行传参, 注意点:强调的是调用函数是关键字的名字要和函数定义时参数的名字保持一致;
  • 前面是按照位置参数的方式进行传参后面是关键字参数的方式进行传参。

首先我们先定义一个函数:

def show_info(name, age):
    print("姓名:", name, "年龄:", age)

接下来没我,我们按照不同的方式进行传参

按照位置参数的方式进行传参:

positional argument: 表示位置参数

show_info("李四", 30)

按照关键字参数的方式进行传参:

keyword argument: 表示关键字参数

show_info(name="李四", age=30)

前面是按照位置参数的方式进行传参后面是关键字参数的方式进行传参:

show_info("李四", age=30)

注意:如果前面使用关键字参数后面必须使用关键字参数

11. 函数的不定长参数

函数的不定长参数:函数参数的个数不确定,可能是0个或者1个或者多个这样的参数称为不定长参数。

不定长参数可以分为两种:

  • 不定长位置参数: *args,提示:当调用函数时,所有的位置参数封装成一个元组给args参数;
  • 不定长关键字参数: **kwargs,提示:当调用函数时,所有的关键字参数封装成字典给kwargs参数

缺省参数结合不定长位置参数使用

注意点:把缺省参数放到不定长位置参数的后面。

如果不这样操作,那按照位置参数的方式进行传参时,这里的缺省参数的默认值永远使用不了。听上去很复杂的样子,其实稍微操作一下非常容易理解

def show(name, age=18, *args):
    print("name:", name, "age:", age, "args:", args)

# 调用函数时使用位置参数的方式进行传参
show("李四", 20, 1, 2, 3)


############## 输出结果 ###############
name: 李四 age: 20 args: (1, 2, 3)

我们可以看到在按照位置参数的方式进行传参时,第二个参数默认给了age参数,剩下的所有数据打包成元组的形式传给了不定长位置参数args,这里的缺省参数的默认值永远使用不了。
解决办法:把缺省参数放到不定长位置参数的后面。

def show(name, *args, age=18):
    print("name:", name, "age:", age, "args:", args)

show("李四", 20, 1, 2, 3)


############## 输出结果 ###############
name: 李四 age: 18 args: (20, 1, 2, 3)

也可以在传参的时候,修改默认参数

def show(name, *args, age=18):
    print("name:", name, "age:", age, "args:", args)

show("李四", 20, 1, 2, age=30)


############## 输出结果 ###############
name: 李四 age: 30 args: (20, 1, 2)

缺省参数结合不定长位置参数和不定长关键字参数的使用

注意点:不定长关键字参数需要放到所有参数的最后面

def show(name, *args, age=18, **kwargs):
    print("name:", name, "age:", age, "args:", args, "kwargs:", kwargs)

show("张三", 1, 3, 5)
show("王五", 1, 3, 5, 20, a=1, b=2, c=3, age=40)

############## 输出结果 ###############
name: 张三 age: 18 args: (1, 3, 5) kwargs: {}
name: 王五 age: 40 args: (1, 3, 5, 20) kwargs: {'a': 1, 'b': 2, 'c': 3}

“王五”的例子说明了:当使用关键字传参的时候,优先判断有没有对于的参数名,如果有数据给对于的参数名,否则kwargs参数。

不定长参数的扩展

在函数定义的时候,可以使用不定长位置参数*args和不定长关键字参数**kwargs来定义。在调用的时候也可以用同样的方法来拆包
比如,我们把要传的位置参数都封装到了元组里面,把关键字参数都封装到了字典里面

my_tuple = (1, 2)
my_dict = {"a": 1, "b": 2}

使用show(*my_tuple)调用所有不定长位置参数:

# *my_tuple: 表示对元组进行拆包,把元组中的每项数据按照位置参数的方式进行传参
show(*my_tuple)  # 等价于 show(1, 2)

使用show(**my_dict)调用所有不定长关键字参数:

show(**my_dict) # 等价于 show(a=1, b=2)

show(*my_tuple, **my_dict)  # 等价于show(1, 2, a=1, b=2)

注意点:*my_tuple**my_dict不能单独使用, 只能结合不定长位置参数和不定长关键字参数使用。

看实战效果
show(*my_tuple) 这里等价于show(a=1, b=2)

def show(*args, **kwargs):
    print(args, kwargs)

my_tuple = (1, 2)
my_dict = {"a": 1, "b": 2}

show(*my_tuple)


############## 输出结果 ###############
(1, 2) {}

show(**my_dict) 等价于 show(a=1, b=2)

def show(*args, **kwargs):
    print(args, kwargs)

my_tuple = (1, 2)
my_dict = {"a": 1, "b": 2}

show(**my_dict)


############## 输出结果 ###############
() {'a': 1, 'b': 2}

show(*my_tuple, **my_dict) 等价于show(1, 2, a=1, b=2)

def show(*args, **kwargs):
    print(args, kwargs)

my_tuple = (1, 2)
my_dict = {"a": 1, "b": 2}

show(*my_tuple, **my_dict)


############## 输出结果 ###############
(1, 2) {'a': 1, 'b': 2}

12. 拆包

拆包:使用不同变量保存容器类型中的每一个数据,这样的操作就是拆包。
注意点:拆包是结合容器类型使用的, 变量的个数要和容器类型中数据个数要一致
容器类型: 字符串,列表,元组,字典,range,集合(set)

对字符串进行拆包:

my_str = "abc"

value1, value2, value3 = my_str

print(value1, value2, value3)


############## 输出结果 ###############
a b c

对列表进行拆包:

my_list = [1, 2, 3]

value1, value2, value3 = my_list

print(value1, value2, value3)

############## 输出结果 ###############
1 2 3

对元组进行拆包:

my_tuple = (2, 2, 3)

value1, value2, value3 = my_tuple

print(value1, value2, value3)

############## 输出结果 ###############
2 2 3

对字典进行拆包:

my_dict = {"name": "李四", "age": 20}

value1, value2 = my_dict

print(value1, value2)

############## 输出结果 ###############
name age

对字典直接进行拆包,获取的是每一个key,对values进行拆包:

my_dict = {"name": "李四", "age": 20}
value1, value2 = my_dict.values()
print(value1, value2)

############## 输出结果 ###############
李四 20

range进行拆包:

my_range = range(1, 3)

value1, value2 = my_range

print(value1, value2)

############## 输出结果 ###############
1 2

拆包应用场景

拆包应用场景一:用拆包来获取容器类型中的每一个数据

def return_value():
    return 1, 3, 5

result = return_value()
print(result, type(result))

############## 输出结果 ###############
(1, 3, 5) <class 'tuple'>

上面定义的return_value()函数返回了一个元组,接下来我们可以用拆包来获取元组中的每一个数据:

def return_value():
    return 1, 3, 5

# 拆包: 获取容器类型中的每一个数据
value1, value2, value3 = return_value()
print(value1, value2, value3)

############## 输出结果 ###############
1 3 5

拆包应用场景二:用拆包交换两个变量的值

a = 1
b = 2

print(a, b)

a, b = b, a

print(a, b)

############## 输出结果 ###############
1 2
2 1

看似很简单的过程,其实中间经历了一个拆包的过程,在pyth里有等号=先看等号右面的内容,这里其实是把ba的数据封装到一个元组里面,然后使用变量a和变量b保存元组里面的第一个值和第二个值。

13. 引用

引用:就是使用的意思, 就是使用变量保存了数据在内存的地址,变量没有直接保存数据,而保存的是数据在内存中的地址。
num1 = 1 :定义一个变量,名称是num1, 使用该变量保存了一下数据1在内存中的地址
num2 = num1:定义一个变量,名称是num2, 使用该变量保存了num1存储数据的内存地址
id(num1):使用id()函数查看变量的内存地址。
提示:内存地址其实就是一个数字,可以理解成一个数字编号

num1 = 1
num2 = num1

print("num1是:", num1, "num2是:", num2)

num1_id = id(num1) 
num2_id = id(num2)

print("num1的内存地址是:", num1_id, "num2的内存地址是:", num2_id)


############## 输出结果 ###############
num1是: 1 num2是: 1
num1的内存地址是: 140727306383616 num2的内存地址是: 140727306383616

我们发现这两个变量的内存地址是一样的,说明了赋值其实就是传递了数据的内存地址。
变量重新赋值内存地址就是发生变化:

num1 = 1 
num2 = num1
num1 = 2

print(num1, num2)

num1_id = id(num1)  
num2_id = id(num2)

print(num1_id, num2_id)

############## 输出结果 ###############
2 1
140727223284000 140727223283968

14. 可变类型和不可变类型

不可变类型

不可变类型: 不允许在原有内存空间的基础上修改数据,修改数据后内存地址会发生变化。

不可变类型有: 数字,字符串,元组

数字:

num1 = 100

# 查看变量的内存地址
num1_id = id(num1)
print(num1, num1_id)

# 不可变类型想要修改数据,都是通过重新赋值来完成
num1 = 200
# 查看变量的内存地址
num1_id = id(num1)
print(num1, num1_id)


############## 输出结果 ###############
100 140727306386784
200 140727306389984

字符串:

my_str = "abc"
print(my_str, id(my_str))

my_str = "adc"
print(my_str, id(my_str))

############## 输出结果 ###############
abc 2191203856688
adc 2191203947568

元组:

my_tuple = (1, 5)

print(my_tuple, id(my_tuple))

my_tuple = (2, 5)

print(my_tuple, id(my_tuple))

############## 输出结果 ###############
(1, 5) 1361982166344
(2, 5) 1361982405960

对于不可变类型来说,不能根据下标修改数据,以下操作都是错误的操作:

my_str = "abc"
my_str[1] = "d"  #这种操作不对,不可变类型不能根据下标修改数据

my_tuple = (1, 5)
my_tuple[0] = 2  #这种操作不对,不可变类型不能根据下标修改数据

总结:

  • 对于不可变类型来说,不能在原有内存空间的基础上修改数据,想要修改数据都是通过重新赋值来完成。
  • 重新赋值一个新值,那么变量保存的内存地址会发生变化。

可变类型

可变类型:允许在原有内存空间的基础上修改(添加,删除,修改)数据,修改后内存地址不变。
可变类型: 列表,字典,集合

列表

my_list = [1, 4, 6]
print(my_list)
print(id(my_list))

print("===== 修改数据内存地址不变 ======")

my_list.append(8)
print(my_list)
print(id(my_list))


############## 输出结果 ###############

[1, 4, 6]
1524680118856
===== 修改数据内存地址不变 ======
[1, 4, 6, 8]
1524680118856

字典

my_dict = {"name": "李四", "age": 20}
print(my_dict)
print(id(my_dict))

print("===== 修改数据内存地址不变 ======")

my_dict["age"] = 50
print(my_dict)
print(id(my_dict))


############## 输出结果 ###############
{'name': '李四', 'age': 20}
2017536906808
===== 修改数据内存地址不变 ======
{'name': '李四', 'age': 50}
2017536906808

不管是列表还是字典修改数据内存地址都不会变化,但如果重新赋值那内存地址就会发生变化:

my_list = ["a", "b"]

print(my_list)
print(id(my_list))

print("===== 重新赋值内存地址改变 =====")

my_list = ["c", "d"]

print(my_list)
print(id(my_list))

############## 输出结果 ###############
['a', 'b']
2441131479624
===== 重新赋值内存地址改变 =====
['c', 'd']
2441132757640

总结:

  • 在原有内存空间的基础上进行修改数据,修改后内存地址不变
  • 重新赋值一个新值,这样做变量保存的内存地址会变化

函数的可变类型和不可变类型

定义一个函数:

def show(params):
    print(params, id(params))

    print("==== 修改前VS修改后 ====")
    
    params += params
    print(params, id(params))

调用函数,给函数传参的时候,如果params变量是一个不可变类型,那么 +=params 保存的内存地址会发生变化,如果 params 变量是一个可变类型,那么 +=params 保存的内存地址不变,因为可变类型允许在原有内存空间的基础上修改数据。

给函数的参数传一个定义可变类型的变量——列表

def show(params):
    print(params, id(params))

    print("==== 修改前VS修改后 ====")
    
    params += params
    print(params, id(params))

# 定义可变类型的变量
my_list = [1, 3]
print(my_list, id(my_list))

#调用函数
show(my_list)

############## 输出结果 ###############
[1, 3] 2682280956488
[1, 3] 2682280956488
==== 修改前VS修改后 ====
[1, 3, 1, 3] 2682280956488

内存地址没有变化

给函数的参数传一个定义不可变类型的变量——数字

def show(params):
    print(params, id(params))

    print("==== 修改前VS修改后 ====")

    params += params
    print(params, id(params))


# 定义不可变类型的变量
my_num = 1

print(my_num, id(my_num))

# 调用函数
show(my_num)


############## 输出结果 ###############
1 140727306383616
1 140727306383616
==== 修改前VS修改后 ====
2 140727306383648

内存地址改变

15.函数使用的注意点

return注意点:

  • return 只能返回一次数据;
  • 当函数执行了return语句说明函数执行结束,return语句后面的代码不能执行;
  • return关键字只能在函数或者方法内使用

局部变量和全局变量的作用域

  • 局部变量的作用域: 只能在函数内部使用;
  • 全局变量的作用域: 可以在不同函数内或者函数外使用

函数名重名

  • 如果函数名相同,后面的函数会把前面的同名函数进行覆盖,前面的函数就不能再使用了;
  • 在python里面没有函数或者方法的重载
def show():
    print("明天大家休息,可以出去浪一下~")


def show():
    print("浪什么浪,继续学Python!")

show()


############## 输出结果 ###############
浪什么浪,继续学Python!

可以看到两个show()函数重名,后面的把前面的覆盖掉了

16. 递归函数

递归函数:在一个函数内部又调用函数本身那么这样的函数称为递归函数。
递归函数的注意点:递归函数必须要有结束递归调用条件。

def show(num):
    print("人生苦短")
    if num == 3:
        print("我用Python")
    else:
        show(num+1)

# 调用函数
show(2)

这里 num == 3 就是结束递归的条件,表示不需要再自己调用函数本身。

递归函数的应用

求某个数字的阶乘

为什么说求数字的阶乘要应用到递归函数呢?请看:
3! = 3 * 2 * 1 ---> 3 * 2!
2! = 2 * 1 --- > 2 * 1!
1! = 1
n! = n * (n-1)!
也就是说,我们定义一个计算某一个数阶乘的函数show(),我们知道 n! = n * (n-1)! 计算n!的过程中需要不断调用求阶乘的函数show(),所以就用到递归函数。

def show(num):

    if num == 1:
        return 1
    else:
        return num*show(num - 1)


result = show(3)
print(result)


############## 输出结果 ###############
6

递归调用扩展:递归函数并不是可以无限次的调用,系统的默认递归调用次数是1000次。

查看系统默认递归调用次数:

import sys
count = sys.getrecursionlimit()
print(count)


############## 输出结果 ###############
1000

修改默认最大递归次数:

# 修改默认递归次数
import sys
sys.setrecursionlimit(2000)

# 查看修改后的结果
print(sys.getrecursionlimit())


############## 输出结果 ###############
2000

17. 匿名函数

匿名函数:使用 lambda 关键字定义函数称为匿名函数。
匿名函数语法格式:lambda 形参1, 形参2, .... : 返回值或者调用其他函数

匿名函数的注意点:

  • 匿名函数也是属于函数的;
  • 匿名函数只能写一行代码;
  • 匿名函数的返回值不需要使用return关键字

匿名函数的使用:

# 使用变量add_func保存了一个匿名函数
noname_func = lambda num1, num2 : num1 + num2

# 给匿名函数传递参数
result = noname_func(1, 2)
print(result)


############## 输出结果 ###############
3

解释一下以上匿名函数 lambda num1, num2 : num1 + num2
冒号:前是匿名函数noname_func的两个形参,待会儿调用的时候需要给他们赋值,冒号后面是该函数的功能:求两个参数的和。
在调用该匿名函数noname_func(1, 2)时,传递了两个参数1和2,求两个参数的和便是3。

没有参数的匿名函数:

no_param = lambda : print("人生苦短,我用Python")
no_param()


############## 输出结果 ###############
人生苦短,我用Python

学习匿名函数目的:使用匿名函数可以简化普通函数的代码。

18. 列表推导式

列表推导式: 使用for循环快速创建一个列表,也就是说列表推导式最终返回的类型是列表。
语法结构:[xx for xx in 容器类型]

使用for循环快速创建列表

使用for循环快速创建列表即列表推导式,示例:

result = [value for value in range(1, 6)]
print(result, type(result))


############## 输出结果 ###############
[1, 2, 3, 4, 5] <class 'list'>

列表推导式代码解释:for循环可以使value遍历range(1, 6)(即,[1, 5])里的每一个元素,给for前面的value,生成一个列表。

列表推导式结合if语句

列表推导式可以结合if语句使用,作为条件过滤对应的数据,比如我们只要1-10之间所有的奇数:

result = [value for value in range(1, 11) if value % 2 == 1]

print(result, type(result))


############## 输出结果 ###############
[1, 3, 5, 7, 9] <class 'list'>

列表推导式结合if语句代码解释:for循环使value遍历range(1, 11)中的每一个元素(即:[1, 10])然后if语句对for循环遍历的每一个元素进行过滤——是否满足if后面的条件,这里就是查看1-10中的每一个数字除以2后是否余数是1,把满足条件的元素赋值给for前面的value然后生成一个列表。

列表推导式结合多个for循环使用

result = [(value1, value2) for value1 in range(2) for value2 in range(1, 4)]
print(result, type(result))


############## 输出结果 ###############
[(0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3)] <class 'list'>

列表推导式结合多个for循环代码解释: value1遍历 range(2)(即0、1),value2遍历 range(1, 4)(即,1、2、3)给前面的元组(value1, value2)value1value2组合成的所有元组生成一个列表输出。

练一练1:使用列表推导式取出my_list列表里年龄大于20岁的
my_list = [{"name": "李四", "age": 20}, {"name": "王五", "age": 22}]

result = [my_dict for my_dict in my_list if my_dict["age"] > 20]
print(result)

############## 输出结果 ###############
[{'name': '王五', 'age': 22}]

练一练1的代码解释: 我们创造一个任意变量my_dict使for循环遍历已知列表my_list里面的每一个元素,这里的元素就是两个字典,然后if语句会判断这每一个元素是否符合条件,然后把符合条件的给for前面的变量然后生成一个列表输出。

练一练2:使用已知列表my_list = ["A", "B", "C"]和列表推导式生成一个新列表new_list = ["AAA", "BBB", "CCC"]

my_list = ["A", "B", "C"]
result = [value * 3 for value in my_list]

print(result)


############## 输出结果 ###############
['AAA', 'BBB', 'CCC']

练一练2的代码解释: for循环遍历了已知列表my_list里的每一个元素(A、B、C),然后将这每个元素赋值给for前面的变量value再进行乘法*运算操作后生成列表返回。

练一练3:使用已知列表my_list = ["A", "B", "C"]和列表推导式生成一个新列表new_list = ["A!", "B!", "C!"]

my_list = ["A", "B", "C"]
result = [value + "!" for value in my_list]

print(result)


############## 输出结果 ###############
['A!', 'B!', 'C!']

练一练3的代码解释: for循环遍历了已知列表my_list里的每一个元素(A、B、C),然后将这每个元素赋值给for前面的变量value再进行加法+运算操作后生成列表返回。

19. 集合

集合(set): 也是一个容器类型,可以存储多个数据,但是集合里面的数据不能重复。
学习集合目的:以后可以通过集合对容器类型数据进行去重

集合特点:

  • 数据不能重复
  • 集合也是一个可变类型
  • 集合里面的数据是无序的—“无序”的意思就是,我们定义一个集合每次的输出顺序都不一样。

定义集合

my_set = {1, 3, "A", "b"}
print(my_set, type(my_set), id(my_set))

############## 输出结果 ###############
{1, 'A', 3, 'b'} <class 'set'> 2132515827272

因为集合里面的数据是无序的所以我们可以看出,输出的集合里的数据顺序和我们定义的顺序并不一致。

添加数据

使用my_set.add(2)添加数据

my_set = {1, 3, "A", "b"}
print(my_set, type(my_set), id(my_set))

my_set.add(2)
print(my_set, type(my_set), id(my_set))

############## 输出结果 ###############
{1, 'A', 3, 'b'} <class 'set'> 2266825698888
{1, 2, 3, 'b', 'A'} <class 'set'> 2266825698888

我们发现,向集合添加数据后,集合的id地址并没有发生变化,所以说集合也是一个可变类型。

知道如何向集合里面添加数据后,我们向集合里添加一个集合里本来就存在的数据,来验证一下集合里的数据不能重复:

my_set = {1, 3, "A", "b"}
print(my_set)

my_set.add("A")
print(my_set)


############## 输出结果 ###############
{1, 'b', 3, 'A'}
{1, 'b', 3, 'A'}

我们可以看到向集合里添加一个集合里本来就有的数据"A"后,输出并没有变化。我们常常使用集合里面的数据不能重复这一特性来对其他容器类型进行去重操作。

空集合的写法 — set()

空的集合不能直接使用大括号,大括号表示字典。

错误演示:

my_set = {}
print(my_set, type(my_set))

############## 输出结果 ###############
{} <class 'dict'>

我们可以看到,空集合如果直接用大括号定义,输出的类型是一个字典dict

定义空集合正确演示:

my_set = set()
print(my_set, type(my_set))


############## 输出结果 ###############
set() <class 'set'>

向空集合中添加数据:

print("==== 定义一个空集合 ======")

my_set = set()
print(my_set, type(my_set))

print("=== 空集合中添加数据:=====")

my_set.add(1)
print(my_set, type(my_set))


############## 输出结果 ###############
==== 定义一个空集合 ======
set() <class 'set'>
=== 空集合中添加数据:=====
{1} <class 'set'>

注意点:因为集合是无序的所以不能根据下标获取和修改数据。

不能根据下标获取数据的报错演示:

my_set = {1, 3, "A", "b"}

print(my_set[1])


############## 输出结果 ###############
TypeError: 'set' object is not subscriptable

不能根据下标修改数据的报错演示:

my_set = {1, 3, "A", "b"}

my_set[2] = "C"


############## 输出结果 ###############
TypeError: 'set' object does not support item assignment

集合里面只能存放不可变类型数据(字符串,数字,元组),不能存放可变类型数据。

集合里如果存放列表等可变类型的数据就会报错:

my_set = {1, "A", (1, 2), [1, 2]}
print(my_set)


############## 输出结果 ###############
TypeError: unhashable type: 'list'

集合的拆包:

value1, value2, value3 = {1, 3, "A"}
print(value1, value2, value3)

############## 输出结果 ###############
1 3 A

遍历集合:

for value in {1, 3, "A"}:
    print(value)


############## 输出结果 ###############
1
3
A

使用set对容器中的数据进行去重:

我们常常使用集合里面的数据不能重复这一特性来对其他容器类型进行去重操作。

需求:对已知列表my_list = [1, 1, 2, 4]去重。

思路:把已知列表转成集合(set) ---> 把集合再转成列表
列表转换成集合后会自动对列表里面的数据去重,再把该集合转换回列表。

my_list = [1, 1, 2, 4]

# 把列表转成集合(set)
new_set = set(my_list)
print(new_set)

# 把集合再转成列表
new_list = list(new_set)
print(new_list)



############## 输出结果 ###############
{1, 2, 4}
[1, 2, 4]

需求:对已知元组my_tuple = (1, 22, 1, 33)去重。

与上面列表的去重操作完全一样:

my_tuple = (1, 22, 1, 33)

# 把元组转成集合
new_set = set(my_tuple)
print(new_set)

# 把集合转成元组
new_tuple = tuple(new_set)
print(new_tuple)


############## 输出结果 ###############
{1, 22, 33}
(1, 22, 33)

20.高阶函数

高阶函数:函数的参数或者返回值是一个函数类型,那么这样的函数称为高阶函数

高阶函数 — 函数的参数是函数类型

def show(new_func): # 此时new_func参数就是一个函数类型

    num1 = 1
    num2 = 2

    # 调用输入过来的函数
    result = new_func(num1, num2)

    print("显示结果:", result)


# 此时调用show函数时,传入一个匿名函数。
show(lambda x, y: x - y)


############## 输出结果 ###############
显示结果: -1

show()函数括号里面的参数是一个函授,这就是一个高阶函数。调用show()函数时先回到定义函数的地方,一步一步往下执行。

高阶函数 — 函数返回值是一个函数类型

def show_info():

    # 定义子函数
    def inner_func():

        print("我是一个子函数或者内部函数")

    # 调用内部函数
    # inner_func()
    return inner_func  # 返回一个函数,函数名是inner_func
    # return inner_func()# 返回是一个函数调用后的结果 inner_func() = 》 None


new_func = show_info()

new_func()

############## 输出结果 ###############

我是一个子函数或者内部函数

python提供的高阶函数

python提供的高阶函数-reduce

reduce: 根据提供的功能函数对容器类型中每一个数据进行相关计算

import functools  # 函数工具模块

# reduce: 根据提供的功能函数对容器类型中每一个数据进行相关计算

my_list = ["A", "B", "C"]


# 定义功能函数
def calc_str(my_str1, my_str2):
    return my_str1 + my_str2

# 1. 提供的功能函数
# 2. 要计算的容器类型
result = functools.reduce(calc_str, my_list)
print(result, type(result))

############## 输出结果 ###############

ABC <class 'str'>

应用:计算1-100之间的累加和

import functools  # 函数工具模块

new_list = [value for value in range(1, 101)]
print(new_list)
# 提示:匿名函数以后都是结合高阶函数去使用
# functools.reduce(匿名函数, new_list)
result = functools.reduce(lambda x, y: x + y, new_list)
print(result)

############## 输出结果 ###############

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
5050

合并简写版:

# 提示:函数如果结合reduce高阶函数使用,那么提供函数的参数必须有两个
import functools  # 函数工具模块
result = functools.reduce(lambda x, y: x + y, range(1, 101))
print(result)

############## 输出结果 ###############

5050

python提供的高阶函数-filter高阶函数

filter 高阶函数所需要的功能函数,功能函数的参数只有一个

my_list = [1, 3, 5, 10, 11, 20]

def my_filter(value):
    return value % 2 == 1

new_filter = filter(my_filter, my_list)

print(new_filter)

# 把过滤结果转成列表
result = list(new_filter)
print(result)

############## 输出结果 ###############

<filter object at 0x00000220F4FAC388>
[1, 3, 5, 11]

简化后的写法:

my_list = [1, 3, 5, 10, 11, 20]

new_filter = filter(lambda x: x % 2 == 1, my_list)

result = list(new_filter)
print(result)

############## 输出结果 ###############

[1, 3, 5, 11]

练习:过滤出my_list里年龄大于20岁的学生信息
my_list = [{"name": "李四", "age": 20}, {"name": "王四", "age": 22}]

my_list = [{"name": "李四", "age": 20}, {"name": "王四", "age": 22}]

new_filter = filter(lambda my_dict: my_dict["age"] > 20, my_list)

result = list(new_filter)
print(result)

############## 输出结果 ###############

[{'name': '王四', 'age': 22}]
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容