函数
数学中的函数相当于一种映射关系,参数通过一系列数学变换来计算生成一个结果
编程中的函数和数学中的函数是相似的,编程中的函数指的是一段可以被重复使用的代码
函数的格式
1.函数定义(分配任务)
def关键字,注意之前提到的硬性规则和软性规则
def 函数名(形参列表):
函数体(这一部分要带有一级缩进,带有缩进的代码,才是函数内部的语句)
return 返回值(函数的返回值,函数执行到retun,意味着执行完了,return后面的值,就是函数的返回值,return语句并不是必须的,可以有,也可以没有)
形参列表中可以有多个形参,多个形参(形式参数)间使用逗号分隔
2.函数调用(开始执行任务,完成任务)
第一种:
函数名(实参列表,实际参数,简称实参,此处写的实参的个数要和形参的个数匹配)
第二种:
返回值 = 函数名(实参列表)
Python中要求,函数定义写在前面,函数调用写在后面--先定义,后调用
推荐函数名这么写calc_Sum,都可以
PEP8是Python里面一套非常流行的程序规范,也是写代码中的一些软性要求
在C++/Java里,不光要求形参和实参的个数匹配,类型也要匹配
但是在Python里,只是要求个数,对类型没有要求(因为Python里存在动态类型)
所以Python里传什么都行,可以顺利运行,顺利打印出来
Python里也可以写return
def add(x, y):
return x + y
只要保证函数体里的操作能正常运行,就可以
# 在实际开发中,一般都倾向于第二种写法,有一个通用的编程原则:一个函数只做一件事! # 这样一旦后续需要改变和用户的交互方式,第二种写法就更有优势。当前是通过控制台和用户交互的 # 让逻辑和交互/界面能够分离,进一步的好处有:解耦合(避免藕断丝连的影响)。 # 在一个稍微复杂一些的程序中,经常会涉及到很多个模块,模块之间可能要进行交互,有些交互耦合性比较强,有些交互耦合性比较弱 # 我们期望尽量通过良好的涉及来让耦合降低。 # 耦合:一方产生一些变动,对另一方产生的影响 # 这种情况不算是有多个return语句的 # def test(): # return 1 # return 2 # # # 一般多个return语句是搭配 分支语句/循环语句的 # def is_odd(num): # """ # 用来判断是不是奇数,如果是奇数,则返回True,如果不是返回False # :param num: 要判定的数 # :return: 返回True/False来表示是不是奇数 # """ # if num % 2 == 0: # return False # else: # return True # # print(is_odd(10)) # print(is_odd(19)) # 写一个函数,返回平面上的一个点 # 横坐标,纵坐标 # def get_point(): # x = 10 # y = 20 # return x, y # # 就可以一次返回多个值了 # # 多元赋值 # a, b = get_point() # # #虽然现在返回了多个值,但是我只想用其中一部分,不关注其他的,可以使用下划线来占位: # _, d = get_point()# 可以写个下划线占位
函数的返回值
一个函数中是可以有多个返回语句的
当执行到return时就不再往下执行了,而是回到了调用位置(函数结束)
一个函数可以返回多个值,比起C++/Java好很多,因为这两种语言调用一个函数一次只能返回一个值。如果C++想返回多个值,需要通过输出型参数(指针/引用)。如果Java想要返回多个值,需要把多个值给包装成一个对象,返回这个对象。而Python直接用逗号分割就可以了
但是有时候,虽然返回了多个值,但是我们只想用其中的一部分,不关注其他的,可以使用下划线来占位
# 写一个函数,返回平面上的一个点
# 横坐标,纵坐标
def get_point():
x = 10
y = 20
return x, y
# 就可以一次返回多个值了
# 多元赋值
a, b = get_point()
#虽然现在返回了多个值,但是我只想用其中一部分,不关注其他的,可以使用下划线来占位:
_, d = get_point()# 可以写个下划线占位。这里把y直接赋给了d
当没有return时,执行到末尾也就结束了
变量的作用域
链式调用
把一个函数的返回值,作为另一个函数的参数
嵌套调用
一个函数的函数体内部,还可以调用其他函数。就叫做嵌套调用
一个函数里面可以嵌套调用任意多个函数
函数递归
函数自己调用自己
递归的两个要素:
1.递归结束条件
2.递归的递推公式
类似于数学归纳法:
1.初始条件
2.递归公式
递归的缺点:
1.执行过程非常复杂,难以理解
2.递归代码容易出现“栈溢出”的情况
3.递归代码一般都是可以转换成等价的循环代码的。并且循环的版本通常运行速度要比递归的版本有优势(函数调用也是有开销的)既慢又占空间
递归的优点:
代码非常简洁,尤其是处理一些“问题本身就是通过递归的方式定义的”
数据结构中的二叉树本身就是通过递归的方式定义的额,有时候处理二叉树就需要用到递归解决问题
参数默认值
Python中的函数,可以给形参指定默认值
带有默认值的参数,可以再调用的时候不传参
关键字参数
按照先后顺序来传参,这种传参风格称为“位置参数”,这是各个编程语言中最普遍的方式。
关键字传参,按照形参的名字来进行传参
位置参数和关键字参数还能混着用,只不过混着用的时候要求位置参数在前,关键字参数在后
关键字参数,一般也就是搭配摩恩参数来使用的。一个函数,可以提供很多的参数,来实现对这个函数的内部功能做出一些调整设定,为了降低调用者的使用成本,就可以把大部分参数设定出默认值(导出默认值)
当调用者需要调整其中的一部分参数的时候,就可以搭配关键字参数来进行操作
函数小结:
函数的定义
函数的调用
函数的参数传递过程
函数的执行流程(进去好近,出来不好出)
列表和元组
变量一定程度上代表着内存 空间,用来表示/存储数据
如果表示的数据少,直接定义几个变量就行了
那么需要表示的数据很多时,该怎么办呢?
列表可以管理很多元素
在Python中,列表和元组(类似于其他编程语言中的“数组”)可以用一个变量来表示很多个数据
列表和元组,大部分功能都是差不多的,但是有一个功能有非常明显的区别:
列表是可变的,创建好了之后,随时能改
元组是不可变的,创建好了之后,改不了,要想改,就只能丢弃旧的,搞个新的
列表可变,元组不可变
创建列表:
1.直接使用字面值来创建
在Python中[ ]就表示一个空的列表
a = [ ]
2.使用list()来创建
b = list()
推荐使用[]来创建列表,因为[]能方便我们在创建列表时指定初始值,元素之间用,分割
a = [1, 2, 3, 4, 5]
C++/Java里要求一个数组里只能存相同类型的变量,而Python里可以在一个列表里放不同类型的变量
列表的下标访问:
我们可以通过下标访问的方式(用到下标访问运算符[])来获取到列表中的元素
a = [ ]
把[]放到一个列表变量的后面,同时[]里写上一个整数,此时它就是下表访问运算符,[]中间写的这个证书,就是称为“下标”或者“索引”
下标是从0开始计数的
当我们下标超出有效范围时,就会出现异常
可以使用len来测列表长度
a = [1, 2, 3, 4]
print(len(a))
len可以传字符串,列表,元组,字典,自定义的类...一个函数能支持这么多类型,背后的原理:动态类型
Python中的下标其实还可以写成负数,例如写成-1,其实等价于len(a)-1
# 下标为-1时,其实等价于len(这个列表)-1 a = [1, 2, 3, 4] print(len(a)) print(len(a) - 1) print(a[len(a) - 1]) print(a[-1]) # -1这个下标就是倒数第一个元素
结果:
4
3
4
4
也可以认为-1这个下标就是倒数第一个元素
切片操作:
一次取出一组连续的元素
# 关于切片操作的基本使用 a = [1, 2, 3, 4] print(a[1:3])
切片操作中,[]里面有两个数字,表示了一段区间。
1表示开始区间的下标,3表示结束区间的下标。取下标1到3的元素,但是这里包含1不包含3,前闭后开
切片操作是可以省略前后的两个边界的
切片操作是一个比较高效的操作,进行切片的时候,只是取出了原有列表中的一个部分,并不涉及到“数据的拷贝”。假设有一个很大的列表进行切片,切片的范围也很大,即使如此,切片操作仍然非常高效(没有去拷贝什么的)
切片操作还可以指定“步长”,
# 切片操作中指定步长的操作 a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] print(a[::1])# 这里的1就是步长,指每次走的长度 print(a[::2]) print(a[1:-1:2]) print(a[::-2])# 步长的数值还可以是负数,当步长为负数时,意思是从后往前取
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 3, 5, 7, 9]
[2, 4, 6, 8]
[10, 8, 6, 4, 2]
# 当切片中的范围超出有效下标之后,不会出现异常!而是尽可能的把符合要求的元素给取到 a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] print(a[1:100])
[2, 3, 4, 5, 6, 7, 8, 9, 10]
当然,单纯访问时越界是会报错的
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # print(a[1:100]) print(a[100])
Traceback (most recent call last):
File "C:\Users\14534\Desktop\Python\code12.py", line 85, in <module>
print(a[100])
IndexError: list index out of range
遍历
把一个列表里的每个元素都一次取出来,并进行某种操作,不重不漏
遍历往往要搭配循环
在列表中插入元素
# 使用append往列表末尾新增元素 a = [1, 2, 3, 4, 5] # 此处的append是搭配列表对象a来一起使用的,而不是作为一个独立的函数 # 这种要搭配对象(变量)来使用的函数,也叫作“方法” # type,print,input,len,自定义函数...都是独立的函数 a.append(5) a.append("hello") print(a) b = [5, 6, 7, 8] b.append("world") print(b)
# 使用insert往列表的任意位置来新增元素 a = [1, 2, 3, 4] a.insert(1, "hello")# 第一个参数是要往哪个位置去插,第二个参数是要插入的元素是什么 a.insert(100, "hello")# 并不是报错或扩展列表,而是插到末尾 print(a)
在列表中查找元素
# 使用in来判断某个元素是否在列表中存在 a = [1, 2, 3, 4, 5] print(1 in a)# 存在返回True,不存在返回False print(17 in a) print(1 not in a) print(10 not in a) # 使用index来判断当前元素在列表中的位置,得到一个下标 a = [1, 2, 3, 4, 5] print(a.index(2))# 看2存不存在,如果存在,就返回2的下标,如果不存在,会抛出异常
删除列表元素
# 删除元素pop,remove a = [1, 2, 3, 4, 5] a.pop()# 不传参的话删除掉的是末尾元素 print(a) # 使用pop还能删除任意位置的元素,pop的参数可以传一个下标过去 a = [1, 2, 3, 4] a.pop(1)# 删除该下标元素 print(a) # 使用remove来按照值来删除 a = ["aaa", "bbb", "ccc", "ddd"] a.remove("ccc") print(a)
列表的拼接
# 使用“+”针对两个列表进行拼接 # a = [1, 2, 3, 4, 5] # b = [6, 7, 8, 9, 10] # c = a + b# 谁在前,谁的内容就在前,谁在后,谁的内容就在后 # print(c) # 使用extend来进行拼接 # 这个拼接是把后一个列表的内容拼接到前一个列表里面,这样做会修改原来的列表 # a = [1, 2, 3, 4, 5] # b = [6, 7, 8, 9, 10] # a.extend(b) # c = a.extend(b) # print(a) # print(b) # print(c)# 打印出的结果是None # # 关于None,这是一个特殊的变量值,表示啥都没有 # # extend其实是没有返回值的,如果拿变量接收没有返回值的返回值,就什么都没有 # # None非常类似于C里面的NULL,或Java里的null,或C++里的nullptr # 使用+=来进行拼接 a = [1, 2, 3, 4] b = [5, 6, 7, 8] a += b print(a) print(b) # 那么它和extend有什么区别呢? # a = a + b相当于把a原先的值被a+b覆盖了,相比较之下比较低效 # extend更高效
元组
创建元组
# 创建元组 # a = ()# 表示一个空的元组 # print(type(a)) # b = tuple()# 空的元组 # print(type(b)) # # 创建元祖的时候,可以指定初始值 # a = (1, 2, 3, 4) # print(a) # 元组中的元素也可以是任意类型的 # a = (1, 2, True, "hello", []) # print(a) # 通过下标来访问元组中的元素,下表也是从0开始的,到len-1结束 # a = (1, 2, 3, 4) # print(a[1]) # print(a[-1]) # print(a[100])# 抛出异常,下标越界 # 通过切片来获取元组中的一部分 # a = (1, 2, 3, 4) # print(a[1:3]) # # # # 也可以使用for循环等方式来遍历元素 # a = (1, 2, 3, 4) # for elem in a: # print(elem) # # # # 也可以使用in判断元素是否存在,使用index来查找元素的下标 # a = (1, 2, 3, 4) # print(3 in a) # print(a.index(3)) # # # 可以使用+来拼接两个元组 # a = (1, 2, 3, 4) # b = (5, 6, 7, 8) # print(a + b) # 注意,以上操作都是没有改变元组的,元组只要创建好,就不能改变了。虽然a + b是把元组拼接,但是a,b的本体是没有改变的 # 元组只是支持读操作(只读),不支持修改 a = (1, 2, 3, 4) # 所以以下这些都不行 # a[0] = 100 # a.append(5) # a.pop(0) # a.extend() # 这些涉及修改元组的都是不可行的,元组能读但是不能修改。这是它和列表的很大的区别 # 当进行多元赋值的时候,其实本质上是按照元组的方式来进行工作的 def get_point(): x = 10 y = 20 return x, y x, y = get_point()# 构造新元组 print(type(get_point()))# 这里的get_point()就是元组 # 那么为什么有了列表还要有元组呢? # 既然列表已经可读可修改了,为什么还要有元组这个只可读的呢? # 因为协同开发的时候,有时候需要程序员1实现一些功能,提供给程序员2来使用,1写好一些函数,让2去调用。 # 2在使用函数,在传参时,可能纠结一个问题,把参数传过去了,1的函数里面是否会把2的参数的内容给改了呢? # 当我们使用元组作为参数时,就可以避免这样的纠结 # 元组不能修改(也称为不可变对象)。不可变对象是可以哈希的!(一会后面再介绍)不可变对象可以哈希,意思是:它可以作为字典的那个键。 # 不可变对象天然就是线程安全的