一、Python基础语法
1.1 基本运算
1.1.1 取幂运算
a=100
a**=10  			#100001.1.2 除以
a/=10 				#10.01.1.3 整除
a//9  				#111.2 字符串的定义与相关操作
1.2.1 字符串的定义
字符串在Python中有多种定义形式:
- 单引号定义法
- 双引号定义法
- 三引号定义法:与多行注释的写法一样,同样支持换行操作- 使用变量接收它,它就是字符串
- 不使用变量接收它,就可以作为多行注释使用
 
1.2.2 字符串的引号嵌套
- 单引号定义法,可以内含双引号
- 双引号定义法,可以内含单引号
- 可以使用转义字符”\“来将引号解除效用- 例:name = "\"黑马程序员\""
 
- 例:
1.2.3 字符串的拼接
使用”+“号连接字符串变量或字符串字面量即可,但无法和非字符串类型进行拼接。
1.2.4 索引
word='Python'
word[0] #'P'
word[-1] #'n'1.3 字符串的格式化与对齐
字符串的拼接有明显的劣势:
- 变量过多,拼接起来较麻烦
- 字符串无法和数字或其他类型完成拼接
1.3.1 占位型拼接
- %s表示:占位
- s表示:将变量变成字符串放入占位的地方
name = "黑马程序员"
message = "学IT就来 %s" %name
print(message)多个变量占位,变量要用括号括起来,并按照占位的顺序填入:
class_num = 57
avg_salary = 16781
message = "Python大数据学科,北京%d期,毕业平均工资:%d" % (class_num,avg_salary)
print(message)可以使用辅助符号”m.n“来控制数据的宽度和精度:
- m控制宽度,要求是数字(设置的宽度小于数字自身将不生效)
- .n控制小数点精度,要求是数字,会进行小数的四舍五入
1.3.2 快速格式字符串的方式
f"内容{变量}"(f意为format)
这种方式不理会类型,且不作精度控制,适合对精度没有要求的时候快速使用。
1.3.3 对齐字符串
- ^:居中,后面带宽度
- <:左对齐,后面带宽度
- >:右对齐,后面带宽度
- :后面带填充的字符,只能是一个字符,不指定则是默认用空格填充
print("{:>5}".format(1))  			#宽度为5,右对齐
print("{:>5}".format(10))
print("{:>5}".format(100))
print("{:>5}".format(1000))输出结果为:
   1
  10
 100
1000| 数字 | 格式 | 输出 | 描述 | 
|---|---|---|---|
| 3.1415926 | {:.2f} | 3.14 | 保留小数点后两位 | 
| 3.1415926 | {:+.2f} | +3.14 | 带符号保留小数点后两位 | 
| -1 | {:-.2f} | -1.00 | 带符号保留小数点后两位 | 
| 2.71828 | {:.0f} | 3 | 不带小数 | 
| 5 | {:0>2d} | 05 | 数字补零 (填充左边, 宽度为2) | 
| 5 | {:x<4d} | 5xxx | 数字补x (填充右边, 宽度为4) | 
| 10 | {:x<4d} | 10xx | 数字补x (填充右边, 宽度为4) | 
| 1000000 | {:,} | 1,000,000 | 以逗号分隔的数字格式 | 
| 0.25 | {:.2%} | 25.00% | 百分比格式 | 
| 1000000000 | {:.2e} | 1.00e+09 | 指数记法 | 
| 13 | {:>10d} | 13 | 右对齐 (默认, 宽度为10) | 
| 13 | {:<10d} | 13 | 左对齐 (宽度为10) | 
| 13 | {:^10d} | 13 | 中间对齐 (宽度为10) | 
| 11 | '{:b}'.format(11) '{:d}'.format(11) '{:o}'.format(11) '{:x}'.format(11) '{:#x}'.format(11) '{:#X}'.format(11) | 1011 11 13 b 0xb 0XB | 进制 | 
# 正号表示正数
print("{:+2f}".format(3.14))            #+3.140000
print("{:-2f}".format(-1))              #-1.000000
# 不带小数的
print("{:.0f}".format(3.23123131))      #3
# 以逗号为分隔符的
print("{:,}".format(100000))            #100,000
# 表示一个百份比
print("{:.2%}".format(0.25))            #25.00%1.4 切片操作
简单切片指的是这样的切片形式:a[start:stop],其行为是得到下标在这样一个前闭后开区间范围内的元素,其中start和stop为负数时,简单看作是负数下标对应的位置即可。
word[0:2]  			#'Py'相当于数学中的[0,2)
z = [1,2,3,4,5]
z[:-1]				#[1, 2, 3, 4]1.5 Python中的基本概念
- 列表list
x1=[1,2,3,4]- 元组Tuple
x2=(1,2,3,4,5,6,7)- 步长
print(x2[1::2])  #第三个参数代表步长,答案为(2,4,6)- 序列重复
x3="123"*5  #123123123123123二、列表
列表的下标索引,从前向后从0开始,每次+1;从后向前从-1开始,每次-1。
2.1 追加元素
#一个元素
x1.append("Hello") #仅对列表使用append,[1,2,3,4,"Hello"]
#追加多个元素
x1.extend([6,7]) 2.2 中间插入元素
x1.insert(2,"GREAT") #x1=[1, 2, 'GREAT', 3, 4, 'Hello', 6, 7]2.3 删除元素
x1.remove(3) 			#remove会删除查找到的第一个元素,并且没有返回值
del x1[2]     			#删除指定元素
element = x1.pop(2)		#取出(并删除)元素,并用一变量接收返回值2.4 反转列表
x1.reverse()2.5 排序
x1.sort()三、元组
元组一旦定义完成,就不可修改。
特例:
可以修改元组内的
list内容(修改元素、增加、删除、反转等)。
3.1 只有一个元素的元组定义
a1=("Hello",)
print(a1,type(a1))  	#<class 'tuple'>3.2 统计某元素在元组中出现的次数
a1.count("Hello")3.3 元组的嵌套
t = ((1,2,3),(4,5,6))
num = t[1][2]			#6注:
元组支持
for循环。
四、字典
4.1 格式
		每个元素均由”:“和键值对构成,”:“左边称为键(Key),右边称为值(Value)。
