Python基础

数学运算

得商除法:

>>> 3 // 2
1

次方:

>>> 10 ** 3
1000

Py解释器的内置函数

print()

type();//用于查看数据对象的类型

字符串str

字符串的定义

双引、单三引、双三引

区别:三引支持跨行;双引不支持,需要在字符串中使用 \n 转义符表示换行

字符串内容里本身就有引号

  • 字符串内容里有单引号,那定义字符串时就用双引号
  • 字符串内容里有双引号,那定义字符串时就用单引号
  • 字符串内容里既有单引又有双引,那定义字符串时就用三引号
字符串切片

68ac1936f5284d759222f1bd3f3ef31e.png

hello = '刘总你好啊'
print(hello[2:4])
# 或
print(hello[-3:-1])

特:开头切 / 切到结尾 可省或一个切片索引 eg.hello[ : 2] / hello[2 : ]

获取字符串长度

len()

函数

定义函数关键字:def

def interview():
    print("把求职者带到3号会议室")
    print("请求职者 完成答卷")
    print("让测试经理来面试 求职者")
    print("让技术总监面试 求职者")
interview()
函数返回值

当函数需要返回结果时,就需要使用return关键字 

def squarep(num1,num2):
    return num1**2 + num2**2

ret = squarep(1,2)
print(ret)

注意:解释器执行代码的时候,一旦执行了函数中的 return 语句,就会立即从函数中返回到调用的地方,该函数下面还有代码,也不会再执行了

函数缺省值参数 
def  overScoreStudents(studentScoreList, score=60):

调用时如果score传60,可写为overScoreStudents(studentScoreList)

如果调用时传入了参数,eg. overScoreStudents(ssList, 70) ;那么解释器就会将 70 传给参数score

注意:定义函数时,一旦有一个参数有缺省值,那它后面所有的参数必须都有缺省值

列表list

用方括号来表示一个列表;每个格可存储 任何类型 的数据对象 ;可以是整数、小数、字符串、函数、也可以存储另一个列表对象

可索引可切片

a = [1, 2, 3.14, 'hello', [7,8,9] ] 

a[-1][:2] 取出的是 [7,8]

改变列表内容

列表元素可以填写变量,也可以直接填写表达式,解释器会自动计算结果,放入列表中

切片赋值

list1 = [0, 1, 2, 3, 4, 5]
list1[1:4] = ['a','b','c','e','f','g','h']

> [0, 'a', 'b', 'c', 'e', 'f', 'g', 'h', 4, 5]

合并俩列表

+

判断元素是否在列表中

关键字in ;返回值为true/false

list = [1,2,3,4,'hello']
print('hello' in list)
支持多个变量同时赋值 
x,y=[1,2]
print(x,y)

元组tuple 

用圆括号来表示一个元组;同样每个格可存储 任何类型 的数据对象 ;可以是整数、小数、字符串、函数、另一个列表对象、另一个元组对象;不同的是 元组的内容是不可改变的

注意:若元组中只有一个元素,必须在它后面加逗号 eg. a= (1,) 否则写成 a= (1) ,a就是数字1了(会把()当成用于提高优先级的东西),而非包含数字1的元组

定义元组时也可去掉() eg. a = 1,

可索引可切片
判断元素是否在元组中

关键字in;返回值为true/false

tuple = (1,2,3,4,'hello')
print('hello' in tuple)
支持多个变量同时赋值
x,y = (1,2)
print(x,y)
判断语句 
布尔表达式

条件组合

且 :关键字and ;表达式1 and 表达式2

或 :关键字or ;表达式1 or 表达式2

注意:当 and 和 or 一起用时,会先计算and部分,得到的结果再和or进行计算

非:关键字not;not 表达式1

注意:not、 and 和 or 一起用时, 会先计算 not , 再计算 and 部分, 最后再计算 or 

判断语句 

▲ :else if 要简写为 elif

情景:当函数执行某个功能前,要做一些条件检查, 如果这些条件任何一个不满足, 后续的操作就不要做了—— 可以结合return语句

对象的方法

字符串对象的方法

count();//返回字符串对象中包含了多少个参数指定的字符串

>>>'我们今天不去上学,我们去踢足球'.count('我们')

2

find();//在字符串中查找参数子字符串,并返回该参数字符串在其中第一个出现的位置索引

