跳至主要內容

Python

HeChuangJun约 22255 字大约 74 分钟

Python

数据类型

类型描述说明
数字(Number)支持
整数(int)
浮点数(float)
复数(complex)
布尔(bool)
整数(int),如:10、-10
浮点数(float),如:13.14、-13.14
复数(complex),如:4+3j,以结尾表示复数
布尔(bool)表达现实生活中的逻辑,即真和假,True表示真,False表示假。True本质上是一个数字记作1,False记作0
字符串(5tring)描述文本的一种数据类型字符串(string)由任意数量的字符组成
列表(List)有序的可变序列Python中使用最频繁的数据类型,可有序记录一堆数据
元组(Tuple)有序的不可变序列可有序记录一堆不可变的Python数据集合
集合(Set)无序不重复集合可无席记录一堆不重复的Pvthon数据集合
字典(Dictionary)无序Key-Value集合可无序记录一堆Key-Value型的Python数据集合

标识符命名规则

标识符命名中,只允许出现:英文、中文、数字、下划线(_)这四类元素。其余任何内容都不被允许。

不推荐使用中文、数字不可以开头

字符串格式化-数字精度控制

辅助符号"m.n"来控制数据的宽度和精度。m和.n均可省略。设置的宽度小于数字自身,不生效.

  • m,控制宽度,要求是数字(很少使用),

  • n,控制小数点精度,要求是数字,会进行小数的四舍五入