english=
{
	"we":"我们",
    "world":"世界",
    "company":"公司"
}
print(english,type(english)) #运行结果为:{'we':'我们','world':'世界','company':'公司'} <class 'dict'>
english["world"]="城市"  #修改/增加元素4.2 删除、清空与拷贝
del english["city"]  #删除元素
english.clear()  #清空元素
english1=english.copy()  #复制一个具有相同键值对的字典4.3 创建新词典
seq=("name","age","class")
stu1=dict.fromkeys(seq)
print("不指定默认值",stu1)  #{'name':None,'age':None,'Class':None}
stu2=dict.fromkeys(seq,15)
print("指定默认值",stu2)  #{'name':15,'age':15,'Class':15}4.4 返回键对应的值
print(english.get("world"))  #输出"世界",若字典中没有则返回None,可以指定默认值,如下
print(english.get("city","未知")) #输出"未知"
print(english.keys()) #输出:dict_key(['we','world','company']),keys方法常用来判断一个键是否存在于字典中
print("是否存在world?","world" in english.keys()) #True4.5 返回列表
print(english.items())  #输出:dict_items([('we','我们'),('world','世界'),('company','公司')])4.6 遍历字典
for k,v in english.items():
    print(k,"->",v)
#输出:we->我们
#     world->世界
#     company->公司五、集合
5.1 集合的格式
empty = set()   					#空集合
number = {1,2,3}  					#数字集合
mix = set([1,"您好",3.14]) 		   #混合类型集合,输出{3.14,1,'您好'}5.2 集合的基本操作
集合的基本操作主要有增加,减少,取交、并、差等。
number = {1,2,3}
number.add(5)  						#{1,2,3,5},增加元素可以增加重复的(相当于不增加)
number.remove(3) 					#{1,2},减少元素不可以用于不存在的元素,不然会报错设n1={1,3,5},n2={2,7,1,3}:
- 交:n1&n2,结果是{1,3}
- 并:n1|n2,结果是{1,2,3,5,7}
- 差:n1-n2,结果是{5}
- 对称差:n1^n2,结果是{2,5,7}
六、推导式(解析式)
6.1 列表推导式
odd=[x for x in range(10) if x%2!=0]
print(odd)  #输出为[1,3,5,7,9]6.2 字典推导式
d1={n:n**2 for n in range(5)}
print(d1)  #{0:0,1:1,2:4,3:9,4:16}
d2={v:k for k,v in d1.items()}
print(d2)  #{0:0,1:1,4:2,9:3,16:4}6.3 集合推导式
s1={i**2 for i in [-1,-5,1,2,-2]}
print(s1)  ##{1,4,25},推导时可以去除重复元素七、流程控制
7.1 if判断
格式:
x=True
if x: #表达式
    print("It is True!") elif,else