>>>str1='我们今天不去上学,我们去踢足球'

>>>pos1=str1.find('我们')

0

第二个参数,可用来指定查找字符串范围(从索引为几的位置开始往后查找)

>>>str1='我们今天不去上学,我们去踢足球'

>>>pos1=str1.find('我们',5)

9

split();//用于字符串的截取

以参数字符串为分隔符,将字符串切割为多个字符串,作为元素存入一个列表,最后返回这个列表

>>> str1= '小张:79 | 小李:88 | 小赵:83'

>>>pos1=str1.split('|')

['小张:79 ',' 小李:88 ',' 小赵:83']

join(); //用于字符串的连接

将列表中的字符串元素 以某字符串为连接符,连接成一个字符串

>>> '|'.join(['小张:79 ',' 小李:88 ',' 小赵:83'])

小张:79 | 小李:88 | 小赵:83

strip() / lstrip() /rstrip() //删字符串空格

  • strip();//只删字符串前后空格,不删中间
  • lstrip();//只删字符串左侧的空格
  • rstrip();//只删字符串右侧空格

replace();//替换

用于替换字符串中所有指定的 子字符串 为另一个字符串

⭐删字符串所有空格该如何实现

>>>'    小  李:88      '.replace(' ','')  //将空格替换为空字符串

'小李:88'

startswith() / endswith();//检查字符串是否以参数指定的字符串开头或结尾

返回值:true / false

列表对象的方法

append();//在列表后面添加一个元素

insert();//在指定位置插入一个元素

pop();//取出并删除一个元素,返回值是取出的元素;参数是取出的元素的索引

remove();//删除一个元素(注意只能删一个),参数是要删的元素的值

注意:从第1个元素开始,寻找 和参数对象 相等的元素,如果找到了,就删除;找到后,不会继续往后寻找其它相等的元素。

reverse() ;//列表对象的一个方法,可将列表元素倒过来 

index();//返回参数对象在列表中的索引

sort();//对列表进行排序;但返回值是None

格式化字符串

先用占位符占位,将要填的变量传进去

print格式化

格式:在字符串模板中使用占位符先占位,后面再提供一个元组,里面依次存放需要填入到占位符位置的数据

print('税前薪资:%s,缴税:%s,税后薪资:%s'  %(salary,tax,aftertax))

%s 是一种格式化符号, Python 解释器 看到%s , 就会调用内置函数 str(),并将对应的格式化对象作为参数传入 , 返回的结果字符串填入对应占位符

注意:如果占位符是1个,后面如果用元组注意加,;当然因为只有一个格式化对象,也可以直接使用,不放入元组中

占位符

除了%s还有%d、%f ;格式化对象为整数或浮点数的情况

  • 指定宽度:

1.正常是不足用空格补齐

'税前薪资:%10d 元' % 100000

 =>税前薪资: 100000 元

2.(10)不足10个字符补0(0) => '税前薪资:%010d 元' % 100000

                                           =>  税前薪资:0000100000 元

3.小数补0补后面

'税前薪资:%010f 元' % 1000.4522

=>税前薪资:1000.452200 元

4.保留小数点后两位

'税前薪资:%010.2f 元' % 1000.4522

=>税前薪资:0001000.45 元

  • 对齐:

左对齐,加-

'税前薪资:%-10s 元' % 100000

=>税前薪资:100000       元

f-string格式化

前提:必须是Python3.6之后的版本

格式:在字符串模板前加f,然后占位符使用{},里面放的是对应的数据对象

print(f '税前薪资是:{salary}元,缴税:{tax}元,税后薪资:{aftertax}元')

  • 指定宽度:

在括号里的变量后面加:宽度值

print(f '{salary:10}');//指定宽度为10,不足补空格

指定小数点后保留几位

print(f '{salary:10.2f}');

不足补0

print(f '{salary:010.2f}');

这里采用的是右对齐,补0补到前面;如果是左对齐,补0就补到后面

  • 对齐:

左对齐,<

print(f '{salary:<10}');

循环

while循环
for循环

通常用于从一个sequence类型(字符串、列表、元组)中依次取出每一个元素进行操作

studentAges = ['小王:17', '小赵:16', '小李:17', '小孙:16', '小徐:18']

for student in studentAges:
    print(student)

