python快速入门
一、开头杂谈
生日:1991,开发语言:c
python是一个脚本语言,运行时python编译器会将.py文件转化为m.pyc文件然后再放入PVM(python virtual machine)即python虚拟机去运行。那么,其编译器和虚拟机该如何安装?找python官网下一个安装包安装就都有了。
python2和python3的区别:
python2的默认dos命令是这样的:python xx.py
python3的(有些版本)默认dos命令是这样的:python3 xx.py
当然dos命令怎么改都可以,只是处步接触时会有点迷惑
python2的print输出:print hello
python3的print输出: print(“hello”)
二、基础语法
1.注释
第一种注释:#xxxxx
第二种注释:""“xxxx”""
区别是第二种注释一般放在包、模块、类或者函数里的第一个语句,可以通过对象的__doc__
属性找到这个注释内容
#test test test
def find_all ():
"""The funtion is mean to find all files"""
print(find_all.__doc__)
tips:
从上面例子可以看出,定义且未执行的一个函数就是一个对象,那么也就是说,一种数据类型的声明也是对象的实例化,c语言能有这样的语法?这就很有面向对象的特色。
2.行的缩进规范
python中特色就是以行的缩进来表示代码块:4个空格 或 一个tab
if True:
print("true")
else:
print("false")
3.语句换行
字符串过长换行:
string = (" 唐诗 "
" 宋词 ")
print(string)
[]、{}、()中元素过长换行:
total = ['19.0','21.0',
'31.0','21.0',
'21.0','21.0']
4.变量类型
infinite(无限数)
-
数字类型(number)
-
整形(int)
十进制、二进制(0b / 0B 开头)、八进制(0o / 0 / o0 开头)、十六进制(0x / 0X 开头)
-
浮点型(float)
2.3E20 2.3e20
-
复数(complex)
3.12+1.23j -1,23-98j
-
-
布尔类型(bool)
True / False (1 / 0)
-
字符串类型(string)
‘xxx’ “xxx” ‘’‘xxx’’’
-
列表类型(list)
[1 ,2 ,‘xxx’]
列表生成语法:
arr = [i*2 for i in range(4)] #arr等于[0,2,4,6]
冒号语法:
arr = [1,2,3,4,5,6,7,8,9,10]
print(arr[1:4])
print(arr[1:10,2])
print(arr[2:])
-
元组类型(tuple)
(1 ,2 ,‘xxx’)
拆包:
a , b = (1,2) b , a = (a,b)
-
字典类型(dictionary)
{“name”:“xxx”,“age”:21}
5.标识符和关键字
标识符形如变量名、函数名等,以字母、下划线和数字组成,不以数字开头
关键字指构成python语法的字符,标识符不可单独使用
6.运算符
6.1算数运算符
+ | - | * | / | % | ** | // |
---|---|---|---|---|---|---|
加 | 减 | 乘 | 除 | 取余 | 幂 | 整除 |
6.2赋值运算符
a = 1
a = b = c = 1
a,b = 1,2
6.3复合赋值运算符
+= -= *= /= %= **= //=
6.4比较运算符
== != > < >= <=
6.5逻辑运算符
and or not
6.6成员运算符
in not in
7.位运算
<< 按位左移 >> 按位右移 & 按位与 | 按位或 ^ 按位异或 ~ 按位取反
三、常用语句
1.判断语句
age = 18
if age>=18:
print("成年人")
elif age>=13:
print("青少年")
else:
print("小孩")
猜拳游戏:
import random
def check(pnum,cnum):
words_tuple = ('剪刀','石头','布')
result_tuple = ('和局','电脑赢','玩家赢')
renum = 0;
if pnum==cnum:
renum = 0;
elif (pnum+1)%2 == cnum:
renum = 1;
else:
renum = 2;
print("电脑:" + words_tuple[cnum]+",玩家:" + words_tuple[pnum] + "," + result_tuple[renum])
player_num = int(input("猜拳(0剪刀、1石头、2布):"))
computer_num = random.randint(0, 2)
check(player_num,computer_num)
2.循环语句
2.1 for循环
num_tupple = (1,2,3)
for a in num_tupple:
print(a)
2.2 while循环
num_tupple = (1,2,3)
index = 0
while index<3:
print(num_tupple[index])
index += 1
3.其他语句
3.1 break
循环中断
3.2 continue
跳过这轮循环
3.3 pass
保持程序完整的占位语句,没有实际意义
3.4 else
else可以作为if的分支语句,也可以作为for / while的分支语句
num_tupple = "123"
for char in num_tupple:
print(char)
else:
print("循环结束")
index = 0
while index < 3:
print(num_tupple[index])
index += 1
else:
print("循环2结束")
3.5 in
连续的数据类型(字符串、列表、元组)都可以用in来寻找数据:
str = "123"
tuple = (1,2,3)
list = [1,2,3]
if 1 in list:
print(1)
if 2 in tuple:
print(2)
if "3" in str:
print(3)
四、函数
1.默认参数
def sayNum(num = 30):
print(num)
sayNum()#30
sayNum(40)#40
2.不定长参数
def sayNum(num = 30,*args,**kwargs):
print(num)
if args:
print("args:")
for arg in args:
print(arg)
if kwargs:
print("kwargs")
for kwarg in kwargs:
print(kwarg+":"+str(kwargs[kwarg]))
sayNum()
sayNum(40)
sayNum(40,50,60,70,80)
sayNum(40,50,60,70,80,a=90,b=100)
*args数据类型:元组,多出的无命名参数会被放入这里
**kwargs数据类型:字典,多出的有命名参数会被放入这里
3.匿名函数
1、一般函数声明需要def 函数名
,当函数需要更换其函数内容的时候,函数就要重新定义。
2、当函数需要作为参数传入另一个函数去执行的时候,就需要匿名函数
语法:
fun = lambda a,b:a+b
特点:
冒号后的语句执行结果会被返回,省去了return的语句
例子如下:
#例子1
def counter(num1,num2,acaculate):
return acaculate(num1,num2)
print(counter(1,2,lambda a,b:a+b))
#例子2
students = [{"name":"小明","age":21,"sex":"男","brief":"游手好闲吃吃喝喝"},
{"name":"小红","age":18,"sex":"女","brief":"天底下最漂亮"},
{"name":"小亮","age":20,"sex":"男","brief":"游手好闲吃吃喝喝"}]
def show(students):
for item in students:
print(item)
print("\n")
students.sort(key = lambda x:x['name'])
show(students)
students.sort(key = lambda x:x['age'])
show(students)
#例子3
nums1 = [2,4,6]
nums2 = [1,2,3,4,5,6,7]
re = list(filter(lambda x:x not in nums1,nums2))
print(re)
*.名片管理1
import os
def display_menu():
#菜单展示函数
os.system("cls")
global options_list
options_list_length = len(options_list)
list_index = 0
print("-" * 37)
print(" 名片管理系统 v1.0")
while list_index<options_list_length:
print(str(list_index)+". "+options_list[list_index])
list_index += 1
print("-" * 37)
def worker(status):
#工作函数
global options_list
print("="*37)
print(options_list[status])
print("="*37)
def get_option(status):
tmp = input("请输入选择的序号:")
return int(tmp)
def run():
global status
while status != 6:
display_menu()
worker(status)
status = get_option(status)
print("退出系统")
#全局变量
status = 0
options_list = ["菜单","添加名片","删除名片","修改名片","查询名片","获取所有名片信息","退出系统"]
#程序开始
run()
五、变量作用域
从一个嵌套函数讲起:
word = "word11"
words = "words"
def say():
word = "word"
def go():
print(word)
go()
def hh():
print(words)
hh()
say()
输出:word,words
1.四种作用域
函数内,函数外的函数,全局,内建
内建:
文件作为模组被引入时 ,__name__ = '被引入的文件名'
,文件直接运行时,__name = '__main__'
例如:
#test2.py
def say():
print(__name__)
if __name__ == "__main__":
say()
#test.py
from test2 import say
say()
#python test.py ==>test2
#python test2.py ==>__main__
当我们构建这样的代码:
if __name__ == "__main__":
...
那么当这个文件被引用的时候,下方代码就不会被执行。那么我们就可以称这个人为创造的区域叫内建作用域
那么就延伸出这么写:
if __name__ = "被引用的文件名":
...
这样就可以控制当前文件是被引用还是直接执行下的代码
2.闭包
一个函数被执行的时候,会开一个存储空间给它,当函数执行完毕,存储空间被回收释放,那么其中的局部变量都会被回收。
def hh():
word = 'hh'
print(word)
hh()
print(word)
#==>hh
#==>name 'word' is not defined
因为hh函数的局部变量没有被任何代码引用,那么函数的空间就被删除。
但局部变量仍然被其他代码引用,那么原有的空间就不会被删除,局部变量地址仍然存在,仍可以被引用。如下:
word = "aa"
def hh():
word = 'hh'
print(word)
def say():
print(word)
return say
ll = hh()
ll()
#==> hh
#==> hh
观察上面,say函数被返回并被执行,但是输出的word却是hh函数中的word。那是因为hh函数中有局部变量被引用,存储空间没有被回收,其中的word还被say函数引用。
利用上面的特性,就可以仅通过函数来作出循环动作:
def say(num):
if num>0:
num = num-1
print("word")
return say(num)
else:
return 0
say(10)
#输出十次word
但这只是闭包这种特性的展示,实际上这种写法很占内存,因为闭包使得程序占用没有必要的空间。
最主要的应用是模组引用方面,但这个可以衔接到下面一点讲。
3.命名空间
在当前作用域中你还有多少命名的余地。
例如a = 1
,那么后面再a = 2
,它都是在改同一个变量。没有两个变量都叫a,用一个少一个。
为了可以给更多的变量命名而不重复,可以充分使用数字和下划线的同时,也有很多方法。
例如用类:
word = "lalallala"
class Nam:
def __init__(self):
self.word = "balabalabala"
nam = Nam()
print(word)
print(nam.word)
word变量已经被定义,但是我有一个变量也是word,但是对象的一个word,那么就可以区分开来。
还有一种就是衔接上面闭包的方法,也是模组引用的特性:
#test2.py
word = "hello"
def say():
print(word)
#test.py
from test2 import say
word = "lalallala"
say()
print(word)
#>hello
#>lalallala
test中有变量word,test2有变量word,但是两个不冲突。
六、对象
1.实例化
首先理清两个概念:类和对象。
类是对象的模板,类有自己的存储空间,对象也有自己的存储空间
对象通过类创建,这个过程叫实例化。
如下:
class Stu:
def __init__(self):
print("create object")
stu = Stu()#实例化
stu2 = Stu()#实例化2
构造函数def __init__():
是实例化过程会在对象内部执行一次的函数
self是实例化中自动创建的变量,记录了当前新对象的存储空间的地址。
stu变量内容也就是新对象的地址。即:
self == stu #True
2.属性
给对象添加一个属性,可以通过这个对象去访问这个属性。这也被应用到命名空间拓展中。
属性可以通过内部构造函数设置,也可以通过外部设置
class Stu:
def __init__(self,name):
self.name = name#内部设置
print(name+":created")
xiaoming = Stu("xiaoming")
xiaoming.age = 16#外部设置
当想避免对象属性被外部赋值,就在属性名前加上两个下划线,设置私有属性:
class Stu:
def __init__(self,name,age):
self.name = name#内部设置
self.__age = age
print(name+":created")
def getage(self):
print(self.__age)
xiaoming = Stu("xiaoming",18)
那么xiaoming.__age
属性就无法被外部访问,就只能通过定义好的内部函数操作(如这里的getage函数)
3.方法
可以给对象定义内部方法,也可以定义外部方法。
一般是用内部方法,通过类定义
class Stu:
def __init__(self,name):
self.name = name
print(name+":created")
def run(self):
#内部方法
print(self.name+"is running~")
xm = Stu("xiaoming")
xm.run()
def talk(obj):
#外部方法
print(obj.name)
xm.talk = talk
xm.talk(xm)
4.类
python中很多元素都是对象,由各自的类实例化而得到的,我们定义变量实际上也是在实例化各种对象。所有的对象都有自己的方法和属性,例如字符串对象的find方法str.find()
。
num = 1
num2 = 0.1
bo = True
word = "words"
arr = [1,2,'xxx']
tul = (1,2,'xx')
dic = {"name":"ag"}
def go():
return
class Stu:
def __init__(self):
print("create object")
print(type(num))
print(type(num2))
print(type(bo))
print(type(word))
print(type(arr))
print(type(tul))
print(type(dic))
print(type(go))
print(type(Stu))
print(type(type))
#输出:
#<class 'int'>
#<class 'float'>
#<class 'bool'>
#<class 'str'>
#<class 'list'>
#<class 'tuple'>
#<class 'dict'>
#<class 'function'>
#<class 'type'>
#<class 'type'>
还记得开篇讲到的注释吗?
def find_all ():
"""The funtion is mean to find all files"""
print(find_all.__doc__)
通过这个注释,我们可以为函数这个对象添加一个文本属性。印证了上面关于函数也是function类的实例化对象的展示。
5.运算符重载
通过前几点的基本认识,得出一个结论:在对变量进行操作的时候,实质是与对象打交道。
例如,当我们对变量进行加或减等操作的时候,就会调用对象的特殊方法。想象一下:a + b
变成a.add(b)
。而这个a变量(实质是a对象)的add方法被调用,则称为运算符重载。
如下例子:
class Mularray:
def __init__(self,obj):
self.data = obj[:]
def __add__(self,obj):
x = len(self.data)
y = len(obj.data)
number_list = []
if x > y:
max = x
middle = y
big = self.data
small = obj.data
else:
max = y
middle = x
big = obj.data
small = self.data
for i in range(max):
print(i)
if i == middle:
number_list.append(big[i])
else:
number_list.append(big[i] + small[i])
return Mularray(number_list[:])
a1 = Mularray([1,2,3,4])
a2 = Mularray([11,22,33])
a3 = a1+a2
print(a3.data)
#输出[12, 24, 36, 4]
当对象相加的时候a1+a2
,就会调用a1对象中的__add__
函数,并将a2对象作为参数传入
a1+a2
相当于a1.__add__(a1,a2)
这是一个简单的例子。
这里列一下部分的运算符重载方法:
方法 | 说明 | 触发调用方法 |
---|---|---|
__add__ | 加法运算 | x+y,x+=y |
__sub__ | 减法运算 | x-y,x-=y |
__mul__ | 乘法运算 | x*y,x*=y |
__div__ | 除法运算 | x/y,x/=y |
__mod__ | 求余运算 | x%y,x%=y |
__bool__ | 真值测试 | boo(x) |
__repr__,__str__ | 打印、转换 | print(),repr(),str() |
__contains__ | 成员测试 | item in x |
__setitem__ | 索引、分片 | x[i]、x[i:j]、没有__iter__ 的for循环等 |
__getitem__ | 索引赋值 | x[i]=值、x[i:j] = 序列对象 |
__delitem__ | 索引和分片删除 | del x[i]、del x[i:j] |
__len__ | 求长度 | len(x) |
__iter__,__next__ | 迭代 | iter(x),next(x),for循环等 |
__eq__,__ne__ | 相等测试、不等测试 | x==y,x!=y |
__ge__,__gy__ | 大于等于测试、大于测试 | x>=y,x>y |
__le__,__lt | 小于等于测试、小于测试 | x<=y,x<y |
6.继承与多态
6.1类继承符
当我们定义了一个类,另一个类需要在前面类的基础上扩展,那么就要用类继承符。
在类声明的时候纳入继承括号 class 类名(父类名)
:
class Person:
def sayhello(self):
print("hello")
class Student(Person):
pass
p1 = Person()
p1.sayhello()
s1 = Student()
s1.sayhello()
#输出
#hello
#hello
6.2根类
我们在第4点说到,py所有的数据都是对象,它们都有各自对应的类,而所有的类都是继承自object这个超级父类。
例如一个类的简单声明:
class Person:
def __init__(self):
print("Obj Created")
这个类的声明严格来说应该是如下:
class Person(object):
def __init__(self):
print("Obj Created")
没有继承自创的类,那么默认都是继承object这个类。
object被成为py的根类,这个根类提供了许多基本的特性和方法,为其他类的创建和使用提供了基础。
例如所有对象都会有的__init__
方法,各种运算符重载方法,__del__
方法,__mro__
方法等等都是继承自object这个根类。
6.3父类构造方法
在子类的构造方法中想要引用父类的构造方法,可以直接使用类继承符中包含的父类的构造方法。
如下:
class Person(object):
def __init__(self,name):
self.name = name
def getname(self):
print("my name is:"+self.name)
class Student(Person):
def __init__(self,name,sid):
self.sid = sid
Person.__init__(self,name)
def getinfo(self):
print("my name is:%s ,my sid is:%s" % (self.name,self.sid))
s1 = Student("Tom",18)
s1.getname()
s1.getinfo()
还可以调用super()方法获取父类并调用其构造方法
class Person(object):
def __init__(self,name):
self.name = name
def getname(self):
print("my name is:"+self.name)
class Student(Person):
def __init__(self,name,sid):
self.sid = sid
super().__init__(name)
def getinfo(self):
print("my name is:%s ,my sid is:%s" % (self.name,self.sid))
s1 = Student("Tom",18)
s1.getname()
s1.getinfo()
在多继承当中,使用super()优于写父类名
6.4单继承与多继承
这是一个继承的例子:
class Person(object):
def __init__(self,name):
self.name = name
def getname(self):
print("My name is:"+self.name)
class Student(Person):
def __init__(self,name,stuid):
self.stuid = stuid
Person.__init__(self,name)
p1 = Person("Tom")
p1.getname()
s1 = Student("Tony",18)
s1.getname()
这是一个多继承的例子:
class Person(object):
def sayhello(self):
print("hello")
class Weapon(object):
def shot(self):
print("shotting")
class Army(Weapon,Person):
pass
print(Person.__mro__[0].sayhello)
print(Army.__mro__)
类名.__mro__
是一个元组类型的对象属性。
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
可以写一个直观的例子:
class Person(object):
def __init__(self,name):
self.name = name
def getname(self):
print(self.name)
def shot(self):
print("Person shotting~")
class Weapon(object):
def shot(self):
print("Weapon shotting")
class Soilder(Weapon,Person):
def __init__(self,name):
Person.__init__(self,name)
s1 = Soilder("Tom")
print(Soilder.__mro__)
s1.shot()
#输出
#Weapon shotting
上面的例子中,Soilder继承了Weapon和Person,它们都有相同的方法shot。Soilder优先继承了括号中第一个父类的方法即Weapon。
如果Soilder继承括号中父类名顺序不同,Soilder继承的方法不同,输出结果不同:
class Person(object):
def __init__(self,name):
self.name = name
def getname(self):
print(self.name)
def shot(self):
print("Person shotting~")
class Weapon(object):
def shot(self):
print("Weapon shotting")
class Soilder(Person,Weapon):
def __init__(self,name):
Person.__init__(self,name)
s1 = Soilder("Tom")
print(Soilder.__mro__)
s1.shot()
#输出
#Person shotting~
6.5多态
子类重写父类的方法,当不同子类调用相同的方法时,会有不同的结果。
如下例子:
class Person(object):
def __init__(self,name):
self.name = name
def getinfo(self):
print(self.name)
class Student(Person):
def __init__(self,name,sid):
super().__init__(name)
self.sid = sid
def getinfo(self):
print("Name:%s \t Sid:%s"%(self.name,self.sid))
class Teacher(Person):
def __init__(self,name,tid):
super().__init__(name)
self.tid = tid
def getinfo(self):
print("Name:%s \t Tid:%s"%(self.name,self.tid))
S1 = Student("Tom",18)
S1.getinfo()
T1 = Teacher("Tony",56)
T1.getinfo()
Student类和Teacher类重写了其父类的方法getinfo,使得对象相同的方法有不同的结果。
七、程序,进程,线程与协程
*.重要的概念
1)同步:所谓同步,就是发出一个功能调用时,在没有得到结果之前,该调用就不返回或继续执行后续操作。
简单来说,同步就是必须一件一件事做,等前一件做完了才能做下一件事。
2)异步:异步与同步相对,当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作。
3)并行:一个处理器核心跑一个任务,多个任务分别在不同的处理器核心上运行
4)并发:多个任务在一个处理器核心上运行
5)多任务:多任务处理是指用户可以在同一时间内运行多个应用程序 ,多任务系统中有3种层面,即任务、进程和线程。
1.程序
py文件是一个py虚拟机可以识别的指令文件,py虚拟机通过运行py文件开启一个进程。但是py文件不能说就是一个程序,因为py文件无法被计算机直接执行。通过 pyinstaller
将py文件打包成.exe二进制文件,这样就单独是一个程序。
2.进程
程序保存在硬盘,进程保存在主存。
程序被计算机从外存加载到内存执行后,就会生成进程。
进程是计算机资源集中的一个过程,被计算机调集了可能有cpu算力、显卡资源、io设备等,进程的开始结束过程被成为生命周期。
如果我们写一个循环,那么进程会被阻塞,即无法达到进程的结束。通过对循环的控制,可以控制程序的结束。
每个进程都有操作系统分配的id:
import os
pid = os.getpid()
或者通过操作系统的任务管理查看pid
不同进程之间的切换需要计算机经过以下步骤:
- 切换页目录以使用新的地址空间
- 切换内核栈
- 切换硬件上下文
3.线程threading
不同线程之间的切换需要计算机经过以下步骤:
- 切换内核栈
- 切换硬件上下文
3.1主、子线程
简单的线程例子:
import time
import threading
def sing():
"""唱歌 5秒钟"""
for i in range(5):
print("正在唱歌~")
time.sleep(1)
def dance():
"""跳舞 5秒钟"""
for i in range(5):
print("正在跳舞~\n")
time.sleep(1)
def main():
print("边唱边跳\n")
t1 = threading.Thread(target = sing)
t2 = threading.Thread(target = dance)
t1.start()
t2.start()
if __name__ == "__main__":
main()
通过上面例子,我们可以知道sing和dance是同步执行的。也就是边唱边跳。
通过Thread创建的对象称为子线程
进程开始——》main(主线程)——等待子线程结束——》进程结束
————》dance(子线程)
————》sing(子线程)
如果我们不用线程,那么就会出现唱完再跳的情景。
进程开始——》main(主线程)——》sing(主线程)——》dance(主线程)——》进程结束
3.2同步
子线程在执行的时候,主线程仍然会执行直到结束。
如下:
import time
import threading
def sing():
"""唱歌 5秒钟"""
for i in range(5):
print("正在唱歌~")
print(threading.enumerate())
time.sleep(1)
def dance():
"""跳舞 5秒钟"""
for i in range(5):
print("正在跳舞~\n")
time.sleep(1)
def main():
print("边唱边跳\n")
t1 = threading.Thread(target = sing)
t2 = threading.Thread(target = dance)
t1.start()
t2.start()
print("hello")
if __name__ == "__main__":
main()
#输出
边唱边跳
正在唱歌~
[<_MainThread(MainThread, started 5732)>, <Thread(Thread-1 (sing), started 6716)>, <Thread(Thread-2 (dance), initial)>]
正在跳舞~
hello
正在唱歌~
[<_MainThread(MainThread, stopped 5732)>, <Thread(Thread-1 (sing), started 6716)>, <Thread(Thread-2 (dance), started 9132)>]
正在跳舞~
正在唱歌~
正在跳舞~
[<_MainThread(MainThread, stopped 5732)>, <Thread(Thread-1 (sing), started 6716)>, <Thread(Thread-2 (dance), started 9132)>]
正在唱歌~
[<_MainThread(MainThread, stopped 5732)>, <Thread(Thread-1 (sing), started 6716)>, <Thread(Thread-2 (dance), started 9132)>]
正在跳舞~
正在唱歌~
[<_MainThread(MainThread, stopped 5732)>, <Thread(Thread-1 (sing), started 6716)>, <Thread(Thread-2 (dance), started 9132)>]
正在跳舞~
可以看到上面主线程main函数,在开启两个线程后,还有一个输出hello的任务,那它会继续执行,直到主线程任务完成。
即使主线程任务完成,两个子线程仍要继续完成自己的任务,最后主线程结束
线程的运行没有先后顺序
3.3线程通讯
使用全局变量:
import threading
num = 1
def go():
global num
print(num)
def main():
t1 = threading.Thread(target=go)
t1.start()
if __name__ == "__main__":
main()
给线程传参:
import threading
def go(num):
print(num)
def main():
t1 = threading.Thread(target=go,args=(1,))
t1.start()
if __name__ == "__main__":
main()
实例化线程的时候给传入一个参数args,就会作为参数传到子线程的go函数中。args是元组,如果是单个参数就得加逗号:args=(1,)
3.4资源竞争
多个线程在使用同一个全局变量的时候,容易出现资源竞争,导致程序计算错误,或者程序卡死
例如:
import threading
import time
sum = 0
def go(num):
global sum
for tmp in range(num):
sum += 1
print(sum)
def go2(num):
global sum
for tmp in range(num):
sum += 1
print(sum)
def main():
t1 = threading.Thread(target=go,args=(100000000,))
t2 = threading.Thread(target=go,args=(100000000,))
t1.start()
t2.start()
time.sleep(2)
print(sum)
if __name__ == "__main__":
main()
上面这个例子在我电脑就卡死了,args传参1百万时还可以,传1亿就不行了。
3.5互斥锁
为了解决资源竞争
方法一:
threading模块有一个类可以创建互斥锁。
3.4的例子修改一下如下:
import threading
import time
sum = 0
mutex = threading.Lock()#创建资源锁
def go(num):
global sum
mutex.acquire()#
for tmp in range(num):
sum += 1
mutex.release()#
print(sum)
def go2(num):
global sum
mutex.acquire()#
for tmp in range(num):
sum += 1
mutex.release()#
print(sum)
def main():
t1 = threading.Thread(target=go,args=(100000000,))
t2 = threading.Thread(target=go,args=(100000000,))
t1.start()
t2.start()
time.sleep(2)
print(sum)
运行顺利。
执行到acquire方法的时候,对象状态为locked,可以通过对象的locked方法查看。
当第一个子线程t1将mutex为locked时,另一个线程t2执行acquire方法时,会被阻塞住,直到子线程t1用release释放,t2在acquire处结束阻塞,并继续执行后面的代码。
acquire方法还可以微调acquire(blocking=False, timeout=-1)
方法二:
通过上面的例子,我们可以将其互斥锁原理重现,用代码自己实现互斥锁。
import threading
import time
sum = 0
class MyLock(object):
```我的互斥锁```
def __init__(self):
self.lock = False
def inlock(self):
if self.lock == False:
self.lock = True
else:
while self.lock == True:
pass
def unlock(self):
self.lock = False
def islock(self):
return self.lock
lock1 = MyLock()
def go(num):
global sum
lock1.inlock()#
for tmp in range(num):
sum += 1
lock1.unlock()#
print(sum)
def go2(num):
global sum
lock1.inlock()#
for tmp in range(num):
sum += 1
lock1.unlock()#
print(sum)
def main():
t1 = threading.Thread(target=go,args=(100000000,))
t2 = threading.Thread(target=go,args=(100000000,))
t1.start()
t2.start()
time.sleep(2)
print(sum)
if __name__ == "__main__":
main()
互斥锁的思路:每一次锁住只有到其开锁才不会被阻塞住。阻塞用while循环判断锁的状态。
互斥锁嵌套使用要注意,如果是交叉锁(t1锁了A,嵌套锁B;t2锁了B,嵌套锁A),容易出现死锁
补充:
银行家算法
当资金小于多个用户的总贷款需求,就需要将资金灵活分配,而不是一次性都放出去,避免资金周转失灵。
4.协程
协程特点:
1)协程是属于线程的。协程程序是在线程里面跑的,因此协程又称微线程和纤程等。
2)协没有线程的上下文切换消耗。
3)由于协程是用户调度的,所以不会出现执行一半的代码片段被强制中断了,因此无需原子操作锁。
4.1迭代器
数组、字符串和元组等是可以通过for…in…语法来将每个元素读出来,这就是可迭代的。
使用方法判断一个对象是否可迭代:
from collections import Iterable
print(isInstance([1,2,3],Iterable))
普通的对象是不可迭代的:
from collections import Iterable
class MyData(object):
def __init__(self):
self.datas = [1,2,3]
data1 = MyData()
print(isInstance(data1,Iterable))
for i in data1:
print(i)
#输出
#False
#TypeError: 'MyData' object is not iterable
当给对象定义了方法__iter__
:
from collections.abc import Iterable
class MyData(object):
def __init__(self):
self.datas = [1,2,3]
def __iter__(self):
pass
data1 = MyData()
print(isinstance(data1,Iterable))
for i in data1:
print(i)
#输出
#True
#iter() returned non-iterator of type 'NoneType'
对象data1就变为可迭代对象,但是for…in…语法仍然报错,因为需要iter()返回的是一个迭代器
这里写一个类,并判断其对象是否为迭代器:
from collections import Iterator
class MyIterator(object):
def __iter__(self):
pass
def __next__(self):
pass
myiterator = MyIterator()
print(isinstance(myiterator,Iterator))
#True
一个对象是否为迭代器,就在于有没有写__iter__
和__next__
的运算符重载方法
将上面的结合并补充:
class MyIterator(object):
def __init__(self,item):
self.item = item
self.count = 0
def __iter__(self):
pass
def __next__(self):
if self.count<len(self.item.datas):
result = self.item.datas[self.count]
self.count += 1
return result
else:
raise StopIteration
class MyData(object):
def __init__(self):
self.datas = [1,2,3]
def __iter__(self):
return MyIterator(self)
data1 = MyData()
for i in data1:
print(i)
#输出
#1 2 3
我们可以从上面代码看出,当一个对象被迭代,首先会调用运算符重载函数__iter__
,这个函数会返回一个带有__next__
和__iter__
方法的对象,每一次迭代的结果都是__next__
函数返回的结果,当__next__
函数抛出一个StopIteration(停止迭代)的异常,迭代就停止。
这时可以将上面的再结合:
class MyData(object):
def __init__(self):
self.datas = [1,2,3]
def __iter__(self):
self.count = 0
return self
def __next__(self):
if self.count<len(self.datas):
result = self.datas[self.count]
self.count += 1
return result
else:
raise StopIteration
data1 = MyData()
for i in data1:
print(i)
#输出
#1 2 3
4.2生成器
当一个有规律的数组中,它是有规律的,那么可以记录规律而不记录数据,在数据体量非常庞大时这样做可以达到节省内存的目的。
那么生成器就是这样的作用,而与此同时,生成器又是特殊的迭代器。
创建生成器方法
1)
列表生成语法如下
arr = [i*2 for i in range(4)]
创建一个生成器如下
generator1 = (i*2 for i in range(4))
generator2 = (i for i in arr)
2)
在函数中写入yield,那么这个函数调用就返回一个生成器对象,函数就是生成器的模板。
例如:
def task():
print("--step1--")
yield
print("--step2--")
t1 = task() #输出--step1--
next(t1) #输出--step2--
调用函数就会返回一个生成器,函数会执行到yield然后挂起,yield后的代码会在返回的生成器被next函数调用后继续执行。
生成器模板
def task():
a = 1
print("step1,return a")
b = yield a
print("step2,get b and print,return a")
print(b)
yield a
t1 = task()
num = next(t1)
print(num)
num2 = t1.send(9)
#输出
#step1,return a
#1
#step2,get b and print,return a
#9
yield 后的变量会被next或send函数返回
send函数传送的值会被赋值到yield前的等值
4.3yield
yield实现伪多任务
如下例子:
import time
def ts1():
while True:
print("----task1----")
yield
time.sleep(0.5)
def ts2():
while True:
print("----task2----")
yield
time.sleep(0.5)
def main():
t1 = ts1()
t2 = ts2()
while True:
next(t1)
next(t2)
if __name__ == "__main__":
main()
4.4greenlet
greenlet是一个封装了yield语法的库,也可以实现伪多任务
from greenlet import greenlet
import time
def task1():
while True:
print("t1")
t2.switch()
time.sleep(0.5)
def task2():
while True:
print("t2")
t1.switch()
time.sleep(0.5)
t1 = greenlet(task1)
t2 = greenlet(task2)
t1.switch()
4.5gevent
gevent是网络的一个并发库,采用协程实现。
在前面讲到的例子都是伪多任务,只是将两个任务分别交叉运行,真多任务在一个延时的时候就应该切换到另一个任务。
延时的时候就应该切换到另一个任务,如下例子:
import gevent
def task(n):
for i in range(n):
print(gevent.getcurrent(),i)
gevent.sleep(0.5)
def task2(n):
for i in range(n):
print(gevent.getcurrent(),i)
gevent.sleep(0.5)
t1 = gevent.spawn(task,5)
t2 = gevent.spawn(task2,5)
t1.join()
t2.join()
#输出
<Greenlet at 0x249cd3fa3e0: task(5)> 0
<Greenlet at 0x249cf167100: task2(5)> 0
<Greenlet at 0x249cd3fa3e0: task(5)> 1
<Greenlet at 0x249cf167100: task2(5)> 1
<Greenlet at 0x249cd3fa3e0: task(5)> 2
<Greenlet at 0x249cf167100: task2(5)> 2
<Greenlet at 0x249cd3fa3e0: task(5)> 3
<Greenlet at 0x249cf167100: task2(5)> 3
<Greenlet at 0x249cd3fa3e0: task(5)> 4
<Greenlet at 0x249cf167100: task2(5)> 4
5.进程、线程和协程的比较
它们的切换速度:进程<线程<协程
当想要实现多任务可以使用协程,只有需求的时候会添加线程和进程
八、网络
1.socket
socket分为tcp和udp两种
tcp:
#发送端
from socket import *
tcp_s = socket(AF_INET,SOCK_STREAM)
tcp_s.connect(("127.0.0.1",7788))
tcp_s.send("你好啊".encode("utf-8"))
tcp_s.close()
#接收端
from socket import *
tcp_s = socket(AF_INET,SOCK_STREAM)
tcp_s.bind(("127.0.0.1",7788))
tcp_s.listen(128)
client = tcp_s.accept()[0]
print(client.recv(1024).decode())
client.close()
tcp_s.close()
udp:
#发送端
from socket import *
udp_s = socket(AF_INET,SOCK_DGRAM)
udp_s.sendto(b"hahahah",("127.0.0.1",7788))
udp_s.close()
#接收端
from socket import *
udp_s = socket(AF_INET,SOCK_DGRAM)
udp_s.bind(("127.0.0.1",7788))
re = udp_s.recvfrom(1024)
print("地址:",re[1])
print("接收信息:",re[0].decode())
udp_s.close()
2.http
简单的例子——基于tcp的http请求的响应服务:
from socket import *
def main():
tcp_s = socket(AF_INET,SOCK_STREAM)
service_port = 7788
service_addr = "127.0.0.1"
print("service running: %s %s"%(service_port,service_addr))
tcp_s.bind((service_addr,service_port))
tcp_s.listen(128)
while True:
client_socket,client_addr = tcp_s.accept()
service_response(client_socket)
tcp_s.close()
def service_response(client_socket):
request = client_socket.recv(1024)
print(request)
response = "HTTP/1.1 200 ok\r\n"
response += "\r\n"
response += "py service Welcome"
client_socket.send(response.encode("utf-8"))
client_socket.close()
if __name__ == "__main__":
main()
九、扩展
1.正则表达式re
匹配邮箱简单例子:
import re
email = input("please enter a 163 email address(4-20):")
ret = re.match(r"[a-zA-Z0-9]{4,20}@163\.com$",email)
if(ret):
print("format comfirm")
else:
print("format comfirm")
2.文件读取
简单地逐行读取:
arr1 = []
with open(FilePath, 'r', encoding='utf8') as f1:
for line in f1.readlines():
line = str(line).split()
arr1.extend(line)
f1.close()
3.OS模块
3.1执行cmd命令
import os
os.system("dir")
3.2获取当前文件路径
import os
os.getcwd