7.2 while循环
x=1
while x<=10:
    print(x)
    x+=17.3 for循环
for x in (1,2,3,4,5)
    print(x)  #输出1 2 3 4 5(每个都将会换行)
for x in range(5)  #range也可指定开始和结束数字,如range(1,10),输出从1到9
    print(x)  #输出0 1 2 3 4 5(每个都将会换行),注意从0开始range还可以指定序列步长,如range(1,10,2),即为1,3,5,7,9(每个都将会换行)。
ps:pass语句,不执行任何操作,其作用是保持程序结构的完整性
如:
if i==3:
    pass
else:
    print(i)while、for后可以跟else,当跳出循环时会执行else的语句。但如果是因为break产生的跳出循环,则不会执行else的语句。
八、函数
8.1 可变参数
(1)*args参数:获取到的是一个元组
def foo(*args):
    print(args)
foo(1,"Shanghai") #输出(1,'Shanghai')(2)**kwargs参数:获取到的是一个字典
def foo(**kwargs):
    print(kwargs)
foo(name='Jack')  #输出{'name':'Jack'}(3)组合使用
def cal(*args,**kwargs)
	s=0
    for i in args:
    	s+=i
    print("输入的数字之和为",s)
    for k,v in kwargs.items():
        print(k,v)
cal(1,2,3,4,5,姓名="Jack") #结果为:输入的数字之和为15  姓名Jack8.2 函数的多返回值
def test():
    return 1,2
x,y = test()
print(x)		#1
print(y)		#28.3 函数的多种传参方式
8.3.1 位置参数
调用函数时根据函数定义的参数位置来传递参数。
8.3.2 关键字参数
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="男")8.3.3 缺省参数
def user_info(name,age,gender='男'):
	print(f"您的名字是:{name},年龄是:{age},性别是:{gender}")
user_info(name="小明",age=20)8.4 函数作为参数传递
函数作为参数传递是一种计算逻辑的传递,计算逻辑由被传入函数决定,而非数据的传递。
def test_func(compute):
    result = compute(1,2)	#确定compute是函数
    print(result)
def compute(x,y):
    return x + y
test_func(compute)8.5 lambda匿名函数
lambda关键字,可以定义匿名函数(无名称)。匿名函数只可以临时使用一次。
匿名函数定义语法:lambda 传入参数 : 函数体 
注:
函数体就是函数的执行逻辑,只能写一行,无法写多行代码。
def test_func(compute):
    result = compute(1,2)	#确定compute是函数
    print(result)
test_func(lambda x,y: x + y)8.6 filter函数
filter函数是内置函数,用于过滤序列,即过滤掉不符合条件的元素。
a1=[1,2,3,4,5,6,7,8]
a2=[item for item in filter(lambda x:x>5,a1)]
print(a2)  #[6,7,8]8.7 内置函数__call__()
Python中一切皆对象,函数也是对象,同时也是可调用对象(callable)。
一个类实例要变成一个可调用对象,只需要实现一个特殊方法__call__()。
允许一个类的实例像函数一样被调用。实质上说,这意味着**x()与x.__call__()是相同的**。注意__call__参数可变。这意味着你可以定义__call__()为其他你想要的函数,无论有多少个参数。
class Entity:
    def __init__(self, size, x, y):
        self.x, self.y = x, y
        self.size = size
    def __call__(self, x, y):
        '''改变实体的位置'''
        self.x, self.y = x, y
e = Entity(1, 2, 3)
print(e.x)
print(e.y)
e(4, 5)
print(e.x)
print(e.y)8.8 函数注释
格式为:(参数注释+返回值注释)
def function_name(a:expression,b:expression)->expression:
    function body
return value若需要获取函数注释,可以使用__annotations__的方法:
print(function_name.__annotations__)九、类和对象
9.1 类的属性
在Python中,构造函数就是类的__init__方法,当一个类定义了__init__方法后,类在实例化时会自动调用__init__方法,用于创建新的类实例。在构造方法中我们可以初始化一些属性(或称成员变量、类变量)。
class Dog:
    def __init__(self,name)
    	self.name=name
        self.age=3