循环指定次数:

使用range(次数)函数; 当参数为两个的时候就指明了起止范围,注意是左闭右开;当参数为三个的时候,最后一个指明的是步长

终止循环

break

和return的区别:

return只能用在函数里面;在函数里面return和break的区别:break终止的是循环,当循环后面有语句,它依然会执行;return是直接返回到调用这个函数的地方

只结束当前这轮循环

continue

列表推导式

场景:把一个列表里的每一个元素,经过相同的处理,生成另一个列表

正常情况下我们一般这样写

list1 = [1,2,3,4,5,6]
list2 = []
for num in list1:
    list2.append(num*num)

列表推导式简化

list1 = [1,2,3,4,5,6]
list2 = [num**2 for num in list1]

for num in list1:从list1中取出每一个元素

num**2(for前面的部分):对取出的元素进行的处理操作

循环的嵌套
字符编码
字符集

以数字表示字符的映射关系

ASCII

unicode字符集:包括了现今世界上的常用文字符号 和 其对应的数字表示

字符编码

用字节表示 字符对应的数字

unicode最常见的字符编码规范:UTF8、UTF16

UTF16:任何字符对应的数字都用两个字节保存

8位一字节(1111 1111 => \xEE );\x 说明用16进制表示一个字节(2转16,四个一组)

字符串编码

就是将字符串变为字节串

Py3里的字符串对象都是unicode字符串,在对它进行存储和传输时, 通常使用字符串的encode方法,参数指定编码方式

字节串解码

就是将字节串变为字符串

Py的解码都是解码成unicode字符串对象,要解码这个字节串,首先要知道这个字节串用什么字符编码方式编码的

通常使用字节串对象的decode方法进行解码,参数指定编码方式

文件读写

Py内置函数open

open(
    file, 
    mode='r', 
    buffering=-1, 
    encoding=None, 
    errors=None, 
    newline=None, 
    closefd=True, 
    opener=None
    )

用于打开文件

open参数

file:指定要打开的文件路径

mode:文件打开的模式

常见打开模式:

mode缺省值是r

r —— 只读

w —— 只写(注意是清空文件重头写;文件不存在的时候会新建文件)

a —— 追加

encoding:指定读写文本文件时,使用的字符编解码方式

后面调write写入(字符串写到文件),open指定编码(编码为字节串)

后面调read读取(从文件中读取内容),open指定解码(解码为字符串)

文件写入
f = open('tmp.txt','w',encoding='utf8')

# write方法会将字符串编码为utf8字节串写入文件
f.write('白月黑羽:祝大家好运气')

f.close()
文件读取

场景:从文本文件中,读取内容到字符串对象,并截取其中的名字部分

f = open('tmp.txt','r',encoding='gbk')

# read 方法会在读取文件中的原始字节串后, 根据上面指定的gbk解码为字符串对象返回
content = f.read()

f.close()

name = content.split(':')[0]
print(name)

注意:read()有参数size ;可以指定读取多少个字符,不传就读全部

逐行读取readlines()

返回一个列表,列表中的每个元素依次对应文本文件中每行内容

f = open('tmp.txt')
linelist = f.readlines() 
f.close()  
for line in linelist:
    print(line)

a0be04f5ace343c3880f7225b8ba02c6.png

这里 linelist = ['哈哈哈哈哈哈\n','邓超\n','陈赫\n','鹿晗\n'];而print函数会自动在结尾加\n,这就导致最后有空行

要想去掉空行,可改写为:

f = open('tmp.txt')
content = f.read()   # 读取全部文件内容
f.close()  

# 将文件内容字符串 按换行符 切割 到列表中,每个元素依次对应一行
linelist = content.splitlines()
for line in linelist:
    print(line)
二进制方式读写文件
二进制读方式打开文件

mode参数指定为rb

# mode参数指定为rb 就是用二进制读的方式打开文件
f = open('tmp.txt','rb')
# 这里f.read()底层就不需要解码了,content得到的是字节串对象
content = f.read()   
f.close()  

# 内容为 b'\xe7\x99\xbd\xe6\x9c\x88\xe9\xbb\x91\xe7\xbe\xbd'
print(content) 

要想解码自己调decode函数
print(content.decode('utf8'))

