python快速入门

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)

    1. 整形(int)

      十进制、二进制(0b / 0B 开头)、八进制(0o / 0 / o0 开头)、十六进制(0x / 0X 开头)

    2. 浮点型(float)

      2.3E20 2.3e20

    3. 复数(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

不同进程之间的切换需要计算机经过以下步骤:

  1. 切换页目录以使用新的地址空间
  2. 切换内核栈
  3. 切换硬件上下文

3.线程threading

不同线程之间的切换需要计算机经过以下步骤:

  1. 切换内核栈
  2. 切换硬件上下文
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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值