dog=Dog("旺财")
print(dog.name) #旺财
print(dog.age)  #3类中定义的方法第一个参数必须是“self”,即def play(self)。
私有属性可用dog.__name加两条下划线的形式,只能在类内进行修改。私有方法同理。
9.2 继承
定义要从哪个父类继承,只需在定义子类的名字后面的括号中填入父类的名字即可,如:
class Animal:
    def __init__(self,name)
    	self.name=name
        self.age=3
class Dog(Animal):
    pass9.3 多态
多态的意思就是多种形态。多态意味着即使不知道变量所引用的对象是什么类型,也能对对象进行操作。多态会根据类的不同表现出不同的行为。
9.3.1 继承方式实现多态
class Animal:
    def say(self):
        print("Animal")
class Dog(Animal):
    def say(self):
        print("Dog")
class Cat(Animal):
    def say(self):
        print("Cat")
dog=Dog()
dog.say()  #Dog
cat=cat()
cat.say()  #catps:判断一个实例是否是某个对象可以用isinstance函数,格式为isinstance(dog,Dog),返回TrueorFalse。
9.3.2 函数参数实现多态
#接上例
def animal_say(animal:Animal)
	animal.say()
animal_say(dog)
animal_say(cat)9.3.3 鸭子类型
 鸭子类型中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口决定的,而是由当前方法和属性的集合决定的。
class Dog:
	def say(self):
        print("Dog")
class Cat:
    def say(self):
        print("Cat")
def animal_say(animal): #编写一个函数,使它接受一个类型为鸭子的对象
    animal.say()
dog=Dog()
cat=Cat()
animal_say(dog)9.4 静态方法和类方法
9.4.1 静态方法
		定义静态方法时,在定义函数的上面一行添加“@staticmethod”,且静态方法不再有第一个默认参数“self”,本身也不能调用成员变量和成员方法。
class Animal
	name="动物"
    @staticmethod
    def play()
    	print("Playing")
Animal.play()9.4.2 类方法
			类方法将该方法绑定在定义的类上,而不是绑定在实例上。在定义函数的上面一行添加“@classmethod”,有初始参数”cls“,类方法指向的是定义的类本身,类方法可以读取和修改类变量。
class Animal
	name="动物"
    @classmethod
    def play(cls)
    	print(c"Playing")
Animal.play()十、模块
模块就是一个包含了Python定义和声明的”.py”文件。在完成一个”.py”文件后,在与其同目录的文件可以使用这个模块,即import fibs。如果要导入模块中的一个指定的部分到当前命名空间中,可用from fibs import fib,fib2。
10.1 获取模块名
模块的模块名可以通过全局变量”__name__“获得。
import fibs
print(fibs.__name__)  #fibs
print(__name__)       #__main__由此可知,在定义模块时,可通过看当前的”__name__“变量是否为”__main__“来判断当前文件是被运行还是作为模块被导入。
if __name__=="__main__"
	print("直接运行")
else:
    print("被作为模块导入")10.2 dir函数
dir函数可以列出对象的模块标识符,包括函数、类和变量。模块也是对象,调用模块中的内容和调用对象中的内容的方法是一样的。
import fibs
	fibs.fib(10)
print(fibs.fib2(5))10.3 包
模块即一个py文件,通过“包”加以组织。“包”是一个包含模块,且至少包含一个__init__.py的文件夹,模块中包含代码。
package9
|--__init__.py
|--fun1.py
|--fun2.pypackage9/fun1.py:
def print1():
    print("fun1")package9/fun2.py:
def print2():
    print("fun2")__init__.py:
if __name__ == "__main__":
    print("作为主程序运行")
else:
    print("package初始化")demo.py
#导入package9包
from package9.fun1 import print1
from package9.fun2 import print2
print1()
print2()10.4 常用标准库
10.4.1 math库
两个常量:圆周率和自然常数e,调用方式为:math.pi,math.e。
运算函数:向上取整,如math.ceil(1.7),结果为2;向下取整,如math.floor(1.7),结果为1。
 对数函数(默认底数为e,可使用第二个参数来改变对数的底数)
 平方根计算(sqrt),三角函数计算,角度(degree)弧度(radians)转换