# 该对象的长度是字节串里面的字节个数,就是12,每3个字节对应一个汉字的utf8编码 白月黑羽
# 注意这个len参数是字节串返回的就是字节串的个数,参数是字符串返回的就是字符串个数
print(len(content))
二进制方式写数据到文件

注意:传给write的参数只能是字节串对象

f = open('tmp.txt','wb')

content = '白月黑羽祝大家好运连连'
f.write(content.encode('utf8'))

f.close()

 也可以直接字节串写入

content = b'\xe7\x99\xbd\xe6\x9c\x88\xe9\xbb\x91\xe7\xbe\xbd'
f.write(content)
应用: 文件拷贝

必须用二进制方式打开;如果用文本文件方式打开,read()底层就会自动给它解码,又没指定解码方式,就会默认gbk编码方式解码,这个文件又不一定全是汉字,就会报错。

def fileCopy(srcPath,destPath):
    srcF = open(srcPath,'rb')
    content = srcF.read()
    srcF.close()

    destF = open(destPath,'wb')
    destF.write(content)
    destF.close()

fileCopy('1.png','1copy.png')

模块和库

Py中,一个代码文件代码,也就是一个.py文件,我们称它为一个模块

模块间的调用

法一:关键字import

eg. import save

这样save就成了模块aa(aa.py)里的一个变量,这个变量指向的是一个模块(文件)对象,这样就可以通过save.savetofile访问到save模块(save.py)里的函数

法二:关键字from import

可导入其它模块里的标识符(包括变量名和函数名等)

eg. from save import savetofile

导入多个其它模块

可分开导入,也可一起导入(模块间用,隔开)

从一个模块里导入多个标识符(可以是变量名也可以是函数名)

eg. from aa import func1,var1,func2,var2

导入一个模块里所有标识符

eg. from aa import *

起别名 as

当我们从两个模块导入函数,恰好这两个函数同名,这时我们可以给其中一个起个别名,避免冲突

from save import savetofile
from save2 import savetofile as savetofile2
将模块放入包中 

Py把放模块文件的目录,称为包         

包目录里需要有一个名为__init__.py的初始化文件,有它,Py才认为这是一个Py包,通常这个初始化文件里不需要什么代码,一个空文件就可。  ce42b5860a344378acf43a72d595cf90.png注意:调用时要加上所有包的路径前缀

import stock.food.beef
stock.food.beef.stockleft()

也可以

from stock.food.beef import stockleft
stockleft()
库                                   

模块文件里的函数,实现了通用的功能,经常被其它模块所调用,我们把这些被调用的模块文件称为库

字典dict

存放的是键值对数据

字典的定义

字典对象定义用{},其中的每个元素间用,隔开;每个元素都是一个键值对,键和值间用:隔开

members = {
    'account1'  : 13 ,
    'account2'  : 12 
}

value可以是任何类型的 对象

字典对象的特点:根据键查找值 很方便

members = {
    'account1'  : 13 ,
    'account2'  : 12 
}
print(members['account1'])
字典的使用

字典对象中添加/修改元素

key存在就是修改;key不存在就是添加

members['account1'] = 13

                     key          value

删除元素
members = {
    'account1'  : 13 ,
    'account2'  : 12 
}

val = members.pop('account1')
print(val)

这里pop方法还会返回参数key对应的value对象

检查元素

通过in关键字,检查字典对象中是否有我们要找的元素

eg. 'account1'  in  members

遍历字典

通过使用字典对象的items方法,items方法返回的是一个类似列表一样的对象,其中每个元素是 键值组成的元组

members = {
    'account1'  : 13 ,
    'account2'  : 12 ,
    'account3'  : 15 ,
}

for account,level in members.items():
    # members.items() = [('account1', 13), ('account2', 12), ('account3', 15)]
    print (f'account:{account}, level:{level}')
keys() / values() 

字典对象的keys方法,返回的是 将字典所有的键存入的一个类似列表的对象

members = {
    'account1'  : 13 ,
    'account2'  : 12 ,
    'account3'  : 15 ,
}

members.keys()

> ['account1', 'account2', 'account3']

字典对象的values方法,返回的是 将字典所有的值存入的一个类似列表的对象

members = {
    'account1'  : 13 ,
    'account2'  : 12 ,
    'account3'  : 15 ,
}

members.values()

> [13, 12, 15]

