机器视觉--Python补充知识
1 函数
1.1 语法格式
创建函数/定义函数
def 函数名(形参列表):函数体return 返回值
调用函数/使用函数
函数名(实参列表) // 不考虑返回值
返回值 = 函数名(实参列表) // 考虑返回值
注意:
- 函数定义并不会执行函数体内容,必须要调用才会执行,调用几次就执行几次
- 函数必须先定义, 再使用
1.2 函数参数
在函数定义的时候, 可以在 ( ) 中指定 “形式参数” (简称 形参),然后在调用的时候,由调用者把 “实际参数” (简称 实参) 传递进去。
这样就可以做到一份函数,针对不同的数据进行计算处理。
实例代码:
def calcSum(beg, end):sum = 0for i in range(beg, end + 1):sum += iprint(sum)sum(1, 100)
sum(300, 400)
sum(1, 1000)
上面的代码中, beg, end
就是函数的形参, 1, 100
/ 300, 400
就是函数的实参。
在执行 sum(1, 100) 的时候, 就相当于 beg = 1, end = 100 , 然后在函数内部就可以针对 1- 100 进行运算。
在执行 sum(300, 400) 的时候, 就相当于 beg = 300, end = 400,然后在函数内部就可以针对 300-400 进行运算。
注意:
-
一个函数可以有一个形参, 也可以有多个形参,也可以没有形参
-
一个函数的形参有几个,那么传递实参的时候也得传几个,保证个数要匹配
-
和 C++ / Java 不同,Python 是动态类型的编程语言,函数的形参不必指定参数类型,换句话说,一个函数可以支持多种不同类型的参数
def test(a):print(a)test(10) test('hello') test(True)
1.3 函数返回值
函数的参数可以视为是函数的 “输入”,则函数的返回值,就可以视为是函数的 “输出” 。
此处的 “输入”,“输出” 是更广义的输入输出,不是单纯指通过控制台输入输出。
可以把函数想象成一个 “工厂”。工厂需要买入原材料,进行加工,并生产出产品。
函数的参数就是原材料,函数的返回值就是生产出的产品。
代码示例:
#代码1
def calcSum(beg, end):sum = 0for i in range(beg, end + 1):sum += iprint(sum)calcSum(1, 100)#代码2
def calcSum(beg, end):sum = 0for i in range(beg, end + 1):sum += ireturn sumresult = calcSum(1, 100)
print(result)
这两个代码的区别就在于,前者直接在函数内部进行了打印,后者则使用 return 语句把结果返回给函数调用者,再由调用者负责打印。
-
一个函数中可以有多个
return
语句。# 判定是否是奇数def isOdd(num):if num % 2 == 0:return Falseelse:return Trueresult = isOdd(10) print(result)
-
执行到 return 语句,函数就会立即执行结束,回到调用位置。
# 判定是否是奇数 def isOdd(num):if num % 2 == 0:return Falsereturn Trueresult = isOdd(10) print(result)
如果 num 是偶数, 则进入 if 之后, 就会触发
return False
, 也就不会继续执行return True
-
一个函数是可以一次返回多个返回值的。使用 , 来分割多个返回值。
def getPoint():x = 10y = 20return x, ya, b = getPoint()
-
如果只想关注其中的部分返回值,可以使用
_
来忽略不想要的返回值。def getPoint():x = 10y = 20return x, y_, b = getPoint()
补充:
-
在C语言中如果想在函数内部,改变全局变量的值可以直接修改。
// 声明全局变量 x int x = 20;void test() {// 修改全局变量 xx = 10;// 打印函数内部的 xprintf("函数内部 x = %d\n", x); }int main() {// 调用 test 函数test();// 打印函数外部的 xprintf("函数外部 x = %d\n", x);return 0; }
在Python中如果想在函数内部,修改全局变量的值,需要使用global关键字进行声明
x = 20def test():global xx = 10print(f'函数内部 x = {x}')test() print(f'函数外部 x = {x}')
如果此处没有
global
, 则函数内部的x = 10
就会被视为是创建一个局部变量 x, 这样就和全局 变量 x 不相关了
1.4 函数栈帧
函数之间的调用关系,在 Python 中会使用一个特定的数据结构来表示,称为函数调用栈 。每次函数调用,都会在调用栈里新增一个元素,称为 栈帧。
可以通过 PyCharm 调试器看到函数调用栈和栈帧。
在调试状态下,PyCharm 左下角一般就会显示出函数调用栈。
每个函数的局部变量, 都包含在自己的栈帧中
def a():num1 = 10print("函数 a")def b():num2 = 20a()print("函数 b")def c():num3 = 30b()print("函数 c")def d():num4 = 40c()print("函数 d")d()
1.5 参数默认值
Python 中的函数,可以给形参指定默认值
带有默认值的参数,可以在调用的时候不传参
代码示例:计算两个数字的和
def add(x, y, debug=False):if debug:print(f'调试信息: x={x}, y={y}')return x + yprint(add(10, 20))print(add(10, 20, True))
运行结果:
30
调试信息:x=10, y=20
30
此处 debug=False 即为参数默认值。当不指定第三个参数的时候,默认 debug 的取值即为 False。
**注意:**带有默认值的参数需要放在没有默认值参数的后面。
1.7 关键字参数
在调用函数的时候,需要给函数指定实参。一般默认情况下是按照形参的顺序,来依次传递实参的。
但是也可以通过关键字参数,来调整这里的传参顺序,显式指定当前实参传递给哪个形参。
示例代码:
def test(x, y):print(f'x = {x}')print(f'y = {y}')test(x=10, y=20)
test(y=100, x=200)
运行结果:
x = 10
y = 20
x = 200
y = 100
形如上述 test(x=10, y=20)
这样的操作,即为关键字参数。
2 列表和元组
2.1 列表和元组的定义
编程中,经常需要使用变量,来保存/表示数据。
如果代码中需要表示的数据个数比较少,直接创建多个变量即可。
num1 = 10
num2 = 20
num3 = 30
......
但是有的时候,代码中需要表示的数据特别多,甚至也不知道要表示多少个数据。这个时候, 就需要用到列表。
列表是一种让程序员在代码中批量表示/保存数据的方式
就像去超市买辣条, 如果就只是买一两根辣条,那直接拿着辣条就走了。
但是如果一次买个十根八根的,这个时候用手拿就不好拿,超市老板就会给我们个袋子。这个袋子,就相当于列表。
元组和列表相比,是非常相似的,只是列表中放哪些元素可以修改调整,元组中放的元素是创建元组的时候就设定好的,不能修改调整。
列表就是买散装辣条,装好了袋子之后,随时可以把袋子打开,再往里多加辣条或者拿出去一些辣条。
元组就是买包装辣条,厂家生产好了辣条之后,一包就是固定的这么多,不能变动了。
2.2 创建列表
-
创建列表主要有两种方式,
[ ]
表示一个空的列表alist = [ ]#创建空列表(list)赋值给alist#这也是创建一个空列表,并将其赋值给变量 alist。list() 是调用 list 类的构造函数来创建一个空列表,和 [] 的作用是一样的 alist = list()#打印alist类型,最终打印<class.'list'> print(type(alist))
-
如果需要往里面设置初始值, 可以直接写在
[ ]
当中alist = [1, 2, 3, 4] #直接使用 print 来打印 list 中的元素内容 print(alist)
-
列表中存放的元素允许是不同的类型
#因为 list 本身是 Python 中的内建函数, 不宜再使用 list 作为变量名, 因此命名为 alist alist = [1, 'hello', True] print(alist)
2.3 访问下标
-
可以通过下标访问操作符
[ ]
来获取到列表中的任意元素alist = [1, 2, 3, 4] print(alist[2])#打印3
**注意:**下标是从 0 开始计数的,因此下标为 2 ,则对应着 3 这个元素。
-
通过下标不光能读取元素内容,还能修改元素的值
alist = [1, 2, 3, 4] alist[2] = 100 print(alist)#打印1,2,100,4
-
如果下标超出列表的有效范围,会抛出异常
alist = [1, 2, 3, 4] print(alist[100]) #运行报错
-
因为下标是从 0 开始的,因此下标的有效范围是 [0, 列表长度 - 1]。使用 len 函数可以获取到列表的元素个数
alist = [1, 2, 3, 4] print(len(alist))#打印4
-
下标可以取负数。表示 “倒数第几个元素”
alist = [1, 2, 3, 4] print(alist[3])#打印4 print(alist[-1])#打印4
alist[-1]
相当于alist[len(alist) - 1]
2.3 切片操作
通过下标操作是一次取出里面第一个元素。
通过切片, 则是一次取出一组连续的元素,相当于得到一个 子列表。
-
使用
[ : ]
的方式进行切片操作alist = [1, 2, 3, 4] print(alist[1:3])
运行结果:
[2,3]
alist[1:3]
中的 1:3 表示的是 [1, 3) 这样的由下标构成的前闭后开区间。也就是从下标为 1 的元素开始(2),到下标为 3 的元素结束(4),但是不包含下标为 3 的元素。
所以最终结果只有 2, 3
-
切片操作中可以省略前后边界
alist = [1, 2, 3, 4] print(alist[1:]) # 省略后边界, 表示获取到列表末尾 print(alist[:-1]) # 省略前边界, 表示从列表开头获取 print(alist[:]) # 省略两个边界, 表示获取到整个列表
运行结果:
[2,3,4] [1,2,3] [1,2,3,4]
-
切片操作还可以指定 “步长” ,也就是 “每访问一个元素后,下标自增几步”
alist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] print(alist[::1]) print(alist[::2]) print(alist[::3]) print(alist[::5])
运行结果:
[1,2,3,4,5,6,7,8,9,10] [1,3,5,7,9] [1,4,7,10] [1,6]
-
切片操作指定的步长还可以是负数,此时是从后往前进行取元素。表示 “每访问一个元素之后,下标自减几步”
alist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] print(alist[::-1]) print(alist[::-2]) print(alist[::-3]) print(alist[::-5])
运行结果:
[10,9,8,7,6,5,4,3,2,1] [10,8,6,4,2] [10,7,4,1] [10,5]
-
如果切片中填写的数字越界了,不会有负面效果。只会尽可能的把满足条件的元素过去到
alist = [1, 2, 3, 4] print(alist[100:200])
运行结果:
[]
2.4 遍历列表
-
最简单的办法就是使用 for 循环
alist = [1, 2, 3, 4]for elem in alist:print(elem)
-
使用 for 按照范围生成下标,按下标访问
alist = [1, 2, 3, 4]for i in range(0, len(alist)):print(alist[i])
-
使用 while 循环,手动控制下标的变化
alist = [1, 2, 3, 4]i = 0 while i < len(alist):print(alist[i])i += 1
2.5 新增元素
-
使用
append
方法, 向列表末尾插入一个元素(尾插)alist = [1, 2, 3, 4] alist.append('hello') print(alist)
运行结果:
[1,2,3,4,'hello']
-
使用
insert
方法, 向任意位置插入一个元素insert 第一个参数表示要插入元素的下标
alist = [1, 2, 3, 4] alist.insert(1, 'hello') print(alist)
运行结果:
[1,'hello',2,3,4]
PS:什么是 “方法” (method)
方法其实就是函数。只不过函数是独立存在的,而方法往往要依附于某个 “对象”。
像上述代码
alist.append
,append 就是依附于 alist,相当于是 “针对 alist 这个列表,进行尾插操 作”。
2.4 查找元素
-
使用
in
操作符, 判定元素是否在列表中存在。返回值是布尔类型alist = [1, 2, 3, 4] print(2 in alist) print(10 in alist)
运行结果:
True False
-
使用
index
方法, 查找元素在列表中的下标。返回值是一个整数。如果元素不存在,则会抛出异常。alist = [1, 2, 3, 4] print(alist.index(2)) print(alist.index(10))
运行结果:
3 异常
2.5 查找元素
-
使用
pop
方法删除最末尾元素alist = [1, 2, 3, 4] alist.pop() print(alist)
运行结果:
[1,2,3]
-
pop
也能按照下标来删除元素alist = [1, 2, 3, 4] alist.pop(2) print(alist)
运行结果:
[1,2,4]
-
使用
remove
方法, 按照值删除元素alist = [1, 2, 3, 4] alist.remove(2) print(alist)
运行结果:
[1,3,4]
2.6 连接元素
-
使用
+
能够把两个列表拼接在一起此处的
+
结果会生成一个新的列表。而不会影响到旧列表的内容。alist = [1, 2, 3, 4] blist = [5, 6, 7] print(alist + blist)#打印[1,2,3,4,5,6,7]
-
使用
extend
方法, 相当于把一个列表拼接到另一个列表的后面a.extend(b) , 是把 b 中的内容拼接到 a 的末尾. 不会修改 b, 但是会修改
alist = [1, 2, 3, 4] blist = [5, 6, 7]alist.extend(blist) print(alist)#打印[1,2,3,4,5,6,7] print(blist)#打印[5,6,7]
2.7 关于元组
元组的功能和列表相比,基本是一致的。元组使用 ( )
来表示
atuple = ( )
atuple = tuple()
元组不能修改里面的元素,列表则可以修改里面的元素
因此,像读操作,比如访问下标,切片,遍历,in,index,+ 等,元组也是一样支持的。但是,像写操作,比如修改元素,新增元素,删除元素,extend 等,元组则不能支持。
另外,元组在 Python 中很多时候是默认的集合类型。例如,当一个函数返回多个值的时候
元组不能修改里面的元素, 列表则可以修改里面的元素
def getPoint():return 10, 20result = getPoint()
print(type(result))
运行结果:
<class 'tuple'>#此处的 result 的类型, 其实是元组
问题来了,既然已经有了列表,为啥还需要有元组?
元组相比于列表来说,优势有两方面:
有一个列表,现在需要调用一个函数进行一些处理。但是你有不是特别确认这个函数是否会把你的列表数据弄乱。那么这时候传一个元组就安全很多。
马上要讲的字典,是一个键值对结构,要求字典的键必须是 “可hash对象” (字典本质上也 是一个hash表)。而一个可hash对象的前提就是不可变。因此元组可以作为字典的键,但是列表不行。
3 字典
3.1 字典介绍
字典是一种存储 键值对 的结构.
什么是键值对? 这是计算机/生活中一个非常广泛使用的概念。
把 键(key) 和 值(value) 进行一个一对一的映射,然后就可以根据键,快速找到值。
举个栗子,学校的每个同学, 都会有一个唯一的学号。
知道了学号,就能确定这个同学。
此处 “学号” 就是 “键”。这个 “同学” 就是 “值”
3.2 创建字典
-
创建一个空的字典. 使用
{ }
表示字典a = { } b = dict()
-
也可以在创建的同时指定初始值
-
键值对之间使用 ,分割,键和值之间使用:分割。 (冒号后面推荐加一个空格)
-
使用 print 来打印字典内容
#代码更美观
student = { 'id': 1, 'name': 'zhangsan' }print(student)
运行结果:
{'id':1, 'name': 'zhangsan'}
3.3 查找key
-
使用
in
可以判定 key 是否在 字典 中存在。返回布尔值。student = {'id': 1,'name': 'zhangsan', }print('id' in student) print('score' in student)
运行结果:
True False
-
使用
[ ]
通过类似于取下标的方式,获取到元素的值。只不过此处的 “下标” 是 key。 (可能是整数, 也可能是字符串等其他类型)student = {'id': 1,'name': 'zhangsan', }print(student['id']) print(student['name'])
运行结果:
1 zhangsan
-
如果 key 在字典中不存在, 则会抛出异常
3.4 增加/修改元素
使用 [ ]
可以根据 key 来新增/修改 value
-
如果 key 不存在,对取下标操作赋值, 即为新增键值对
student = {'id': 1,'name': 'zhangsan', }student['score'] = 90 print(student)
运行结果:
{'id':1, 'name': 'zhangsan', 'score':90}
-
如果 key 已经存在, 对取下标操作赋值, 即为修改键值对的值
student = {'id': 1,'name': 'zhangsan','score': 80}student['score'] = 90 print(student)
运行结果:
{'id':1, 'name': 'zhangsan', 'score':90}
3.5 删除元素
-
使用 pop 方法根据 key 删除对应的键值对
student = {'id': 1,'name': 'zhangsan','score': 80}student.pop('score') print(student)
运行结果:
{'id':1, 'name': 'zhangsan'}
3.6 遍历字典元素
-
直接使用 for 循环能够获取到字典中的所有的 key, 进一步的就可以取出每个值了
student = {'id': 1,'name': 'zhangsan','score': 80}for key in student:print(key, student[key])
运行结果:
id 1 name zhangsan score 80
3.7 取出所有key和value
-
使用
keys
方法可以获取到字典中的所有的 keystudent = {'id': 1,'name': 'zhangsan','score': 80}print(student.keys())
运行结果:
dict_keys(['id', 'name', 'score'])
此处
dict_keys
是一个特殊的类型,专门用来表示字典的所有 key。大部分元组支持的操作对于dict_keys
同样适用。 -
使用
keys
方法可以获取到字典中的所有的 keystudent = {'id': 1,'name': 'zhangsan','score': 80}print(student.values())
运行结果:
dict_values(['1', 'zhangsan', '80'])
此处
dict_values
也是一个特殊的类型,和 dict_keys 类似。 -
使用
items
方法可以获取到字典中所有的键值对student = {'id': 1,'name': 'zhangsan','score': 80}print(student.items())
运行结果:
dict_items([('id', 1), ('name', 'zhangsan'), ('scorre',80)])
此处
dict_items
也是一个特殊的类型,和 dict_keys 类似。