%5d:将整数的宽度控制在5位,如数字11,被设置为5d,就会变成:[空格I[空格][空格111,用三个空格补足宽度

%5.2f:表示将宽度控制为5,将小数点精度设置为2
小数点和小数部分也算入宽度计算。如,对11.345设置了%7.2f后,结果是:[空格I[空格111.35。2个空格补足宽度,小数部分限制2位精度后,四舍五入为.35

%.2f:表示不限制宽度,只设置小数点精度为2,如11.345设置%.2f后,结果是11.35

  • 表达式:一个具有明确结果的代码语句,

如1+1、type(“字符串”) 3* 5等

在变量定义的时候,如 age=11+11,等号右侧的就是表达式,也就是有具体的结果,将结果赋值给了等号左侧的变量

  • 如何格式化表达式?f"{表达式]" "%s%d%f" % (表达式、表达式、表达式)
name:"传智播客"
set_up_year =2006
stock_price =19.99
print(f"我是{name},我成立于:{set_up_year},我今天的股票价格是:{stock_price})

if判断语句

要判断的条件
	条件成立时,要做的事情

if语句的注意事项:
判断条件的结果一定要是布尔类型
不要忘记判断条件后的:冒号
归属于if语句的代码块,需在前方填充4个空格缩进

# 定义变量
age = 30
# 进行判断
if age >= 18:
	print("我已经成年了")

if 条件:
	满足条件时要做的事情1满足条件时要做的事情2满足条件时要做的事情3..(省略)...
else:
    不满足条件时要做的事情1
    不满足条件时要做的事情2不满足条件时要做的事情3
    ..(省略)...
if else语句的注意事项
else不需要判断条件,当if的条件不满足时,else执行
else的代码块,同样要4个空格作为缩进

print("欢迎来到黑马儿童游乐场,儿童免费,成人收费。)
age = int(input("请输入你的年龄:"))
if age >= 18:
	print("您已成年,游玩需要补票10元。")//条件不成立时执行
else:
	print("您未成年,可以免费游玩。")//条件成立时执行
print("祝您游玩愉快。")

if 条件1:
	条件1满足应做的事情条件1满足应做的事情
elif 条件2:
	条件2满足应做的事情条件2满足应做的事情
elif 条件N:
	条件N满足应做的事情条件N满足应做的事情
else:
	所有条件都不满足应做的事情
	所有条件都不满足应做的事情

if elif else语句的作用是?可以完成多个条件的判断
使用if elif else的注意点有:
else if可以写多个
判断是互斥且有序的,上一个满足后面的就不会判断了
可以在条件判断中,直接写input语句,节省代码量

if elif else 可以自由组合 满足缩进的要求即可

while语句

while 条件:
	条件满足时,做的事情
	(省略)
  • 条件需提供布尔类型结果,True继续,False停止
  • 空格缩进不能忘
  • 请规划好循环终止条件,否则将无限循环
  • 多层嵌套,主要空格缩进来确定层次关系

for循环语句

for 临时变量in 待处理数据集:
	循环满足条件时执行的代码

要注意,循环内的语句,需要有空格缩进
语法中的:待处理数据集,
严格来说,称之为:序列类型序列类型
指,其内容可以一个个依次取出的一种类型,包括:字符串、列表、元组
for循环语句,本质上是遍历:序列类型:

for循环将从数据集(序列)中取出的数据赋值给:临时变量
为什么是临时的呢?临时变量,在编程规范上,作用范围(作用域)只限定在for循环内部
如果在for循环外部访问临时变量:
实际上是可以访问到的
在编程规范上,是不允许、不建议这么做的

同while一样,for循环也支持嵌套使用
for循环和while循环可以相互的嵌套使用

# 定义字符串
name = "itheimaname“
# for循环处理字符串
for x in name:
	print(x)

for循环与while循环区别

  • while循环的循环条件是自定义的,自行控制循环条件
  • for循环是一种”轮询”机制,是对一批内容进行”逐个处理
  • 同while循环不同,for循环是无法定义循环条件的,只能从被处理的数据集中,依次取出内容进行处理,
  • 理论上讲,Python的for循环无法构建无限循环(被处理的数据集不可能无限大)
  • 在循环控制上:
    • while循环可以自定循环条件,并自行控制
    • for循环不可以自定循环条件,只可以一个个从容器内取出数据
  • 在无限循环上:
    • while循环可以通过条件控制做到无限循环
    • for循环理论上不可以,因为被遍历的容器容量不是无限的
  • 在使用场景上:
    • while循环适用于任何想要循环的场景
    • for循环适用于,遍历数据容器的场景或简单的固定次数循环场景

range函数

语法1:
range(num):获取一个从0开始,到num结束的数字序列(不含num本身)range(5)取得的数据是:[0,1,2,3,4]

语法2:
range(numi,num2)获得一个从num1开始,到num2结束的数字序列(不含num2本身)
如,range(5,10)取得的数据是:[5,6,7,8,9]
语法3:
range(num1,num2,step)获得一个从num1开始,到num2结束的数字序列(不含num2本身)
数字之间的步长,以step为准(step默认为1)
如,range(5,10,2)取得的数据是:[5,7,9]

for i in range(5):
    print(i)

print语句,能否访问到变量i?
规范上:不允许实际上:可以

函数

  • 函数是: 组织好的、可重复使用的、用来实现特定功能的代码段
  • 使用函数好处: 将功能封装在函数内,可重复利用提高代码的复用性,减少重复代码,提高开发效率
函数的定义:
def 函数名(传入参数):
	函数体
	return 返回值
函数的调用:
函数名(参数)

注意事项
参数如不需要,可以省略
返回值如不需要,可以省略
函数必须先定义后使用

函数定义中的参数,称之为形式参数
函数调用中的参数,称之为实际参数
函数的参数数量不限,使用逗号分隔开
传入参数的时候,要和形式参数一一对应,逗号隔开

def my_leh(data):
	count = 0;
	for i in data:
		count +=1
	print(f"字符串{data}的长度是{count}")

my_lenf(str1)

多个返回值
def test_return():
	return 1,2
x,y= test_return()
print(x)# 结果2
print(y)# 结果1
按照返回值的顺序,写对应顺序的多个变量接收即可
变量之间用逗号隔开
支持不同类型的数据return

使用方式上的不同,函数有4中常见参数使用方式:位置参数\关键字参数\缺省参数\不定长参数
位置参数:调用函数时根据函数定义的参数位置来传递参数
def user info(name, age,gender):
	print(f'您的名字是{name},年龄是{age},性别是{gender}')

user_info('TOM'20'男')
注意:传递的参数和定义的参数的顺序及个数必须一致

关键字参数:函数调用时通过“键=值”形式传递参数,
作用:可以让函数更加清晰、容易使用,可以不限参数顺序
def user info(name,age,gender)
	print(f"您的名字是:{name},年龄是:{age},性别是:{gender}")
# 关键字传参
user_info(name="小明",age=20, gender="男”)
# 可以不按照固定顺序
user_info(age=20,gender="男",name="小明")
#可以和位置参数混用,位置参数必须在前,且匹配参数顺序
user_info("小明",age=20,gender="男")
注意:函数调用时,如果有位置参数时,位置参数必须在关键字参数的前面,但关键字参数之间不存在先后顺序
          
缺省参数:也叫默认参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调用) 默认值的参数必须定义在最后
作用:当调用函数时没有传递参数,就会使用默认是用缺省参数对应的值.
def user_info(name,age,gender='男'):
	print(f'您的名字是{name},年龄是{age},性别是{gender}')

user_info("TOM',20)
user_info('Rose'18'女')

注意:函数调用时,如果为缺省参数传值则修改默认参数值,否则使用这个默认值
          
不定长参数:不定长参数也叫可变参数用于不确定调用的时候会传递多少个参数(不传参也可以)的场景
作用:当调用函数时不确定参数个数时,可以使用不定长参数
不定长参数的类型:①位置传递 ②关键字传递

def user_info(*args)
	print(args)
#("TOM",)
user_info('TOM")
#(TOM',18)
user_info('TOM',18)
注意:传进的所有参数都会被args变量收集,它会根据传进参数的位置合并为一个元组(tuple),args是元组类型,这就是位置传递
          
def user_info(kwargs)
  print(kwargs)
#{'name':TOM','age":18,"id':110}
user_info(name='TOM',age=18,id=110)
参数是“键=值”形式的形式的情况下,所有的“键=值”都会被kwargs接受,同时会根据“键=值”组成字典
位置不定长传递以*号标记一个形式参数,以元组的形式接受参数,形式参数一般命名为args
关键字不定长传递以**号标记一个形式参数,以字典的形式接受参数,形式参数一般命名为kwargs

函数作为参数传递
函数都是接受数据作为参数传入:数字\字符串\字典、列表、元组等
函数本身,也可以作为参数传入另一个函数内。
将函数传入的作用在于:传入计算逻辑,而非传入数据。

def test func(compute)
	result =compute(1,2)
    print(result)
def commpute(x, y) :
    return x+y
test func(compute)#结果:3
函数compute,作为参数,传入了testfunc函数中使用,
test_func需要一个函数作为参数传入,这个函数需要接收2个数字进行计算,计算运辑由这个被传入函数决定
compute函数接收2个数字对其进行计算,compute函数作为参数,传递给了test_func函数使用
最终,在test func函数内部,由传入的compute函数,完成了对数字的计算操作所以,这是一种,
计算逻辑的传递,而非数据的传递
就像上述代码那样,不仅仅是相加,相见、相除、等任何逻辑都可以自行定义并作为函数传入。

函数的定义中
def关键字,可以定义带有名称的函数
lambda关键字,可以定义匿名函数(无名称)
有名称的函数,可以基于名称重复使用
无名称的匿名函数,只可临时使用一次,

匿名函数定义语法:
lambda 传入参数:
    函数体(一行代码)
lambda 是关键字,表示定义匿名函数传入参数表示匿名函数的形式参数,如:xy表示接收2个形式参数
函数体就是函数的执行逻辑,要注意:只能写一行,无法写多行代码
          
如下图代码,我们可以:
通过def关键字,定义一个函数,并传入,
def test_fun(compute):
	result = compute(12)
	print(result)
def compute(x,y):
  	return x+y
test_func(compute) #结果:3

也可以通过lambda关键字,传入一个一次性使用的lambda匿名函数
def test_func(compute):
  	result=compute(12)
  	print(result)
test_func(lambda x,y:x+y) #结果:3
只是lambda关键字定义的函数是匿名的,无法二次使使用def和使用lambda,定义的函数功能完全一致,

  • 函数说明文档:函数添加说明文档,辅助理解函数的作用,通过多行注释的形式,对函数进行说明解释。为容应写在函数体之前
  • 在PyCharm编写代码时,可以通过鼠标悬停,查看调用函数的说明文档
def func(x, y):
    """
    函数说明
    :param x:形参x的说明
    :param y:形参y的说明
    :return:返回值的说明
    """
    函数体
    return 返回值

# 定义函数,进行文档说明
def add(x,y):
    """
    add函数可以接收2个参数,进行2数相加的功能
    :param x:形参x表示相加的其中一个数字
    :param y:形参y表示相加的另一个数字
    :return:返回值是2数相加的结果
    """
    result = x+y
    print(f"2数相加的结果是:{result}")
    return result

None

  • 如果函数没有使用return语句返回数据,那么函数有返回值吗?实际上是:有的。Python有一个特殊的字面量:None,其类型是:<class'oneType)> 无返回值的函数,实际上就是返回了:None这个字面量

  • None表示:空的、无实际意义的意思。函数返回的None就表示,这个函数没有返回什么有意义的内容。也就是返回了空的意思

  • None类型的应用场景

    • 用在函数无返回值上

    • 用在if判断上:在if判断中,None等同于False,一般用于在函数中主动返回None,配合if判断做相关处理

    • 用于声明无内容的变量上:定义变量,但暂时不需要变量有具体值,可以用None来代替

暂不赋予变量具体值
name = None

变量作用域

变量作用域指的是变量的作用范围(变量在哪里可用,在哪里不可用)

主要分为两类:局部变量和全局变量
所谓局部变量是定义在函数体内部的变量,即只在函数体内部生效

def testA():
	num = 100
	print(num)
testA() # 100
print(num), 报错:name 'num'is not defined

变量a是定义在testA函数内部的变量,在函数外部访问则立即报错

司部变量的作用:在函数体内部,临时保用它能们做密局部

所谓全局变量,指的是在函数体内、外都能生效的变量

思考:如果有一个数据,在函数A和函数B中都要使用,该怎么办?

答:将这个数据存储在一个全局变量里面

#定义全局变量a
num = 100

def testA():
	print(num)#访问全局变量num,并打印变量num存储的数据
def testB():
	print(num)#访问全局变量num,并打印变量num存储的数据
testA() # 100
testB() # 100
你只需要将变量定义在函数的外面

global关键字:可以在函数内部声明变量为全局变量,如下所示

num=100

def testA():
	print(num)

def testB():

	# globa] 关键字声明a是全局变量global num
​	num = 200print(num)

testA()#结果:100
testB()#结果:200

print(f'全局变量num ={num}")# 结果:全局变量num=200

数据容器

一种可以存储多个元素的Python数据类型

数据容器分类

list(列表)、tuple(元组)、str(字符串)、set(集合)、dict(字典)它们各有特点,但都满足可容纳多个元素的特性,

列表

# 字面量
[元素1,元素2,元素3,元素4...]

# 定义变量
变量名称 =[元素1,元素2,元素3,元素4...]

# 定义空列表
变量名称 =[]
变量名称 = 1ist()

列表内的每一个数据,称之为元素
以[]作为标识
列表内每一个元素之间用,逗号隔开

案例演示:使用几的方式定义列表
name_list =['itheima', "itcast', 'python']
print(name_list)
print(type(name_list))

["itheima','itcast','python"]
<class 'list'>

my_list=['itheima',666,True]
print(my_list)

print(type(my_list))
['itheima', 666, True]
<class 'list'>

注意事项
列表可以一次存储多个数据,且可以为不同的数据类型,支持嵌套
元素的数据类型没有任何限制,甚至元素也可以是列表,这样就定义了嵌套列表

列表的下标索引是什么?
列表的每一个元素,都有编号称之为下标索引
从前向后的方向,编号从0开始递增
从后向前的方向,编号从-1开始递减

如何通过下标索引取出对应位置的元素呢?
列表[下标],即可取出

下标索引的注意事项!
要注意下标索引的取值范围,超出范围无法取出元素,并且会报错

查找某元素的下标功能:
查找指定元素在例表的下标.如果找不到,报错ValueError
语法:列表.index(元素)
index就是列表对象(变量)内置的方法(函数)

追加元素方式1:
语法:列表.insert(obj,index)插入元素
my_list=[123]
my_list.insert(4,2)
print(my_list) #结果:[1,2,4, 3]

追加元素方式2:
语法:列表.extend(其它数据容器),将其它数据容器的内容取出,依次追加到列表尾部
my_list=[123]
my_list.extend([456])
print(my_list) #结果:[1,2,3,4,5,6]

删除元素:
语法1: del 列表[下标]
语法2:列表.pop(下标)

删除某元素在列表中的第一个匹配项
语法:列表.remove(元素)
my_list=[12323]
my_list.remove(2)
print(my_list) # 结果:[1,3,2,3]

清空列表内容,
语法:列表.clear()
my_1ist=[123]
my_1ist.clear()
print(my_ist)#结果[]

统计某元素在列表内的数量
语法:列表.coun(元素)
my_list=[11123]
print(my_list.count(1)) # 结果:3
            
统计列表内,有多少元素
语法:len(列表)
可以得到一个int数字,表示列表内的元素数量
my_list=[12345]
print(len(my_1ist)) # 结果5
            
列表的遍历-while循环或for循环
将容器内的元素依次取出进行处理的行为,称之为:遍历、选代
如何遍历列表的元素呢?用while循环
如何在循环中取出列表的元素呢?使用列表[下标]的方式取出
循环条件如何控制?定义一个变量表示下标,从0开始 循环条件为 下标值<列表的元素数量
index = 0
while index<len(列表):
    元素=列表[index]
    对元素进行处理
    index += 1

lef list_for_func():
    """
    使用for循环遍历列表的演示函数
    :return: None
    """

    my_list =[12345]
    # for 临时变量 in 数据容器:
    for element in my_list:
        print(f"列表的元素有:{element}")
使用方式作用
列表.append(元素)向列表中追加一个元素
列表.extend(容器)将数据容器的内容依次取出,追加到列表尾部
列表.insert(下标,元素)在指定下标处,插入指定的元索
del 列表[下标]删除列表指定下标元素
列表.pop(下标)删除列表指定下标元素
列表.remove(元素)删除列表指定下标元素从前向后,删除此元素第一个匹配项
列表.clear()清空列表
列表.count(元素)统计此元素在列表中出现的次数
列表.index(元素)查找指定元素在列表的下标找不到报错ValueError
len(列表)统计容器内有多少元素

元组

列表是可以修改的。如果想要传递的信息,不被篡改列表就不合适了

元组同列表一样,都是可以封装多休同类型的元素在内。但最大的不同点在于
元组一旦定义完成,就不可修改

特点:

  • 可以容纳多个数据
  • 可以容纳不同类型的数据(混装)
  • 数据是有序存储的(下标索引)
  • 允许重复数据存在
  • 不可以修改(增加或删除元素等)
  • 支持for循环

多数特性和list一致,不同点在于不可修改的特性,

元组定义:定义元组使用小括号,且使用逗号隔开各个数据,数据可以是不同的数据类型。
# 定义元组字面量
(元素,元素,…...…-,元素)2
# 定义元组变量
变量名称 = (元素,元素,.....,元素)
# 定文空元组
变量名称 =() #方式1
变量名称 = tuple() #方式2

# 定义3个元素的元组
t1 =(1,'Hello', True)
# 定义1个元素的元组
t2 =('Hello',)
注意:元组只有一个数据,这个数据后面要添加逗号

元组也支持嵌套:
# 定义一个嵌套元组
t1 = ((1,2,3),(4.5,6))
print(t1[0][0]) #结果: 1

#根据下标(索引)取出数据
t1 = (1,2,'he11o’)
print(t1[2]) #结果:’he]1o

# 根据index(),查找特定元素的第一个匹配项,如果数据存在返回对应的下标,否则报错
t1=(1,2,'he11o’,3,4,'he11o’)
print(tl.index('he11o’)) # 结果: 2

# 统计某个数据在元组内出现的次数
t1=(1,2,'he11o’,3,4,'he11o’)
print(t1.count('he71o’)) # 结果:2

#统计元组内的元素个数
t1=(1,2,3) 
print(len(t1)) #结果 3

# 元组的遍历:while
index = 0
while index< len(t8):
	print(f"元组的元素有:{t8[index]}")
    # 至关重要
    index += 1
# 元组的遍历:for
for element in t8:
	print(f"2元组的元素有:{element}")

# 不可以修改元组的内容,否则会直接报错
# 尝试修改元组内容
t1=(1,2,3)
t1[0] = 5

Traceback(most recent call last):
File "p:\python-learn05_数据容器\test.py",line 3,in <module>
t1[0]=5
TypeError:tuple" object does not support item assignment

可以修改元组内的list的内容(修改元素、增加、删除、反转等)
t1 = (1,2,[3,4])
t1[2][1]=6

字符串

字符串同样也是数据容器的一员,看做是字符的容器,支持下标索引等特性

字符串是字符的容器,一个字符串可以存放任意数量的字符

和其它容器如:列表、元组一样,字符串也可以通过下标进行访问

从前向后,下标从0开始从后向前,下标从-1开始

# 通过下标获取特定位置字符
name = "itheima"
print(name[0]) # 结果i
print(name[-1]) #结果a

同元组一样,字符串是一个:无法修改的数据容器

所以:

修改指定下标的字符(如:字符串[0]=“a”)

移除特定下标的字符(如:del字符串[0]、字符串.remove()、字符串.pop()等)

追加字符等(如:字符串.append())

均无法完成。如果必须要做成者说我们去移除特定的标字符

  • 只可以存储字符串
  • 长度任意取决于内存大小)
  • 支持下标索引
  • 允许重复字符串存在不可以修改(增加或删除元素等)
  • 支持for循环
查找特定字符串的下标索引值语法:字符串.index(字符串)
my_str ="itcast and itheima"
print(my_str.index("and")) # 结果7

字符串的替换
语法:字符串.replace(字符串1,字符串2)
字符串1.替换为字符串2
功能:将字符串内的全部:注意:不是修改字符串本身,而是得到了一个新字符串

子符串的分割
语法:字符串.split(分隔符字符串)
功能:按照指定的分隔符字符串,将字符串划分为多个字符串,并存列表中
字符串本身不变,而是得到了一个列表对象主意
"hello python itheima itcast"
["hello", "python", "itheima",itcast"]

字符串的规整操作(去前后空格)
语法:字符串.strip()
my_str =" itheima and itcast "
print(my_str.strip())# 结果:"itheima and itcast

字符串的规整操作(去前后指定字符串)
语法:字符串.strip(字符串)
my_str ="12itheima and itcast12"
print(my_str.strip("12"))# 结果:"itheima and itcast"
注意,传入的是“12”其实就是:”1”和”2”都会移除,是按照单个字符,

# 统计字符串中某字符串的出现次数,
countmy_str = "itheima and itcast"
count = my_str.count("it")
print(f"字符串{my_str}中it出现的次数是:{count}")
# 统计字符串的长度,len()
num = len(my_str)
print(f"字符串{my_str}的长度是:{num}")

my_str ="黑马程序员"
for i in my_str:
	print(i)

my_str ="黑马程序员"
index = 0
while index<len(my_str)
	print(my_str[index])
	index += 1

字符串是按位比较,也就是一位位进行对比,只要有一位大,那么整体就大。
单个字符之间如何确定大小?通过ASCII码表,确定字符对应的码值数字来确定大小
操作说明
字符串[下标]根据下标索引取出特定位置字符
字符串.index(字符串)查找给定字符的第一个匹配项的下标
字符串.ireplace(字符串1,字符串2)将字符串内的全部字符串1,替换为字符串2
不会修改原字符串,而是得到一个新的
字符串.split(字符串)按照给定字符串,对字符串进行分隔不会修改原字符串,而是得到一个新的列表
字符串.strip() 字符串.strip(字符串)移除首尾的空格和换行符或指定字符串
字符串.count(字符串)统计字符串内某字符串的出现次数
len(字符串)统计字符串的字符个数

序列

序列是指:内容连续、有序,可使用下标索引的一类数据容器

列表、元组、字符串,均可以可以视为序列。

序列的常用操作-切片
序列支持切片,即:列表、元组、字符串,均支持进行切片操作

切片:从一个序列中,取出一个子序列
语法:序列[起始下标:结束下标:步长]

表示从序列中,从指定位置开始,依次取出元素,到指定位置结束,得到一个新序列

起始下标表示从何处开始,可以留空,留空视作从头开始
结束下标(不含)表示何处结束,可以留空,留空视作截取到结尾

步长表示,依次取元素的间隔
步长1表示,一个个取元素
步长2表示,每次跳过1个元素取
步长N表示,每次跳过N-1个元素取
步长为负数表示,反向取(注意,起始下标和结束下标也要反向标记)

注意,此操作不会影响序列本身,而是会得到一个新的序列(列表、元组、字符串)

#对list进行切片,从1开始,4结束,步长1
my_list=[0,1,2,3,4,5,6]
result1 = my_list[1:4]
print("结果1:{result1}") #步长默认是1,所以可以省略不写 结果1:[1,2,3]

# 对tuple进行切片,从头开始,到最后结束,步长1
my_tuple=(0,1,2,3,4,5,6)
result2 = my_tuple[:] # 起始和结束不写表示从头到尾,步长为1可以省略
print(f"结果2:{result2}") # 结果2:(0,1、2,3,4,5,6)

# 对str进行切片,从头开始,到最后结束,步长2
my_str ="01234567"
result3 = my_str[::2]
print(f"结果3:{result3}")
# 对str进行切片,从头开始,到最后结束,步长-1
my_str ="01234567"
result4 = my_str[::-1]
print(f"结果4:{result4}") 等同于将序列反转

#对列表进行切片,从3开始,到1结束,步长-1
my_list =[0,h,2,3,4,5,6]
result5 = my_list[3:1:-1]
print(f"结果5:{result5}")

#对元组进行切片,从头开始,到尾结束,步长-2
my_tuple=(d,1,2,3,4,5,6)
resultó = my_tuple[::-2]
print(f"结果6:{result6}")

集合

列表可修改、支持重复元素且有序元组、字符串不可修改、支持重复元素且有序

局限就在于:它们都支持重复元素,如果场景需要对内容做去重处理,列表、元组、字符串就不方便了

  • 可以容纳多个数据
  • 可以容纳不同类型的数据(混装)
  • 数据是无序存储的(不支持下标索引,不能用while循环遍历)
  • 不允许重复数据存在
  • 可以修改(增加或删除元素等)
  • 支持for循环遍历
基本语法:
# 定义集合字面量
{元素,元素,元素,......}
# 定义集合变量
变量名称 ={元素,元素,元素}
# 定义空集合
变量名称 = set()

首先,因为集合是无序的,所以集合不支持:下标索引访问
但是集合和列表一样,是允许修改的
添加新元素
语法:集合.add(元素)。将指定元素,添加到集合内
结果:集合本身被修改,添加了新元素
my_set = {"He11o","world"}
my_set.add("itheima")
print(my_set) #结果{'He11o',"itheima","wor1d'}

移除元素
语法:集合.remove(元素),将指定元素,从集合内移除
结果:集合本身被修改,移除了元素
my_set = {"He11o","itheima","wor1d'}
my_set.remove("He11o")
print(my_set) # 结果{"world","itheima'}

从集合中随机取出元素
语法:集合.pop()功能,从集合中随机取出一个元素
结果:会得到一个元素的结果。同时集合本身被修改,元素被移除
my_set ={"He11o","world","itheima"}
element = my_set.pop()
print(my_set) # 结果 {'world','itheima'}
print(element) # 结果'He11o'

清空集合
语法:集合.clear(),功能,清空集合
结果:集合本身被清空
my_set ={"He11o","wor7d""itheima"}
my_set.clear()
print(my_set) # 结果{}

取出2个集合的差集
吾法:集合1.difference(集合2),
功能:取出集合1和集合2的差集(集合1有而集合2没有的)
结果:得到一个新集合,集合1和集合2不变
set1 = {1,2,3}
set2 = {1,5,6}
set3 = set.difference(set2)
print(set3) #结果:{2,3} 得到的新集合
print(set1) #结果:{1,2,3}不变
print(set2) # 结果:{1,5,6}不变

消除2个集合的差集
语法:集合1.difference_update(集合2)
功能:对比集合1和集合2,在集合1内,删除和集合2相同的元素
结果:集合1被修改,集合2不变
set1 = {1,2,3}
set2 = {1,5,6}
set1.difference_update(set2)
print(set1) #结果:{2,3}
print(set2) # 结果:{1,5,6}

2个集合合并
语法:集合1.union(集合2)
功能:将集合1和集合2组合成新集合
结果:得到新集合,集合1和集合2不变
set1 = {1,2,3}
set2 = {1,5,6}
set3 = set1.union(set2)
print(set3) #结果:{1,2,3,5,6},新集合
print(set1) #结果:{1,2,3},set1不变
print(set2) #结果:{1,5,6},set2不变

统计集合元素数量len()
set1={1,2,3,4,5,1,2,3,4,5}
num = len(set1)
print(f"集合内的元素数量有:{num}个")

#集合的遍历
#集合不支持下标索引,不能用while循环
# 可以用for循环
set1 = {1,2,3,4,5}
for element in set1:
	print(f"集合的元素有:{element}")

操作说明
集合.add(元素)集合内添加一个元素
集合.femoye(元素)移除集合内指定的元素
集合 pop()从集合中随机取出一个元素
集合.clear()将集合清空
集合1.difference(集合2)得到一个新集合,内含2个集合的差集原有的2个集合内容不变
集合1.difference update(集合2)在集合1中,删除集合2中存在的元素集合1被修改,集合2不变
集合1.union(集合2)得到1个新集合,内含2个集合的全部元素原有的2个集合内容不变
len(集合)得到一个整数,记录了集合的元素数量

字典

  • 可以容纳多个数据
  • 可以容纳不同类型的数据
  • 每一份数据是KeyValue键值对
  • 可以通过Key获取到Value,Key不可重复(重复会盖)
  • 不支持下标索引
  • 可以修改(增加或删除更新元素等)
  • 支持for循环,不支持while循环
  • 新增和更新元素的语法一致,如果Key不存在即新增,如果Key存在即更新(Key不可重复)

字典的定义,同样使用{},不过存储的元素是一个个的:键值对,如下语法:

# 定义字典变量
my_dict = {key: value,key: value,key: value}
# 定义空字典
my_dict ={} # 空字典定义方式1
my_dict = dict() # 空字典定义方式2

#定义重复ey的字典
my_dict1 ={"王力鸿“:99,"王力鸿":88,"林俊节": 77}
print(f"重复key的字典的内容是:{my_dict1")# 重复key的字典的内容是:{王力鸿':88,'林俊节':77}

字典同集合一样,不可以使用下标索引

但是字典可以通过Key值来取得对应的Value

# 语法,字典[Key]可以取到对应的Value
stu_score ={"王力鸿":99,"周杰轮":88,"林俊节”:77}
print(stu_score["王力鸿”])#结果99
print(stu_score["周杰轮"])#结果88
print(stu_score["林俊节"])#结果77


字典的Key和Value可以是任意数据类型(Key不可为字典)
那么,就表明,字典是可以嵌套

新增元素
语法:字典[Key]=Value,结果:字典被修改,新增了元素
stu_score = {
"王力鸿":77
"周杰轮”:88,
"林俊节":99
}
# 新增:张学油的考试成绩
stu_score['张学油']= 66
print(stu_score)8 # 结果:{'王力鸿':77,'周杰轮':88,'林俊节":99,'张学油':66}

更新元素
语法:字典[Key]=Value,结果:字典被修改,元素被更新注意:!
字典Key不可以重复,所以对已存在的Key执行上述操作,就是更新Value值

stu_score = {
"王力鸿":77
"周杰轮”:88,
"林俊节":99
}
# 更新:王力鸿的考试成绩
stu_score['王力鸿']= 100
print(stu_score)

删除元素
语法:字典.pop(Key)
结果:获得指定Key的Value,同时字典被修改,指定Key的数据被删除
stu_score = {
"王力鸿":77
"周杰轮”:88,
"林俊节":99
}
value = stu_scare.pop("王力鸿")
print(value) # 结果:77
print(stu_score) # 结果:{“周杰轮":88,“林俊节":99}

清空字典
语法:字典.clear(),结果:字典被修改,元素被清空
stu_score = {
"王力鸿":77
"周杰轮”:88,
"林俊节":99
}
stu_score.clear()
print(stu_score) # 结果: {}

获取全部的key
语法:字典..keys() 结果:得到字典中的全部Key
stu_score = {
"王力鸿":77
"周杰轮”:88,
"林俊节":99
}
keys = stu_score.keys()
print(keys) # 结果: dict_keys(['王力鸿',"周杰轮',"林俊节’])

# 遍历字典
# 方式1: 通过获取到全部的key来完成遍历
for key in keys:
	print(f"字典的key是:{key}")
	print(f"字典的value是:{my_dict[key]}")
#方式2:直接对字典进行for循环,每一次环都是直接得到key
for key in mydict:
	print(f"2字典的key是:{key}")
	print(f"2字典的valve是:{my_dict[key]}")

# 统计字典内的元素数量,len()函数
num = len(my_dict)
print(f"字典中的元素数量有:{num}个")
操作说明
字典[Key]获取指定Key对应的Value值
字典[Key]= Value添加或更新键值对
字典.pop(Key)取出Key对应的Value并在字典内删除此Key的键值对
字典.clear()清空字典
字典.keys()获取字典的全部Key,可用于for循环遍历字典
len(字典)计算字典内的元素数量

数据容器的分类

是否支持下标索引
支持:列表、元组、字符串-序列类型

​ 不支持:集合、字典-非序列类型

是否支持重复元素:
支持:列表、元组、字符串-序列

​ 类型不支持:集合、字典-非序列类型
是否可以修改
​ 支持:列表、集合、字典

​ 不支持:元组、字符串

列表元组字符串集合字典
元素数量支持多个支持多个支持多个支持多个支持多个
元素类型任意任意仅字符任意Key: Value
Key:除字典外任意类型
Value:任意类型
下标索引支持支持支持不支持不支持
重复元素支持支持支持不支持不支持
可修改性支持不支持不支持支持支持
数据有序
使用场景可修改、可重复的一批数据记录场景不可修改、可重复的一批数据记录场景一串字符的记录场景不可重复的数据记录场景以Key检索Value的数据记录场景

基于各类数据容器的特点,它们的应用场景如下:

  • 列表:一批数据,可修改、可重复的存储场景
  • 元组:一批数据,不可修改、可重复的存储场景
  • 字符串:一串字符串的存储场景
  • 集合:一批数据,去重存储场景
  • 字典:一批数据,可用Key检索Value的存储场景

数据容器的通用操作

首先,在遍历上:
5类数据容器都支持for循环遍历
列表、元组、字符串支持while循环,集合、字典不支持(无法下标索引)
尽管遍历的形式各有不同,但是,它们都支持遍历操作。

len(容器)、max(容器)、min(容器)

list(容器) 将给定容器转换为列表
tuple(容器) 将给定容器转换为元组
str(容器) 将给定容器转换为字符串
set(容器) 将给定容器转换为集合

sorted(容器,[reverse=True])将给定容器进行排序

容器.sort(key=选择排序依据的函数,reverse=TruelFalse)
参数key,是要求传入一个函数,表示将列表的每一个元素都传入函数中,返回排序的依据

参数reverse,是否反转排序结果,True表示降序,False表示升序

带名函数形式
如下嵌套列表,要求对外层列表进行排序,排序的依据是内层列表的第二个元素数宇
以前学习的sorted函数就无法适用了。可以使用列表的sort方法
my_list =[["a",33],["b",55],["c",11]]
#定义排序方法
def choose_sort_key(element):
	return element[1]
将元素传入choose sort key函数中,用来确定按照谁来排序
my_list.sort(key=choose_sort_key, reverse=True)
print(my_list)

匿名lambda形式
my_list =[["a",33],["b",55],["c",11]]
my_list.sort(key=lambda element: element[1], reverse=True)
print(my_list)

Class类

45.png
45.png
设计类
现实世界的事物也有属性和行为,类也有属性和行为。
使用程序中的类,可以完美的描述现实世界的事物

类只是一种程序内的“设计图纸”,需要基于图纸生产实体(对象),才能正常工作这种套路,称之为:面向对象编程
这就是面向对象编程设计类,基于类创建对象,由对象做具体的工作

可以看到,在方法定义的参数列表中,有一个:self
关键字self关键字是成员方法定义的时候,必须填写的。
它用来表示类对象自身的意思
当我们使用类对象调用方法的是,self会自动被python传入
在方法内部,想要访问类的成员变量,必须使用self
self关键字,尽管在参数列表中,但是传参的时候可以忽略它

cLass student:
	name = none 类属性 成员变量 
	def add(self,x,y): 成员方法
		returnx+y
	def __init__(self, name): 有这个函数,属性可以省略直接构造函数里面声明也行
		self.name = name
方法和函数功能一样,有传入参数,有返回值,只是方法的使用格式不同:
函数的使用:num = add(12)
创建对象
student = Student()
方法的使用:
num = student.add(1,2)
对象属性赋值
student.name ="周杰轮"

__init__()方法,称之为构造方法
在创建类对象(构造类)的时候,会自动执行
在创建类对象(构造类)的时候,将传入参数自动传递给__init__方法使用,
构造方法名称千万不要忘记init前后都有2个下划线
构造方法也是成员方法,不要忘记在参数列表中提供:self
在构造方法内定义成员变量,需要使用self关键字
这是因为:变量是定义在构造方法内部,如果要成为成员变量,需要用seLf来表示。

init 构造方法,是Python类内置的方法之一这些内置的类方法,各自有各自特殊的功能,称之为:魔术方法
__init__ 构造方法,可用于创建类对象的时候设置初始化行为
__str__ 字符串方法
__lt__ 用于2个类对象进行小于或大于比较
__le__ 用于2个类对象进行小于等于或大于等于比较
__eq__ 用于2个类对象进行相等比较


print(student)#结果:<_main_.student object at 0x000002200CFD7040>
print(str(student))#结果:<__main_.student object at 0x000002200CFD7040>
当类对象需要被转换为字符串之时,会输出如上结果(内存地址)
内存地址没有多大作用,我们可以通过str 方法,控制类转换为字符串的行为。
def __str__():
	return ("fstudent类对象,name={self.name},age={self.age}")

直接对2个对象进行比较是不可以的,但是在类中实现__lt__方法,即可同时完成:小于符号 和大于符号2种比较
def __1t__(self,other):
	return self.age < other.age

魔术方法: le 可用于:<=>=两种比较运算符上。
def __1e__(self,other):
	return self.age <= other.age

不实现 eq 方法,对象之间可以比较,但是是比较内存地址,也即是:不同对象==比较一定是False结果。
实现了 eq 方法,就可以按照自己的想法来决定2个对象是否相等了
def __eq__(self, other):
    return self.age == other.age

面向对象的三大特性

面向对象编程,是许多编程语言都支持的一种编程思想,

简单理解是:基于模板(类)去创建实体(对象),使用对象完成功能开发

面向对象包含3大主要特性: 封装、继承、多态

46.png
46.png

封装

表示的是,将现实世界事物的:属性、行为封装到类中,描述为:成员变量、成员方法

对用户隐藏的属性和行为,比如手机隐藏程序调度,驱动等信息,只暴露上网,通话等

类中提供了私有成员的形式来支持。私有成员变量、私有成员方法
定义私有成员的方式非常简单,

只需要私有成员变量:变量名以__开头(2个下划线)__私有成员方法:方法名以__开头(2个下划线)即可完成私有成员的设置

私有变量赋值不报错,但无效获取

私有变量值报错,无法使用

私有成员的访问限制? 类对象无法访问私有成员 类中的其它成员可以访问私有成员

什么是私有成员?为什么需要私有成员?
现实事物有部分属性和行为是不公开对使用者开放的。同样在类中描述属性和方法的时候也需要达到这个要求,就需要定义私有成员了

继承

继承分为:单继承和多继承
使用如图语法,可以完成类的单继承。
继承表示:将从父类那里继承(复制)成员变量和成员方法(不含私有)

  • 多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级即:先继承的保留,后继承的被覆盖,先继承的优先级高于后继承
  • 子类构建的类对象,可以有自己的成员变量和成员方法使用父类的成员变量和成员方法
class Phone:
	IMEI = None #序列号
	producer = None #厂商
    def ca11_by_4g(self):
        print("4g通话”)

class Phone2022(Phone):
	face_id = True #面部识别
	def ca11_by_5g(self):
		print("2022最新5g通话")
# 单继承
class 类名(父类名):
	类内容体
# 多继承
class 类名(父类1,父类2,………….·,父类N)
	类内容体 //pass补全语法
	

复写
对父类的成员属性或成员方法进行重新定义
在子类中重新定义同名的属性或方法即可。

注意:只可以在子类内部调用父类的同名成员,子类的实体类对象调用默认是调用子类复写的
class phone:
	IMEI = NOne
	producer="ITCAST"
    def cal1_by_5g(self):
        print("父类的5g通话")
class Myphone(phone):
	proucer ="ITHEIMA' #复写父类属性
    def call_by_5g(self): #复写父类方法
        print("子类的5g通话") 
调用父类同名成员
一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
如果需要使用被复写的父类的成员,需要特殊的调用方式:

方式1:
调用父类成员
使用成员变量:父类名.成员变量
使用成员方法:父类名.成员方法(self)

方式2:
使用super()调用父类成员
使用成员变量:super().成员变量
使用成员方法:super().成员方法()
class Phone:
    IMEI = None #序列号
    producer ="ITCAST" #厂商
	def ca11_by_5g(se1f):
		print(“父类的5g通话")
class MyPhone(Phone):
	proucer ="ITHEIMA"
	def ca11_by_5g(se1f):
    #方式1调用父类成员
    print(f"父类的品牌是:{Phone.producer}")
    Phone.ca11_by_5g(se1f)
    
    #方式2调用父类成员
    print(f"父类的品牌是:{super().producer}")
    super().ca11_by_5g()
    print("子类的5g通话")

类型注解

在PyCharm中编写代码,经常能够见到提示,自动提示可用方法

思考,为什么PyCharm工具能够做到这一点?
因为:PyCharm确定这个对象,是list类型

Python在3.5版本的时候引入了类型注解,以方便静态类型检查工具,IDE等第三方工具。

类型注解:在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明)

主要功能:
帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码提示帮助开发者自身对变量进行类型注释(备注)

并不会真正的对类型做验证和判断。也就是,类型注解仅仅是提示性的,不是决定性的支持:

类型注解支持:

变量的类型注解
函数(方法)形参列表和返回值的类型注解

类型注解的语法
为变量设置类型注解
方式1: 变量:类型

注意:元组类型设置类型详细注解,需要将每一个元素都标记出来

字典类型设置类型详细注解,需要2个类型,第一个是key第二个是value

一般,无法直接看出变量类型之时会添加变量的类型注解,见明知义的不用浪费时间添加

基础数据类型注解
var_1:int =10
var_2:float=3.1415926
var_3:bool = True
var_4:str ="itheima"

my_list:ist=[1,2,3]
my_tuple:tuple=(1,2,3)
my_set:set={1,2,3}
my_dict:dict={"itheima":666}
my_str: str ="itheima"

类对象类型注解
class student:
pass
stu: student = student()

容器类型详细注解
my_list:1ist[int]=[1,2,3]
my_tuple:tuple[str,int,bool] = ("itheima",666,True)
my_set:set[int]={1,2,3}
my_dict:dict[str,int] = {"itheima": 666}

方式2:除了使用 变量:类型,这种语法做注解外,也可以在注释中进行类型注解语法:
# type: 类型
在注释中进行类型注解
Class student:
	pass
var_1 = random.randint(1,10) # type: int
var_2 = json.loads(data) # type: dict[str, int]
var_3 = func() # type: student

函数(方法)的类型注解-形参注解
函数(方法)的返回值也是可以添加类型注解的,
def 函数方法名(形参名:类型,形参名:类型...) -> 返回值类型:
	pass

使用Union[类型,……, 类型]
可以定义联合类型注解
Union联合类型注解,在变量注解、函数(方法)形参和返回值注解中,均可使用。
from typing import Union
my_list: 1ist[union[str, int]] = [1,2,"itheima", "itcast"]
my_dict: dict[str, union[str,int]]= {"name":"周杰轮”,"age": 31}

def func(data:union[int, str]) -> union[int, str]:
	pass

多态

多态,指的是:多种状态,同一个行为,使用不同的对象会得到不同的状态。

同样的行为(函数),传入不同的对象,得到不同的状态

多态常作用在继承关系上,比如 函数(方法)形参声明接收父类对象,实际传入父类的子类对象进行工作

以父类做定义声明.以子类做实际工作.用以获得同一行为,不同状态

父类Animal的speak方法,是空实现
这种写法,就叫做抽象类(也可以称之为接口)
抽象类:含有抽象方法的类称之为抽象类。抽象方法:方法体是空实现的(pass)称之为抽象方法
抽象类就好比定义一个标准包含了一些抽象的方法,要求子类必须实现。
配合多态,完成抽象的父类设计(设计标准),具体的子类实现(实现标准)
class Animal:
	def speak(self):
		pass
class Dog(Animal):
	def speak(self):
		print("汪汪汪")
class cat(Animal):
	def speak(self):
		print(""喵喵喵”)
def make_noise(animal: Animal):
	animal.speak()
dog = Dog()
cat = cat()
make_noise(dog) # 输出:汪汪汪
make_noise(cat) # 输出喵喵喵

文件

思考:计算机只能识别:0和1,那么我们丰富的文本文件是如何被计算机识别,并存储在硬盘中呢?
答案:使用编码技术(密码本)将内容翻译成0和1存入。

编码技术即:翻译的规则。记录了如何将内容翻译成二进制,以及如何将二进制翻译回可识别内容。

计算机中有许多可用编码: UTF-8、GBK、Big5等

编码有许多,所以要使用正确的编码,才能对文件进行正确的读写操作呢

读写编码要一致才正确

1.什么是编码? 编码就是一种规则集合记录了内容和二进制间进行相互转换的逻辑。编码有许多中,我们最常用的是UTF-8编码
2.为什么需要使用编码? 计算机只认识0和1,所以需要将内容翻译成0和1才能保存在计算机中,同时也需要编码,将计算机保存的0和1,反向翻译回可以识别的内容

文件的操作步骤

想想我们平常对文件的基本操作,大概可以分为三个步骤(简称文件操作三步走):
① 打开文件
② 读写文件
③ 关闭文件
注意事项
注意:可以只打开和关闭文件,不进行任何读写

open()打开函数

在Python,使用open函数,可以打开一个已经存在的文件,或者建一个新文件语法如下
open(name,mode,encoding)
name:是要打开的目标文件名的字符串(可以包含文件所在的具体路径)。

mode:设置打开文件的模式(访问模式):只读、写入、追加等。

​ r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。

​ w打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,原有内容会被删除,如果该文件不存在,创建新文件。
​ a打开一个文件用于追加。如果该文件已存在,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。

encoding:编码格式(推荐使用UTF-8)
示例代码:
f = open('python.txt','r'.encoding="UTF-8")

#encoding的顺序不是第三位,所以不能用位置参数,用关键字参数直接指定
注意事项
注意:此时的f是`open'函数的文件对象,对象是Python中一种特殊的数据类型,拥有属性和方法,可以使用对象.属性或对象,方法对其进行访问

读操作相关方法
read(方法:
文件对象.read(num)
num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num,那么就表示读取文件中所有的数据。
readlines()方法:可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素。

f= open('python.txt')
content=f.readlines()

with open 语法
with open("python.txt","r") as f:
f.readlines()
# 通过在with open的语句块中对文件进行操作#可以在操作完成后自动关闭close文件,避免遗忘掉close方法

# ['hello world'n', 'abcdefg\n', 'aaa\n', 'bbb\n','ccc']
print(content)

# 关闭文件
f.close()
# 最后通过close,关闭文件对象,也就是关闭对文件的占用
# 如果不调用close,同时程序没有停止运行,那么这个文件将一直被Python程序占用。

for循环读取文件行
for line in open("python.txt", "r"):
	print(line) # 每一个line临时变量,就记录了文件的一行数据

#1.打开文件
f= open('python.txt', 'w’)
#2.文件写入
f.write('hello world')
#3.内容刷新
f.flush()
直接调用write,内容并未真正写入文件,而是会积在程序的内存中,称之为缓冲区
当调用flush的时候,内容会真正写入文件
这样做是避免频繁的操作硬盘,导致效率下降(攒一堆,一次性写盘)
可以使用”\n”来写出换行符

操作功能
文件对象 = open(file, mode, encoding)打开文件获得文件对象
文件对象.read(num)读取指定长度字节
不指定num读取文件全部
文件对象.readline()读取一行
文件对象.readlines()读取全部行,得到列表
for line in 文件对象for循环文件行,一次循环得到一行数据
文件对象.close()关闭文件对象
with open() as f通过with open语法打开文件,可以自动关闭

异常

当检测到一个错误时,Python解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的“异常”,
也就是我们常说的BUG

例如:以r'方式打开一个不存在的文件,
f= open('linux.txt', 'r')

为什么要捕获异常?
在可能发生异常的地方,进行捕获。当异常出现的时候,提供解决方式,而不是任由其导致程序无法运行。

当我们的程序遇到了BUG,那么接下来有两种情况:
① 整个程序因为一个BUG停止运行 ②对BUG进行提醒,整个程序继续运行
显然在之前的学习中,我们所有的程序遇到BUG就会出现①的这种情况,也就是整个程序直接奔溃.

但是在真实工作中,我们肯定不能因为一个小的BUG就让整个程序全部奔溃,也就是我们希望的是达到② 的这种情况那这里我们就需要使用到捕获异常
捕获异常的作用在于:提前假设某处会出现异常,做好提前准备,当真的出现异常的时候,可以有后续手段。

异常的种类多种多样,如果想要不管什么类型的异常都能捕获到,那么使用:
except:
except Exception:

捕获常规异常基本语法:
try:
	可能发生错误的代码
except:
	如果出现异常执行的代码
尝试以`r`模式打开文件,如果文件不存在,则以w方式打开。
try:
	f= open('linux.txt', 'r')
except:
	f= open('linux.txt', 'w')
捕获指定异常
try:
	print(name)
except NameError as e:
	print('name变量名称未定义错误")
else:
	print("我是else,是没有异常的时候执行的代码")// 可选
finally:
	f.close() //表示的是无论是否异常都要执行的代码,例如关闭文件 可选
注意事项
① 如果尝试执行的代码的异常类型和要捕获的异常类型不一致,则无法捕获异常
②一般try下方只放一行尝试执行的代码。

捕获多个异常时,可以把要捕获的异常类型的名字,放到except后,并使用元组的方式进行书写。
try:
	int(1/0)
except (NameError,ZeroDivisionError):
	print('ZeroDivision错误..)

异常的传递

异常是具有传递性的
当main函数调用func2,func2调用func1,当函数func01中发生异常,并且没有捕获处理这个异常的时候,异常会传递到函数func02,当func02也没有捕获处理这个异常的时候main函数会捕获这个异常,这就是异常的传递性
提示:当所有函数都没有捕获异常的时候,程序就会报错

模块

什么是模块?Python 模块 (Module是一个 Python 文件,以·py 结尾.模块能定义函数,类和变量,模块里也能包含可执行的代码.
模块的作用:python中有很多各种不同的模块,每一个模块都可以帮助我们快速的实现一些功能,比如实现和时间相关的功能就可以使用time模块我们可以认为一个模块就是一个工具包,每一个工具包中都有各种不同的工具供我们使用进而实现各种不同的功能
大白话:模块就是一个Python文件,里面有类、函数、变量等,我们可以拿过来用(导入模块去使用)

模块导入方式

模块使用前需要先导入 导入的语法如下

[from 模块名] import[模块 | 类 | 变量 | 函数 |*][as 别名]
常用的组合形式如:
import 模块名
from 模块名 import 类、变量、方法等
from 模块名 import *
import 模块名 as 别名
from模块名 import功能名 as 别名

import模块名
基本语法:
import 模块名
import 模块名1,模块名2
模块名.功能名()

案例:导入time模块
# 导入时间模块
import time
print("开始")# 让程序睡眠1秒(阻塞)
time.sleep(1)
print("结束")

from 模块名 import 功能名
功能名()
案例:导入time模块中的sleep方法
#导入时间模块中的sleep方法
from time import sleep
print("开始")#让程序睡眠1秒(阻塞)
sleep(1)
print("结束”)

# 模块定义别名
import 模块名 as 别名
# 功能定义别名
from 模块名 import 功能 as 别名
案例:
# 模块别名
import time as tt
tt.sleep(2)
print('hello')
#功能别名
from time import sleep as sl
sl(2)
print('hello')

自定义模块
my_module1.py文件中
def test(a, b):
	print(a + b)

text_my_module.py文件中
import my_module1
my_module1.test(10,20)

# 模块1代码def my_test(a, b):print(a + b)
# 模块2代码def my_test(a, b):print(a - b)
# 导入模块和调用功能代码
from my_modulel import mytest
from my_module2 import my_test
my_test(1,1) # my_test函数是模块2中的函数
当导入多个模块的时候,且模块内有同名功能
当调有这个同名功能的时候,调用到的是后面导入的模块的功能

测试模块
在实际开发中,当一个开发人员编写完一个模块后,为了让模块能够在项目中达到想要的效果,
这个开发人员会自行在py文件中添加一些测试信息,例如,在my_module1.py文件中添加测试代码test(1,1)
def test(a. b):
	print(a + b)
	test(1.1)
问题:
此时,无论是当前文件,还是其他已经导入了该模块的文件,在运行的时候都会自动执行`test 函数的调用
解决方案:
def test(a, b):
	print(a+ b)
#只在当前文件中调用该函数,如果我们在模块中调动了自己的方法
#表示,只有当程序是直接执行的才会进入if内部,如果是被导入的,则if无法进入
if __name__ = 'main':
	test (1,1)

如果一个模块文件中有`__all__`变量,当使用`from xxx import *`导入时,只能导入这个列表中的元素
my_module1.py
__all__=['test A']
def test_A():
	print('testA')
def test_B():
	print('testB')


test_my_module.py
from my module1 import *
test_A() # 这里只能使用test_A函数

从物理上看,包就是一个文件夹,在该文件夹下包含了一个 init.py 文件,该文件夹可用于包含多个模块文件从逻辑上看,包的本质依然是模块

包的作用:当我们的模块文件越来越多时,它可以帮助我们管理这些模头,包的作用就是包含多个模块,但包的本质依然是模块。通过包,在逻辑上将一批模块归为一类,方便使用,

init.py文件的作用?

创建包会默认自动创建的文件,通过这个文件来表示一个文件夹是Python的包,而非普通的文件夹,
all 变量的作用?
同模块中学习到的是一个作用,控制import*能够导入的内容

新建包`my_package
新建包内模块:my_module1my module2模块,代码如下
# my_module1模块中
print(1)
def info_print1():
	print('my_module1')
	
# my_module2模块中
print(2)
def info_print2():
	print('my_module2")

Pycharm中的基本步骤:
[New]→[Python package]→输入包名->[ok]->新建功能模块(有联系的模块)
注意:新建包后,包内部会自动创建__init__.py`文件,这个文件控制着包的导入行为

导入包
方式一:
import 包名.模块名
包名.模块名.目标
import my_package.my_module1
import my_package.my_module2
#包中的my_module1模块的info_print1()方法
my_package.my_module1.info_print1()
#包中的my_module1模块的info_print2()方法
my_package .my_module2.info_print2()

方式二:
注意:必须在`__init__.py 文件中添加__all__=[],控制允许导入的模块列表
from 包名import *
模块名.目标

#包中的 all 和模块中的__all__一样有着控制的功能
__all__=["my_module2"]

from my_package import *
#包中的my_module1模块的info_print1()
my_medule1.info_print1() //不能调用
#包中的my_module1模块的info_print2()
my_module2.info_print2() //正常调用

什么是第三方包
包可以包含一堆的Python模块,而每个模块又内含许多的功能。

第三方包就是非Python官方内置的包,可以安装它们扩展功能,提高开发效率。

包就是一堆同类型功能的集合体。
在Python程序的生态中,有许多非常多的第三方包(非Python官方),可以极大的帮助我们提高开发效率,如
科学计算中常用的:numpy包
数据分析中常用的:pandas包
大数据计算中常用的:pyspark、apache-flink包
图形可视化常用的:matplotlib、pyecharts
人工智能常用的:tensorflow

安装第三方包-pip
第三方包的安装非常简单,我们只需要使用Python内置的pip程序即可。
打开命令提示符程序,在里面输入:
pip install 包名称
即可通过网络快速安装第三方包

修改源

连接国内的网站进行包的安装:pip install -i https://pypi.tuna.tsinghua.edu.cn/simpleopen in new window 包名称

https://pypi.tuna.tsinghua.edu.cn/simpleopen in new window 是清华大学提供的一个网站,可供pip程序下载第三方包

pip install numpy

Echarts

是个由百度开源的数据可视化,凭借着良好的交互性,精巧的图表设计,得到了众多开发者的认可,而 Python 是门富有表达力的语言,很适合用于数据处理,当数据分析遇上数据可视化时pyecharts诞生了.

pyecharts模块安装 pip install pyecharts

官方示例:https://gallery.pyecharts.0rg/#/READMEopen in new window

json

JSON是一种轻量级的数据交互格式。采用完全独立于编程语言的文本格式来存储和表示数据。可以按照JSON指定的格式去组织和封装数据

JSON本质上是一个带有特定格式的字符串

主要功能:json就是一种在各个编程语言中流通的数据格式,负责不同编程语言中的数据传递和交互.

# json数据的格式可以是
{"name":"admin","age":18}
# 也可以是:
[{"name":"admin","age":18},{"name":"root","age":16},{"name":"张k三","age":20}]

Python数据和Json数据的相互转化
# 导入json模块
import json
# 准备符合格式json格式要求的python数据
data = [{"name":"老王","age": 16}, {"name":"张三","age": 20}]
#通过json.dumps(data)方法把python数据转化为了json数据 如果有中文可以带上:ensure_ascii=False参数来确保中文正常转换
data=json.dumps(data) 
# 通过 json.loads(data)方法把json数据转化为了python数据
data=json.loads(data)


pymysql

除了使用图形化工具以外,我们也可以使用编程语言来执行SQL从而操作数据库。
在Python中,使用第三方库:pymysql来完成对MySQL数据库的操作。
安装: pip install pymysql

代码如下:
from pymysql import connection
# 获取到MySQL数据库的链接对象
conn = connection(
    host='localhost', #主机名(或IP地址)
    port=3306#端口,默认3306
    user='root', # 账户名
    password='123456,# 密码
    # autocommit = True # 设置自动提交
)

#打印MySQL数据库软件信息
print(conn.get_server_info())

#获取游标对象
cursor= conn.cursor()
conn.select_db("test")
#先选择数据库
#使用游标对象,执行sq1语句
cursor.execute("CREATE TABLE test_pymysql(id INT, info VARCHAR(255))")

# 使用游标对象,出行sq1语句
cursor.execute(SELECT * FROM student")#获取查询结果
results:tuple=cursor.fetchall() # 嵌套元组结果
for r in results:
   print(r)

#执行sq1
#无法将数据插入到数据表student中的。这是为什么呢?pymysql在执行数据插入或其它产生数据更改的SOL语句时默认 是需要提交更改的,即,需要通过代码“确认”这种更改行为。通过链接对象.commit()即可确认此行为。
# pymysql库在执行对数据库有修改操作的行为时,是需要通过链接对象的commit成员方法来进行确认的。只有确认的修改,才能生效,
cursor.execute("insert into student values(10001,'周杰轮'31,'男'))

# 通过commit碗认
# 如果不想手动commit确认,可以在构建链接对象的时候,设置自动commit的属性
conn.commit()
               
#组织SOL语句
for record in all_data :
	sql = f"insert into orders(order_date,order_id,money,province)" \
	f"valves('{record.date}','{record.order_id}',{record.money},'{record.province}')"
#执行SQL语句
cursor.execute(sql)
               
#关闭到数据库的链接
conn.close()

Spark

定义:Apache Spark是用于大规模数据(large-scala data)处理的统一(unified)分析引擎

一款分布式的计算框架,用于调度成百上千的服务器集群,计算TB、PB乃至EB级别的海量数据

PySpark
Spark对Python语言的支持,重点体现在,Python第三方库:PySpark之上。
PySpark是由Spark官方开发的Python语言第三方库。Python开发者可以使用pip程序快速的安装PySpark并像其它三方库那样直接使用

PySpark是Spark的Python实现,是Spark为Python开发者提供的编程入口,用于以Python代码完成Spark任务的开发

PySpark不仅可以作为Python第三方库使用,也可以将程序提交的Spark集群环境中,调度大规模集群进行执行。

黑马有Hadoop课程

PySpark库的安装
同其它的Python第三方库一样,PySpark同样可以使用pip程序进行安装
在”CMD”命令提示符程序内,输入

pip install pyspark
或使用国内代理镜像网站(清华大学源
pip install -ihttps://pypi.tuna.tsinghua.edu.cn/simple pyspark

想要使用PySpark库完成数据处理,首先需要构建一个执行环境入口对象。

PySpark的执行环境入口对象是:类SparkContext的类对象

# 导包
from pyspark import SparkConf, sparkcontext
# 创建Sparkconf类对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")# 
基于Sparkconf类对象创建Sparkcontext类对象
sc=SparkContext(conf=conf)
# 打印PySpark的运行版本
print(sc.version)

#停止SparkContext对象的运行(停止Spark的运行)
sc.stop()

SparkContext类对象,是PySpark编程中一切功能的入口。

PySpark的编程,主要分为如下三大步骤:

  1. 数据输入:通过sparkcontext类对象的成员方法完成数据的读取操作读取后得到RDD类对象sc.parallelize\sc.textFile

  2. 数据处理计算:通过RDD类对象的成员方法完成各种数据计算的需求

    rdd.map\rdd.flatMap\rdd.reduceByKey

  3. 数据输出:将处理完成后的RDD对象调用各种成员方法完成写出文件、转换为list等操作

1.Spark的编程流程就是:
数据输入)将数据加载为RDD
对RDD进行计算(数据计算)
将RDD转换为Python对象(数据输出)
2.数据输出的方法
collect:将RDD内容转换为list
reduce:对RDD内容进行自定义聚合
take:取出RDD的前N个元素组成list
count:统计RDD元素个数

PySpark支持多种数据的输入,在输入完成后,都会得到一个: RDD类的对象

RDD全称为:弹性分布式数据集(ResilientDistributed Datasets)PySpark针对数据的处理,都是以RDD对象作为载体,即:
数据存储在RDD内
各类数据的计算方法,也都是RDD的成员方法
RDD的数据计算方法,返回值依旧是RDD对象

Python数据容器转RDD对象
PySpark支持通过SparkContext对象的parallelize成员方法,将list,tuple,set,dict,str转换为PySpark的RDD对象

注意: 字符串会被拆分出1个个的字符,存入RDD对象字典仅有key会被存入RDD对象

from pyspark import sparkConf, sparkContext
conf = sparkconf().setMaster("1ocal[*]").setAppName("test_spark_app")
sc=Sparkcontext(conf=conf)
rdd = sc.parallelize(数据容器对象)

#通过parallelize万法特Python对象加载到Spark内,成为RDD对象
rdd1 = sc.parallelize([12345])
rdd2 = sc.parallelize((12345))
rdd3 = sc.parallelize("abcdefg")
rdd4 = sc.parallelize({12345})
rdd5 = sc.parallelize({"key1":"value1","key2": "value2"})

# PySpark也支持通过SparkContext入口对象,来读取文件,来构建出RDD对象。
# 用过textFile方法,读取文件数据加裁到Spark内,成为RRDD对象
rdd =sc.textFi1e(文件路径)

#输出RDD的内容
print(rdd.collect())

# PySpark的数据计算,都是基于RDD对象来进行的,那么如何进行呢?依赖RDD对象内置丰富的:成员方法(算子)
rdd.map(func)
# func :f(T) -> U
# f: 表示这是一个函数(方法)
#〔T)→U 表示的是方法的定义:
#() 表示传入参数,(T)表示 传入1个参数 ,()表示没有传入孝数
#T 是泛型的代称,在这里表示 任意类型
#U 也是泛型代称,在这里表示 任意类型
#->U 表示返回值
#(T)-> U 总结起来的意思是:这是一个方法,这个方法接受一个發数传入,传入卷数类型不限。 返回一个返回值,返回值类那不限。
#(A)-> A 总结起来的意思是:这是一个方法。这个方法接受一个参数传入,传入参微类里不限。返回一个返回值,返回值和传入参数类型一数
from pyspark import SparkConf,SparkContext
import os 
os.environ['PYSPARK_PYTHON']= "D:/dev/python/python310/python.exe"

#进备一个RDD
rdd=sc.parallelize([1, 2, 3, 4, 5])
# 通过map方法特金部数都以10
def func(data):
	return data *10
# map算子(成员方法):接受一个处理函数,可用lambda表达式快速编写对RDD内的元素逐个处理,并返回一个新的RDD
# 链式调用 : 对于返回值是新RDD的算子,可以通过链式调用的方式多次调用算子。
rdd2 = rdd.map(func)

# flatMap算子 功能:对rdd执行map操作,然后进行解除嵌套 操作.
# 嵌套的list lst =[[1,2,3],[4,5,6],[7,8,9]]
# 如果解除了嵌套lst=[1,2,3,4,5,6,7,8,9]
# 按照空格切分数据后,解除嵌套 
rdd = sc.parallelize(["a b c","e c a","a c e"])
print(rdd.flatMap(lambda x:x.split("")).collect())

#reduceByKey算子 功能: 针对KV型 RDD,自动按照key分组,然后根据你提供的聚合逻辑,完成 组内数据(valve)的聚合操作.
rdd .reduceBykey(func)
# func:(V,W)-> V 接受2个传入参数(类型要一致),返回一个返回值,类型和传入要求一致
rdd= sc.parallelize([('a',1),('a',1),('b',1),('b',1),('b',1)])
result=rdd.reduceByKey(lambda a,b:a+b)
print(result.collect())#结果:[('b',3),('a',2)]
注意: 
reduceByKey中接收的函数,只负责聚合,不理会分组
分组是自动 by key 来分组的,二元元组。Java Stream:groupingBy

#Filter
#功能: 过滤想要的数据进行保留!
# 接受一个处理函数,可用lambda快速编写函数对RDD数据逐个处理,得到Tre的保留:至返回值的RDD中
#语法:
rdd.filter(func)
# func:(T) bool 传入1个参数进来随意类型,返回值必须是True or False

rdd = sc.parallelize([12345])
# 保留奇数
#rdd.filter(lambda x:True if(x%2=1) else False)
print(rdd.filter(lambda x: x % 2 == 1).colect())

# distinct算子
# 功能:对RDD数据进行去重,返回新RDD
# 语法:rdd.distinct()无需传参
rdd=sc.parallelize([1134556699])
#按照空格切分数据后,解除嵌意
print(rdd.distinct().collect())

# sortBy算子
# 功能: 对RDD数据进行排序,基于你指定的排序依据,语法:
rdd.sortBy(func,ascending=False,numPartitions=1)
# func:(T)→U:告知按照rdd中的哪个数据进行排序,比如 lambda x:x[1]表示按照rdd中的第二列元素进行排序
# aseending True升序 False 降
# numPartitions:用多少分区排序
# 接收一个处理函数,可用lambda快速编写 .函数表示用来决定排序的依据 .可以控制升序或降序 .全局排序需要设置分区数为1

collect算子
功能: 将RDD各个分区内的数据,统一收集到Driver中,形成一个List对象
用法:
rdd.collect()
返回值是一个list

reduce算子
功能: 对RDD数据集按照你传入的逻辑进行聚合
语法:
rdd.reduce(func)
#func:(T,T)→T
# 2参数传入1个返回值,返回值和参数要求类型一致
rdd = sc.parallelize(range(110))
#将rdd的数据进行累加求和
print(rdd.reduce(lambda a,b;a+b))
返回值等同于计算函数的返回值

take算子
功能: 取RDD的前N个元素,组合成list返回
用法:
sc.parallelize([3,2,1,4,5,6]).take(5)# [3,2,1,4,5]

count算子
功能: 计算RDD有多少条数据,返回值是一个数字
用法:
sc.parallelize([3,2,1,4,5,6]).count()

saveAsTextFile算子
功能: 将RDD的数据写入文本文件中
支持 本地写出, hdfs等文件系统
输出的结果是一个文件夹
有几个分区就输出多少个结果文件

代码:
rdd =sc.parallelize([12345])
rdd.saveAsTextFi1e("../data/output/test.txt")

Caused by: java.io.FileNotFoundException: HAD00P_HOME and hadoop.home.dir are unset.
调用保存文件的算子,需要配置Hadoop依赖
下载Hadoop安装包
http://archive.apache.org/dist/hadoop/common/hadoop-3.0.0/hadoop-3.0.0.tar.gz
解压到电脑任意位置
在Python代码中使用os模块配置:os.environ['HADOOP HOME”='HADOOP解压文件夹路径'
下载winutils.exe,并放入Hadoop解压文件夹的bin目录内https://raw.githubusercontent.com/steveloughran/winutils/master/hadoop-3.0.0/bin/winutils.exe
下载hadoop.dll,并放入:C:/Windows/System32 文件来内
https: //raw.githubusercontent.com/steveloughran/winutils/master/hadoop-3.0.0/bin/hadoop.dll
                              
修改rdd分区为1个
方式1,Sparkconf对象设置属性全局并行度为1:
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
conf.set("spark.default.parallelism","1")
sc =SparkContext(conf=conf)
方式2,创建RDD的时候设置(parallelize方法传入numSlices参数为1)
rdd1 =sc.parallelize([1,2,3,45],numSlices=1)
rdd1=sc.parallelize([12345]1)
                              
执行命令
bin/spark-submit --master yarn --num-executors 3 --queue root .teach --executor-cores
 --executor-memory 4g /home/hadoop/demo.py

闭包

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量
的内部函数称为闭包。

定义双层嵌套函数内层函数可以访问外层函数的变量
将内存函数作为外层函数的返回,此内层函数就是闭包函数

nonlocal关键字的作用:在闭包函数(内部函数中)想要修改外部函数的变量值.需要用nonlocal声明这个外部变量

优点,

无需定义全局变量即可实现通过函数,持续的访问、修改某个值

闭包使用的变量的所用于在函数内,难以被错误的调用修改
缺点:
由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存

通过全局变量account amount来记录余额
尽管功能实现是ok的,但是仍有问题代码在命名空间上(变量定义)不够干净、整洁
全局变量有被修改的风险

account amount= 0 #账户余额
def atm(num, deposit=True):
	gLobal account_amount
	if deposit:
		account_amount += num
		print(f"存款:+{num},账户余额:{account_amount}")
    else:
		account_amount -= num
		print(f"取款:-{num},账户余额:{account_amount}")
atm(300)
atm(300)
atm(100, False)

def account_create(initial_amount=0):
	def atm(num, deposit=True):
		nonlocal initial_amount
		if deposit:
			initial_amount += num
			print(f"存款:+{num},账户余额:{initial_amount}")
        else:
			initial_amount -= num
			print(f"取款:{num},账户余额:{initial_amount}")
	return atm
fn = account_create()
fn(300)
fn(200)
fn(300,False)

简单闭包
def outer(lago):
	def inner(msg):
		print(f"<{logo}>{msg}<{logo}>")
	return inner
fn1 = outer("黑马程序员”)
fn1(“大家好呀”)
fn1("学Python就来")
fm2 = outer("传智教育”)
fm2("IT职业教育培训")
fn2("学Python就来")
<黑马程序员>大家好呀<黑马程序员>
<黑马程序员>学Python就来<黑马程序员>
<传智教育>IT职业教育培训<传智教育>
<传智教育>学Python就来<传智教育>

修改外部函数变量的
def outer(num1):
	def inner(num2):
        nonLocal num1 #需要使用nonlocal关键字修饰外部函数的变量才可在内部函数中修改它
        num1 += num2
    	print(num1)
	return inner

fn = outer(10)
fn(10)
fn(10)

装饰器
也是一种闭包,其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能。
装饰器就是使用创建一个闭包函数,在闭包函数内调用目标函数。
可以达到不改动目标函数的同时,增加额外的功能
def sleep():
	import random
	import time
	print(“睡眠中......")
	time.sleep(random.randint(15))
希望给sleep函数,增加一个功能:
在调用sleep前输出:我要睡觉了
在调用sleep后输出:我起床了

定义一个闭包函数,在闭包函数内部:执行目标函数
并完成功能的添加
def outer(func):
    def inner():
    	print("我要睡觉了")
    	func()
    	print("我起床了")
    return inner
def sleep():
    import random
    import time
    print("睡眠中......)
    time.sleep(random.randint(1,5))
fn = outer(sLeep)
fn()
执行结果:
我要睡觉了
睡眠中。..·.
我起床了

使用@outer
定义在目标函数sleep之上
装饰器的语法糖写法
def outer(func):
	def inner():
		print(“我要睡觉了")
		func()
		print("我起床了")
    return inner
@outer
def sleep():
	import randomimport time
	print("睡眠中...)
	time.sleep(random.randint(1,5))
sleep()

设计模式

单例模式(Singleton Pattern)

是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。

在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

定义:保证一个类只有一个实例,并提供一个访问它的全局访问点

适用场景:当一个类只能有一个实例,而客户可以从一个众所周知的访问点访问它时。

单例模式就是对一个类,只获取其唯一的类实例对象,持续复用它
节省内存
节省创建对象的开销

单个代码文件str_tool
class strTools:
	pass
str_tool= strTools()

在另一个文件中导入对象
from test import str_tool
s1 = str_tool
s2 = str_tool
print(s1)
print(s2)
<test,StrTools object at 0x000001300010B910>
<test.StrTools object at 0x00000130001DB910>
s1和s2是同一个对象

工厂模式

当需要大量创建一个类的实例的时候,可以使用

即,从原生的使用类的构造去创建对象的形式

迁移到,基于工厂提供的方法去创建对象的形式。

将对象的创建由使用原生类本身创建
转换到由特定的工厂方法来创建

class Person:
	pass
class Worker(Person):
	pass
class student(Person):
	pass
class Teacher(Person):
	pass
class Factory:
	def get_person(self, p_type):
        if p_type ='w':
            return Worker()
        elif p_type='s':
			return student()
        else:
			return Teacher()
factory = Factory()
worker = factory.get_person('w')
stu = factory.get_person('s')
teacher =factory.get_person('t')

使用工厂类的get person()方法去创建具体的类对象
优点:大批量创建对象的时候有统一的入口,易于代码维护
当发生修改,仅修改工厂类的创建方法即可
符合现实世界的模式,即由工厂来制作产品(对象)

并行执行

指的是同一时间做不同的工作。
进程之间就是并行执行的,操作系统可以同时运行好多程序,这些程序都是在并行执行。
除了进程外,线程其实也是可以并行执行的,

也就是比如一个Python程房,其实是完全可以做到:
一个线程在输出: 你好
一个线程在输出:Hello
像这样一个程序在同一时间做两件乃至多件不同的事情,我们就称之为:多线程并行执行

1.什么是进程
程序在操作系统内运行,即成为一个运行进程
2.什么是线程
进程内部可以有多个线程,程序的运行本质上就是由进程内部的线程在实际工作的
3.什么是并行执行
多个进程同时在运行,即不同的程序同时运行,称之为:多任务并行执行

一个进程内的多个线程同时在运行,称之为:多线程并行执行

threading模块
Python的多线程可以通过threading模块来实现。

import threading
thread_obj = threading.Thread([group [, target [, name [, args [, kwargs]]]]])
group:暂时无用,未来功能的预留参数
target:执行的目标任务名
args:以元组的方式给执行任务传参
kwargs:以字典方式给执行任务传参
name:线程名,一般不用设置
#启动线程,让线程开始工作
thread_obj.start()

def sing():
    while True:
        print("我在唱歌,啦啦啦...")
        time.sleep(1)
def dance(msg):
	while True:
		print(msg)
		time.sleep(1)
sing_thread =threading.Thread(target=sing)
dance_thread = threading.Thread(target=dance,args=("我在跳舞,哈哈哈”,))
dance_thread = thread1nq.ihread(target=dance,Kwargs={"msg",我在跳舞哦 啦啦啦”})
sing_thread.start()
dance_thread.start()
# args参数通过元组(按参数顺序)的方式传参 或使用kwargs参数用字典的形式传参

Socket

Socket服务端编程

主要分为如下几个步骤:
1.创建socket对象
import socket
socket_server =socket.socket
2.绑定socket_server到指定IP和地址
socket_server.bind(host, port)# socket_server,bind(("localhost",8888))
3.服务端开始监听端口
socker_server.listen(backlog) # socket_server.listen(1)
# back1og为int整数,表示允许的连接数量,超出的会等待,可以不填,不填会自动设盟一个合理值
4.接收客户端连接,获得连接对象
conn,address=socket server.accept()
print(f"接收到客户端连接,连接来自:{address}")
# accept方法是阻塞方法,如果没有连接,会卡再当前这一行不向下执行代码
#accept返回的是一个二元元组(链接对象,客户端地址信息),可以使用上述形式,用两个变量接收二元元组的2个元素

5.客户端连接后,通过recv方法,接收客户端发送的消息
while True:
	data = conn.recv(1024).decode("UTF-8")
	# recv接受的参数是缓冲区大小,一般给1024即可
	#recv方法的返回值是字节数组(Bytes),可以通过decode使用UTF-8解码为字符串
	#recv方法的传参是buffsize,缓冲区大小,一般设需为1024即可
	if data == 'exit':
		break
    print(“接收到发送来的数据:",data)
#可以通过while True无限循环来持续和客户端进行数据交互
#可以通过判定客户端发来的特殊标记,如exit,来退出无限循环
6.通过conn(客户端当次连接对象).调用send方法可以回复消息
while True:
	data = conn.recv(1024).decode("UTF-8")
	if data =='exit':
		break
	print("接收到发送来的数据:",data)
	#发送回复消息
    msg = input("请输入你要和客户端回复的消息:").encode("UTF-8")
    #encode可以将字符串编码为字节数组对象
    if msg =='exit':
		break
	conn.send("不好呀哈哈哈",'UTF-8')
7.conn(客户端当次连接对象)和socket server对象调用close方法,关闭连接
conn.close()
socket_server.close()

下载网络调试助手作为客户端https://github.com/nicedayzhu/netAssist/releases

Socket客户端编程

主要分为如下几个步骤:
1.创建socket对象
import socket
socket_client=socket.socket()
2.连接到服务端
socket_client.connect(("localhost",8888))
3.发送消息
while True: # 可以通过无限循环来确保持续的发送消息给服务端
	send_msq = input("请输入要发送的消息")
	if send _msg='exit':
		# 通过特殊标记来确保可以退出无限循环
		break
	socket_client.send(send_msg.encode("UTF-8")) #消息需要编码为字节数组(UTF-8编码)
4. 接收返回消息
while True:
	send_msg = input("请输入要发送的消息").encode("UTF-8")
	socket_client.send(send_msg)
	recv_data=socket_client.recv(1024)# 1024是缓冲区大小,一般1024即可
	# recv方法是阻寒式的,即不接收到返回,就卡在这里等待
	print("服务端回复消息为:",recv_data.detode("UTF-8")) # 接受的消息需要通过UTF-8解码为字符串
5.关闭链接
socket client.close() #最后通过close关闭链接

Python正则表达式

1.什么是正则表达式
是一种字符串验证的规则,通过特殊的字符串组合来确立规则
用规则去匹配字符串是否满足
如([1]+(.[\w-]+)*@[\w-]+(.[\w-]+)+$)可以表示为一个标准邮箱的格式
2.re模块的三个主要方法
re.match,从头开始匹配,匹配第一个命中项
re.search,全局匹配,匹配第一个命中项
re.findall,全局匹配,匹配全部命中项

  • 字符串的r标记表示,字符串内转移字符无效,作为普通字符使用=>re.findall(r'[a-zA-Z]',s)

使用re模块,并基于re模块中三个基础方法来做正则匹配,

分别是:match、search、findall 三个基础方法

re.match(匹配规则,被匹配字符串)
从被匹配字符串开头进行匹配,匹配成功返回匹配对象(包含匹配的信息),匹配不成功返回空

s = 'python itheima python itheima python itheima
result = re.match('python'.s)
print(result) #<re.Match object;span=(0,6), match='python'>
print(result.span())# (0, 6)
print(result.group())# python

s ='1python itheima python itheima python itheima'
result =re.match('python',s)
print(result) # None

re.search(匹配规则,被匹配字符串)
搜索整个字符串,找出匹配的。从前向后,找到第一个后,就停止,不会继续向后
s = '1python666itheima666python666'
result = re.search('python',s)
print(result) #<re.Match object;span=(1,7)match='python'>
print(result.span()) #(1,7)
print(result.group())# python
整个字符串都找不到,返回None
s = 'itheima666'
result =re.search('python',s)
print(result)# None

findall(匹配规则,被匹配字符串)
匹配整个字符串,找出全部匹配项
s='1pythpn666itheima666pythan666'
result =re.findall('python',s)
print(result) # ['python','python"]
找不到返回空list:[]
s ='1pythonó66itheima66ópython666'
result =re.findall('itcast',s)
print(result) #[]

递归

在满足条件的情况下,函数自己调用自己的一种特殊编程技巧

函数调用自己,即称之为递归调用

def func():
	if ...:
		func()
	return ..

最典型的递归场景为找出一个文件夹中全部的文件。

如图,在D:/test 文件夹内,有如下嵌套结构和所属的文件,可以通过递归编程的形式完成

递归需要注意什么?
注意退出的条件,否则容易变成无限递归
注意返回值的传递,确保从最内层,层层传递到最外层
os模块的3个方法
os.listdir,列出指定目录下的内容
os.path.isdir,判断给定路径是否是文件夹,是返回True,否返回False

os.path.exists,判断给定路径是否存在,存在返回True,否则返回False


  1. \w- ↩︎