清空字典

字典对象的clear方法

members.clear() 区别于 members = {}

members.clear()是把原来对象的内容清空了;members = {} 只是指向了新的对象,原来对象并没有改变

两字典合并 

调用字典对象的update方法

members = {
    'account1'  : 13 ,
    'account2'  : 12 ,
    'account3'  : 15 ,
}

another =  {
    'account4'  : 13 ,
    'account5'  : 12 ,
}

members.update(another)

print(members)
获取字典元素的个数 

使用Py内置函数len

members = {
    'account1'  : 13 ,
    'account2'  : 12 ,
    'account3'  : 15 ,
}


print(len(members)) # 结果为3,表示字典中有3个元素

自定义类 

定义类关键字class;后加类名

类属性(静态属性)

若想获得类属性值采用:类名.属性名

静态方法

前加@staticmethod装饰器,指明下面的函数是类里的静态方法

调用:类名.静态方法

类的实例

一个个具体的对象 称为该类型的实例

要想产生一个类的实例对象,只需要 类名后面加();像这样 car1 = BenzCar()

实例对象具有类的一切属性和方法

实例属性

通常在 类的初始化方法(类在实例化对象时,解释器会执行的方法;相当于构造方法) __init__里定义

car1 = BenzCar() ;

解释器在执行这段代码时,会先在内存中创建一个该类的实例对象,然后会查看这个类有没有__init__初始化方法,有的话就调用,调用时就将实例对象传给__init__的第一个参数self

class BenzCar:     
    brand   = '奔驰'  
    country = '德国'  

    @staticmethod
    def pressHorn(): 
        print('嘟嘟~~~~~~')

    def __init__(self,color,engineSN):
        self.color  =  color     # 颜色
        self.engineSN = engineSN # 发动机编号

car1 = BenzCar('白色','24503425527866')       

print (car1.color)

注意:

1.BenzCar.engineSN 不能通过类名访问实例属性(人类的身份证号码???)

2.

   def __init__(self,color,engineSN):
        self.color  =  color     # 颜色
        self.engineSN = engineSN # 发动机编号

属于实例方法

3.静态方法不能访问实例属性

4.实例属性名和静态属性名重复怎么区分

通过类实例访问的该属性,访问的是实例属性;通过类名访问的该属性,访问的是类属性

class Car:
    brand = '奔驰'
    name = 'Car'

    def __init__(self):
        # 可以通过实例访问到类属性
        print(self.brand)

        # 定义实例属性和类属性重名
        self.name = 'benz car'

c1 = Car()

print(f'通过实例名访问name:{c1.name}')
print(f'通过类名  访问name:{Car.name}')
实例方法
class BenzCar:     
    brand   = '奔驰'  
    country = '德国'  

    @staticmethod
    def pressHorn(): 
        print('嘟嘟~~~~~~')

    def __init__(self,color,engineSN):
        self.color  =  color     # 颜色
        self.engineSN = engineSN # 发动机编号

    def changeColor(self,newColor):
        self.color = newColor

car1 = BenzCar('白色','24503425527866')       
car1.changeColor('黑色')

print (car1.color)

调用 changeColor方法的时候,只需要传入参数 newColor 对应新的颜色即可;不需要我们传入self参数,self 参数是实例对象本身,解释器会自动帮我们传入

类间的关系
继承

我们将被继承类称为父类/基类;继承类称为子类/派生类

继承格式

class 子类(父类):

        ...

子类会自动拥有父类的一切属性和方法;

子类在继承父类的一切特性的基础上,还可以有自己的属性和方法;

子类可以重新定义父类的属性和方法 ;

调用父类的初始化方法

子类的初始化方法里一定要调用父类的初始化方法,否则解释器自己不会执行父类的初始化方法

BenzCar.__init__(self,color,engineSN)

class Benz2018(BenzCar):
    price   = 880000
    model   = 'Benz2018'     

    def __init__(self,color,engineSN,weight):
        # 先调用父类的初始化方法
        BenzCar.__init__(self,color,engineSN)
        self.weight = weight # 车的重量
        self.oilweight = 0  # 油的重量

    # 加油
    def fillOil(self, oilAdded):
        self.oilweight +=  oilAdded 
        self.weight    +=  oilAdded

还可使用super()

super().__init__(color, engineSN)