10.4.2 random库
random.random用于生成一个0-1的随机浮点数:0<=n<1.0。
random.uniform(1,150)用于生成一个指定范围内的随机浮点数。
random.choice(seq1)会从给定的序列中获取一个随机元素。
import random
seq1=(1,15,8,97,22)
seq2=["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]
print(random.choice(seq1))
print(random.choice(seq2))random.shuffle(seq1)用于将一个列表中的元素打乱(注意:原来的序列必须是可以修改的,故元组等类型不能作为其参数使用)。
十一、文件操作与IO
11.1 文件基本操作
打开文件:
file_name="10.1.py"
f=open(file_name)文件模式:r+,表示打开一个文件用于读写,文件指针将会放在文件的开头。
使用open函数返回的是一个文件对象,然后通过使用read方法从一个打开的文件中读取内容到字符串。写文件则用write方法。write方法返回写入文件的字符串的长度。
f=open("readme.txt")
txt=f.read()
print(txt)若想在已有的文件内容后追加内容,可在打开文件时使用”a“模式,这样就可以在文件中追加写入内容。
关闭文件用f.close()。
11.2 按行读/写文件
11.2.1 按行读取文件内容
f.readline()
readlines函数会将文件内容按行切割,返回一个list列表对象。(注意:readlines函数会保留结尾的换行符,不会去掉换行符,直接print列表元素会发现每次输出都跟随一个空白行)
f=open("a.txt","r+")
for line in f.readlines():
    print(line)此外还有直接迭代文件对象本身的操作:这是一种”惰性“读取文件的方式,只有迭代到需要读取的一行,才会真的执行读取操作。
f=open("a.txt","r+")
for line in f:
    print(line)11.2.2 按行写入文件内容
利用writelines方法接收一个参数(必须是列表),列表的每个元素就是想写入的每行文本的内容。
f=open("writelines.txt","r+")
lines=[]
for i in range(10):
    lines.append(str(i))
f.writelines(lines)运行结果为0123456789,说明其不会帮助我们在每行之后添加换行符。
i=1
f=open("numbers.txt","a")  #利用追加模式,参数从w替换为a即可
while i <= 10:    
    f.write("{}\n".format(i)) #format()为格式化字符串的函数
    i=i+1
f.close()11.3 StringIO函数
对str操作,要把str写入StringIO,需要先创建一个StringIO对象,然后像文件一样写入即可。
from io import StringIO
f=StringIO()
f.write('hello')
f.write(' ')
f.write('world!')
print(f.getvalue()) #获得写入后的str,输出hello world!要读取StringIO,可以先用一个str初始化StringIO,然后像读文件一样读取。其中strip()方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。若参数不为空,则移除所有该参数,如print str.strip( '0' );  #去除首尾字符 0。
from io import StringIO
f=StringIO('Hello!\nWorld!\nWelcome!\n')
while True:
    s=f.readline()
    if s=='':
        break
    print(s.strip()) 11.4 BytesIO函数
from io import BytesIO
f=BytesIO()
f.write("您好".encode("utf-8")) #以指定的编码格式编码字符串
print(f.getvalue())
print(f.getvalue().decode("utf-8")) #以指定的编码格式解码字符串输出结果如下:b'\xe6\x82\xa8\xe5\xa5\xbd'
                           您好  
读取方式如下:
from io import BytesIO
f=BytesIO(b'\xe6\x82\xa8\xe5\xa5\xbd')
print(f.read().decode("utf-8"))  #输出"中文"11.5 序列化与反序列化
pickle.dumps可以把任意对象序列化成bytes对象,然后写入文件中永久存储。
import pickle
class Student:
    def __init__(self,name,age,gender):
        self.name=name
        self.age=age
        self.gender=gender
student1=Student("小明",15,"男")
print(pickle.dumps(student1))十二、日期和时间
12.1 time函数
 time函数用于返回当前时间的时间戳,time函数返回的是浮点数。
import time
now=time.time()
print("当前的时间戳是:%f" % now)输出结果为:当前的时间戳是:1641976277.347599。
12.2 localtime函数
		 localtime函数的作用是将时间戳格式化为本地时间,返回struct_time对象。localtime函数有一个参数用于接收时间戳,如果调用函数时不提供时间戳, localtime函数默认使用当前时间戳。
import time
print("当前时间",time.localtime())
print("0时间戳对应的时间",time.localtime(0))12.3 datetime模块
 datetime模块包含了日期和时间的所有信息。
12.3.1 date对象
表示在日历中的一个日期(包含年、月、日)。格式为date=datetime.date(2018,7,1)。
12.3.2 一些方法
 Today方法/weekday方法分别返回当天日期和当前星期数(星期一返回0,依此类推)。isoweekday方法星期一返回1,依此类推。isoformat方法返回日期格式为ISO格式,即“YYYY-MM-DD”字符串。strftime方法可以格式化输出日期,如print(date.strftime("%Y-%m-%d"))。对于时-分-秒,分别为%H:%M:%S。
十三、多线程和并行
		多线程是指在软件或者硬件上实现多个线程并发执行的技术。Python标准库中关于线程的主要是_thread和threading模块。
13.1 _thread模块
13.1.1 概述
		核心是start_new_thread方法,格式为:_thread.start_new_thread(function,args[,kwargs])。线程使用参数列表args(必须是元组)执行函数,可选的kwargs参数指定关键字参数的字典。在单个线程执行时,别的线程也在”同步“地执行。
import time
import datetime
import _thread
date_time_format="%H:%M:%S"
def get_time_str():
    now=datetime.datetime.now()
    return datetime.datetime.strftime(now,date_time_format)
def thread_function(thread_id):
    print("Thread %d\t start at %s"%(thread_id,get_time_str()))
    print("Thread %d\t sleeping"%thread_id)
    time.sleep(4)
    print("Thread %d\t finish at %s"%(thread_id,get_time_str()))
def main():
    print("Main thread start at %s"%get_time_str())
    for i in range(5):
        _thread.start_new_thread(thread_function,(i,))
        time.sleep(1)
    time.sleep(6)
    print("Main thread finish at %s"%get_time_str())
if __name__ =="__main__":
    main()运行结果如下:
Main thread start at 17:44:03
Thread 0	 start at 17:44:03
Thread 0	 sleeping
Thread 1	 start at 17:44:04
Thread 1	 sleeping
Thread 2	 start at 17:44:05
Thread 2	 sleeping
Thread 3	 start at 17:44:06
Thread 3	 sleeping
Thread 0	 finish at 17:44:07
Thread 4	 start at 17:44:07
Thread 4	 sleeping
Thread 1	 finish at 17:44:08
Thread 2	 finish at 17:44:09
Thread 3	 finish at 17:44:10
Thread 4	 finish at 17:44:11
Main thread finish at 17:44:1413.1.2 线程锁
_thread.allocate_lock方法返回一个Lock对象。Lock对象有三个方法:
【1】acquire方法:用于无条件地获取Lock对象(如果有必要,等待他被另一个线程释放,一次只有一个线程可以获取锁定)。
【2】release方法:用于释放锁,释放之前必须先锁定,
【3】locked方法:用于返回锁的状态,如果已被某个线程锁定,则返回True,否则返回False。
lock=_thread.allocate_lock()
lock.acquire()
locks.append(lock)13.2 Threading模块
利用threading.Thread创建线程。
import time
import datetime
import threading
date_time_format="%H:%M:%S"
def get_time_str():
    now=datetime.datetime.now()
    return datetime.datetime.strftime(now,date_time_format)
def thread_function(thread_id):
    print("Thread %d\t start at %s"%(thread_id,get_time_str()))
    print("Thread %d\t sleeping"%thread_id)
    time.sleep(4)
    print("Thread %d\t finish at %s"%(thread_id,get_time_str()))
def main():
    print("Main thread start at %s"%get_time_str())
    threads=[]
    #创建线程
    for i in range(5):
        thread=threading.Thread(target=thread_function,args=(i,))
        threads.append(thread)
    #启动线程
    for i in range(5):
        threads[i].start()
        time.sleep(1)
    #等待线程执行完毕
    for i in range(5):
        threads[i].join()  #阻塞线程,使用join后,后面的语句需要等当前进程完成之后才能执行
    print("Main thread finish at %s"% get_time_str())
if __name__ =="__main__":
    main()运行结果如下:
Main thread start at 20:36:02
Thread 0	 start at 20:36:02
Thread 0	 sleeping
Thread 1	 start at 20:36:03
Thread 1	 sleeping
Thread 2	 start at 20:36:04
Thread 2	 sleeping
Thread 3	 start at 20:36:05
Thread 3	 sleeping
Thread 0	 finish at 20:36:06
Thread 4	 start at 20:36:06
Thread 4	 sleeping
Thread 1	 finish at 20:36:07
Thread 2	 finish at 20:36:08
Thread 3	 finish at 20:36:09
Thread 4	 finish at 20:36:10
Main thread finish at 20:36:10 
                        
                        