这个写法,方法参数不需要加self参数 

异常

异常对象

Py标准库中很多异常类都继承自标准库里面的 Exception 类,代表各种不同类型的错误。

捕获异常

在编码时,就预料到了某些代码运行时可能出现某些异常,就可以使用 try...except... 这样的方法来捕获和处理异常。

while True:
    try:
        miles = input('请输入英里数:')
        km = int(miles) * 1.609344
        print(f'等于{km}公里')
    except ValueError:
        print('你输入了非数字字符')

解释器在执行try下面的缩进代码时,如果出现异常,就会停止执行后续代码;检查这个错误类型和except后声明的类型匹不匹配的上,匹配的上,就说明早就预料到了,执行except下的代码,没匹配上就终止程序。

匹配所有异常 
try:
    100/0
except Exception as e:
    # 把抛出的异常对象 赋值 给变量e
    print('未知异常:', e)

因为所有异常都是Exception的子类,所以Exception能匹配所有类型的异常

也可以这样写

try:
    100/0
except:
    print('未知异常:')

如果我们想知道异常产生在第几行,以及异常信息,可以导入traceback模块

import traceback

try:
    100/0
except :
    print(traceback.format_exc())

 Traceback (most recent call last):
  File "xxxx/xxx.py", line 4, in <module>
    100/0
ZeroDivisionError: division by zero

自定义异常 

抛出自定义异常关键字:raise

# 异常对象,代表电话号码有非法字符
class InvalidCharError(Exception):
    pass

# 异常对象,代表电话号码非中国号码
class NotChinaTelError(Exception):
    pass

def  register():
    tel = input('请注册您的电话号码:')

    # 如果有非数字字符
    if not tel.isdigit(): 
        raise InvalidCharError()

    # 如果不是以86开头,则不是中国号码
    if not tel.startswith('86'): 
        raise NotChinaTelError()

    return tel

try:
    ret = register()
    # 还是需要有一个变量接收的,因为register()返回值tel
except InvalidCharError:
    print('电话号码中有错误的字符')
except NotChinaTelError:
    print('非中国手机号码')

多线程

进程和线程

正在运行着的程序就是一个进程;每个进程中至少包含一个线程

之前的Py程序虽然没有创建线程,但当Py解释器程序运行起来成为一个进程,OS就自动的创建一个线程,通常称为主线程,在这个主线程里面执行代码指令

线程是操作系统创建的,每个线程对应一个代码执行的数据结构,保存了代码执行过程中的重要的状态信息;没有线程,操作系统没法管理和维护 代码运行的状态信息;所以没有创建线程之前,操作系统是不会执行我们的代码的。

如何创建线程

Threading模块

创建并启动一个单线程
print('主线程执行代码') 

# 从 threading 库中导入Thread类
from threading import Thread
from time import sleep

# 定义一个函数,作为新线程执行的入口函数
def threadFunc(arg1,arg2):
    print('子线程 开始')
    print(f'线程函数参数是:{arg1}, {arg2}')
    sleep(5)
    print('子线程 结束')


# 创建 Thread 类的实例对象
thread = Thread(
    # target 参数 指定 新线程要执行的函数
    # 注意,这里指定的函数对象只能写一个名字,不能后面加括号,
    # 如果加括号就是直接在当前线程调用执行,而不是在新线程中执行了
    target=threadFunc, 

    # 如果 新线程函数需要参数,在 args里面填入参数
    # 注意参数是元组, 如果只有一个参数,后面要有逗号,像这样 args=('参数1',)
    args=('参数1', '参数2')
)

# 执行start 方法,就会创建新线程,
# 并且新线程会去执行入口函数里面的代码。
# 这时候 这个进程 有两个线程了。
thread.start()


print('主线程结束')

 d28d268dbcde4fcd99876ee02fec5d9d.png

执行结果c2894bc00ffc41a88ae473b46b303720.png

创建并启动一个多线程

循环创建多个线程,并循环启动线程执行

import threading
from datetime import datetime
 
 
def thread_func():  # 线程函数
    print('我是一个线程函数', datetime.now())
 
 
def many_thread():
    threads = []
    for _ in range(10):  # 循环创建10个线程
        t = threading.Thread(target=thread_func)
        threads.append(t)
    for t in threads:  # 循环启动10个线程
        t.start()
 
 
if __name__ == '__main__':
    many_thread()

_:在Py中称为丢弃变量;当你不用这个变量或者不想给这个变量起名字的时候,就可以定义这个变量的名字为_

等待线程结束

使用Thread对象的 join 方法

哪个线程对象调用的,就等待哪个线程结束

print('主线程执行代码') 

# 从 threading 库中导入Thread类
from threading import Thread
from time import sleep

# 定义一个函数,作为新线程执行的入口函数
def threadFunc(arg1,arg2):
    print('子线程 开始')
    print(f'线程函数参数是:{arg1}, {arg2}')
    sleep(5)
    print('子线程 结束')


# 创建 Thread 类的实例对象
thread = Thread(
    # target 参数 指定 新线程要执行的函数
    target=threadFunc, 

    # 如果 新线程函数需要参数,在 args里面填入参数
    args=('参数1', '参数2')
)

# 执行start 方法,就会创建新线程,
# 并且新线程会去执行入口函数里面的代码。
# 这时候 这个进程 有两个线程了。
thread.start()

# 主线程的代码执行 子线程对象的join方法,就会等待子线程结束,才继续执行下面的代码
thread.join()

print('主线程结束')
 多线程共享数据

注意一点:多线程的执行顺序 "线程间的执行是无序的" ;当前执行哪个线程是由CPU决定的(线程是CPU调度资源的基本单位),所以当前CPU调度哪个线程,哪个线程就执行,未被调度的线程是无法执行的

下面我们就 多线程同时访问 bank对象(共享资源)进行分析

首先先来看单线程(串行执行)

from time import sleep

bank = {
    'byhy' : 0
}

# 定义一个函数,作为新线程执行的入口函数
def deposit(theadidx,amount):
    balance =  bank['byhy']
    # 执行一些任务,耗费了0.1秒
    sleep(0.1)
    bank['byhy']  = balance + amount
    

for idx in range(10):
    deposit (idx,1)

print(f'最后我们的账号余额为 {bank["byhy"]}')

> 最后我们的账号余额为 10

这里注意bank['byhy']这个是全局变量嗷

再来看多线程(并发执行)

from threading import Thread
from time import sleep

bank = {
    'byhy' : 0
}

# 定义一个函数,作为新线程执行的入口函数
def deposit(theadidx,amount):
    balance =  bank['byhy']
    # 执行一些任务,耗费了0.1秒
    sleep(0.1)
    bank['byhy']  = balance + amount
    print(f'子线程 {theadidx} 结束')

theadlist = []
for idx in range(10):
    thread = Thread(target = deposit,
                    args = (idx,1)
                    )
    thread.start()
    # 把线程对象都存储到 threadlist中
    theadlist.append(thread)

for thread in theadlist:
    thread.join()

print('主线程结束')
print(f'最后我们的账号余额为 {bank["byhy"]}')

1db009e50d3944dcacdc12c312e9307b.png

1.为什么会出现这种状况

首先  sleep(0.1) 的存在使得所有子线程的balance都为0

做个假设:子9执行到sleep(0.1),子9进入阻塞状态;当等待事件发生完成,线程就会由阻塞状态转为就绪状态;当CPU空闲时,它就会从就绪队列中选一个线程运行,至于选哪个就看CPU调度算法选哪个了

2.线程间的执行是无序的

thread = Thread(...) 线程对象被创建,进入新建状态

thread.start()这里才启动线程,进入就绪状态,等着被CPU调度

注意一个问题:去掉sleep你看到子线程打印出来的是顺序执行的,但其实不是。谁先执行看的是谁先被CPU调度。

但这不是研究的重点,我们这里着重看的是共享资源bank['byhy']—并发执行最后值为1,加了锁之后值为10

3.“有可能同时操作bank对象,可能出现一个线程覆盖另一个线程的结果问题”这什么意思

(没有sleep情况)因为多线程并发执行,存在子2刚执行完balance赋值语句,立马切到子5执行,还没等到bank['byhy']在子2中改变就把它赋给了当前子5线程的balance,也就是这里说的存在的覆盖问题。

解决多线程同时访问共享数据 —— 给共享资源加个锁

from threading import Thread,Lock
from time import sleep

bank = {
    'byhy' : 0
}

bankLock = Lock()

# 定义一个函数,作为新线程执行的入口函数
def deposit(theadidx,amount):
    # 操作共享数据前,申请获取锁
    bankLock.acquire()

    balance =  bank['byhy']
    # 执行一些任务,耗费了0.1秒
    sleep(0.1)
    bank['byhy']  = balance + amount
    print(f'子线程 {theadidx} 结束')

    # 操作完共享数据后,申请释放锁
    bankLock.release()

theadlist = []
for idx in range(10):
    thread = Thread(target = deposit,
                    args = (idx,1)
                    )
    thread.start()
    # 把线程对象都存储到 threadlist中
    theadlist.append(thread)

for thread in theadlist:
    thread.join()

print('主线程结束')
print(f'最后我们的账号余额为 {bank["byhy"]}')

bae68d6494cb4f2386924030088059ef.png

做个假设:子1在访问共享数据时加锁了,那当它执行到sleep( 0.1)的时候正常应该进入阻塞态,放弃CPU使用权;当子2想访问共享数据时访问不了,要等待子1锁的释放,子1等待事件完成,阻塞态转成就绪态,上处理机运行继续执行下面代码,然后释放锁,子2 再去获取锁....

装饰器

就是给原来函数加些功能;参数为被装饰的函数

import time

# 定义一个装饰器函数
def sayLocal(func):
    def wrapper():
        curTime = func()
        return f'当地时间: {curTime}'
    return wrapper

def getXXXTime():
    return time.strftime('%Y_%m_%d %H:%M:%S',time.localtime())

# 装饰 getXXXTime
getXXXTime = sayLocal(getXXXTime)

print (getXXXTime())

可简写为

import time

# 定义一个装饰器函数
def sayLocal(func):
    def wrapper():
        curTime = func()
        return f'当地时间: {curTime}'
    return wrapper

@sayLocal
def getXXXTime():
    return time.strftime('%Y_%m_%d %H:%M:%S',time.localtime())


print (getXXXTime())

JSON

序列化和反序列化
为什么要序列化

我们通过任何协议传输信息时传的都是字节串,这时发送方需要把这个对象转换为字节序列,才能在网络上传送,接收方则需要把字节序列再恢复为对象。

对于JSON格式的字符串,不能直接使用;但对于Py数据对象,我们可以直接通过对象的key去访问它对应的value值,操作方便

定义

序列化:把 程序的各种类型数据对象 变成 表示该数据对象的字节串(可以存储或传输的格式) 的过程

反序列化:把 字节串转化为 程序中的数据对象 的过程

json 序列化/反序列化模块

主要应用于传输数据 , 序列化成字符串

区别于pickle序列化/反序列化模块 —— 主要应用于存储数据 , 序列化成二进制字节流

参考:python序列化与反序列化_python反序列化-CSDN博客

JSON库的内置函数

json.dumps()

作用:将Py中的数据对象序列化为json格式的字符串

一些主要参数:

obj:要转换成json的对象

ensure_ascii:输出是否为ASCII

import json
historyTransactions = [

    {
        'time'   : '20170101070311',  # 交易时间
        'amount' : '3088',            # 交易金额
        'productid' : '45454455555',  # 货号
        'productname' : 'iphone7'     # 货名
    },
    {
        'time'   : '20170101050311',  # 交易时间
        'amount' : '18',              # 交易金额
        'productid' : '453455772955', # 货号
        'productname' : '奥妙洗衣液'   # 货名
    }

]

jsonstr = json.dumps(historyTransactions)
print(jsonstr)

ensure_ascii默认值为true;这里为什么奥妙洗衣液 会被转换成\u5965\u5999\u6d17\u8863\u6db2

确保输出的是ASCII,但对于非ASCII字符则会以\uxxxx的形式显示;要想中文显示,将ensure_ascii值设为False即可,表示禁用ASCII编码,使用unicode编码

json.loads()

作用:将json格式的字符串反序列化为Py中的数据对象

import json
jsonstr = '[{"time": "20170101070311", "amount": "3088", "productid": "45454455555", "productname": "iphone7"}, {"time": "20170101050311", "amount": "18", "productid": "453455772955", "productname": "\u5965\u5999\u6d17\u8863\u6db2"}]'

translist = json.loads(jsonstr)
print(translist)

socket编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值