Python基础

一.写在最前
二.开始吧
三.数据类型
四.结构控制
五.函数
六.模块和包
七.类
八.异常
九.文件操作
十.网络编程
十一.多任务
参考: http://www.runoob.com/python3/python3-errors-execptions.html

一.写在最前

1.解释性语言和编译型语言

编译型语言:由操作系统直接翻译成机器语言,跨平台能力差,运行速度快,比如C,C++
解释型语言:由解释器翻译成机器语言,跨平台能力强,针对不同平台使用不同解释器即可,运行速度稍慢,比如Js,Java,Phthon。

2.Python版本

2.x:不支持中文 2.7为最后一个2.x的版本,2.6,2.7为两个兼容性版本
3.x:支持中文,目前主流版本

二.开始吧

1.三种执行方式
  • 解释器执行:Python解释器执行 Python xx.py/Python3 xx.py
  • 交互式执行:终端输入Python,在>>>后面直接输入Python代码,使用exit()退出
  • IDE:PyCharm
2.PyCharm
  • 项目根目录下的.idea文件用于存放项目的相关信息,包括解释器版本,项目包含的文件等。
  • 设置项目配置
    解释器版本:Preference/Project/Project Interpreter
    字体字号:Preference/Editor
2.注释

# 单行注释 (command+/ 快速添加单行注释)
"""
多行注释
"""

3.运算符

** 乘方
// 整除
逻辑表达式:and,or ,not ->与,或,非
not in 元素不在集合中
in 元素在集合中

list1=[1,2,3,4]
if(1 in list1):
    print("in")
else:
    print("not in")

if("你" not in str):
    print("not in")
else:
    print("in")

is 是判断两个标识符是不是引用自一个对象(地址)
not is
== 是判断两个标识符值是否一样(值)

三.数据类型

1.变量类型

python定义变量不需要加数据类型,解释器会根据值进行判断
python中可变类型不能使用hash(),也就是不能输出变量唯一值(唯一性通过hash函数判断),也就不能作为字典key。

  • 标准数据类型
    Number(数字): int、float、bool、complex
    String(字符串)
    List(列表)
    Tuple(元组)
    Set(集合)
    Dictionary(字典)

其中
不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。
type() -查看变量数据类型

2.数字类型

Python3解析器中int类型可以计算的范围更大

int()  #类型转换int
float() #类型转换float
3.String

1.字符串是 Python 中最常用的数据类型。我们可以使用引号( ' 或 " )来创建字符串。
2.与 C 字符串不同的是,Python 字符串不能被改变。向一个索引位置赋值,比如str[0] = "m"会导致错误。
3.反斜杠可以用来转义,使用r可以让反斜杠不发生转义。
4.字符串可以用+运算符连接在一起,用*运算符重复。
5 .Python中的字符串有两种索引方式,从左往右以0开始,从右往左以-1开始。[start:end:step]用来截取
其中:[start:end:step] 包含开始,默认0;不包含结束,默认最后;step默认1(从左向右选)-1(从右向左选)
最后一个元素下标为-1,如果start->end和step方向不一致,则无法选取。
6.可使用三引号多行格式化字符串

str = "宙斯YY"
print(str)  # 输出字符串  宙斯YY
print(str[0:-1])  # 输出第一个到倒数第二个的所有字符  宙斯Y
print(str[0])  # 输出字符串第一个字符 宙
print(str[2:3])  # 输出从第三个开始到第四个的字符 Y
print(str[2:])  # 输出从第三个开始的后的所有字符  YY
print(str[::2])  # 每隔一个去一个字符  宙Y
print(str[::-1]) # 反向取字符串(逆序字符串) YY斯宙
print(str * 2)  # 输出字符串两次  宙斯YY宙斯YY
print(str + "TEST")  # 连接字符串  宙斯YYTEST
print("宙斯\nYY")  #转义 
输出结果:
宙斯
YY                   
print(r"宙斯\nYY")  #不接受转义 
输出结果:
宙斯\nYY
str = "宙斯YY"
print(str[0],str[3])   #宙 Y
print(str[-1],str[-3])  #Y 斯
  • 格式化输出
count1=2
print("不够3位自动补0:%03d"%count1)
count2=123456789
print("够3位,正常输出:%03d"%count2)

fct1=2.3
fct2=2.29
print("保留3位小数(自动补0):%.3f 保留1位小数(四舍五入):%.1f"%(fct1,fct2))

tips1="输出字符串"
tips2="拼接字符串"
print("money:%s-%s"%(tips1,tips2))
print("输出%%:%f%%"%fct1)

输出结果:
不够3位自动补0:002
够3位,正常输出:123456789
保留3位小数(自动补0):2.300 保留1位小数(四舍五入):2.3
money:输出字符串-拼接字符串
输出%:2.300000%

str='qw想问"你"ooq'
print(str)

#常用方法
print(str.index("oo"))
print(str.count("q"))
print(str.find("sda"))

#三引号定义多行字符串
str2="""i am not
a
student
but i am a
child
"""
print(str2)
4.List
list1 = ["Z","S","Y",2018]
#查看列表或者部分列表
print("list1[0]:",list1[0])
print("list[1-3]:",list1[1:3])

#增加列表值
list1.append("zsy")
print("list:",list1)

#修改列表值
list1[0]="z"
print("list",list1)

#删除列表元素 del or remove
del list1[0]
list1.remove("S")
print("list:",list1)

#常用函数
print(len(list1))
print(list1.index(2018))
list1.clear()
print("list:",list1)
list1=[1,2,3,4,5,1,2]
print(list1.count(1))  #统计元素在列表中的个数
list1.sort(reverse=True) #排序反转,默认不反转
print(list1)
list1.reverse()
print(list1)

输出:
list1[0]: Z
list[1-3]: ['S', 'Y']
list: ['Z', 'S', 'Y', 2018, 'zsy']
list ['z', 'S', 'Y', 2018, 'zsy']
list: ['Y', 2018, 'zsy']
3
1
list: []
2
[5, 4, 3, 2, 2, 1, 1]
[1, 1, 2, 2, 3, 4, 5]

5.元组(Tuple)

1.Python 的元组与列表类似,不同之处在于元组的元素不能修改。
2.元组使用小括号,列表使用方括号。
3.元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。
4.应用场景:
作为函数的返回值和参数;
格式化字符串 eg:print("%f%d%s"%(浮点数,整型,字符串))
保护List安全,List转Tuple eg: tuple(列表)

#创建以及查看元组

tup = ("Z","S","Y",2018)
print(type(tup))
tup2="a","b","c"  #可以不加()
print(tup2[2])
tup3=(1,)   #元组中只包含一个元素时,需要在元素后面添加逗号,否则括号会被当作运算符使用:
print(type(tup3))

#增加元组值(不可变,所以不可增加,只能连接)

newtup=tup+tup2
print(newtup)

#修改元素(不可变,不可修改)

#删除元素(不可变,不可删除)
del tup  #这样属于删除变量tup
print(tup)  #删除之后访问不到tup

#常用方法
print(tup.index("S")) #S在元组中的位置
print(tup.count(2018)) #统计2018出现的次数
print(len(tup))
6.字典

1.字典是另一种可变容器模型,且可存储任意类型对象。
2.字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中。
3.键必须是唯一的,但值则不必。值可以取任何数据类型,但键必须是不可变的,如字符串,数字或元组。

#创建字典和查看
dict={"key1":"zsy","key2":2018}
print(dict["key1"])  #如果key1没有报错
print(dict.get("key1")) #如果key1没有输出None

#添加元素/修改元素
dict["key3"]="new year"
print(dict)
dict["key1"]="zhousy"
print(dict)

#遍历字典
for key in dict:
    print("%s:%s"%(key,dict[key]))

#删除元素
del dict["key1"]
dict.pop("key2")
print(dict)
dict.clear()
容器类

String,Set,List,字典,元组都是容器类

可以使用公共的方法:

  • del()
  • min()
  • max()
  • [start:end:step]切片方法(字典不可以)
  • range(start,end,step)类似切片
  • +/* 运算符 增加容器类元素(这种方法会生成新的变量,而不是向已有容器类添加新元素)
  • in/not in 成员运算符
  • 推导式:
    列表 [xx for xx in range()] 字典 {xx:xx for .. in ..} 集合 {xx for xx in ..}

四.结构控制

0.运算符

1.Python 中使用 and or not 表示逻辑运算符
2.Python 中没有 ++ -- 运算符,使用+=1 -=1替代

1.选择结构

1.Python 中用 elif 代替了 else if,所以if语句的关键字为:if – elif – else。
2.Python 中使用缩进格式匹配代码块,而不是{}匹配,if匹配的代码块是缩进之后的代码块
3.每个条件后面要使用冒号 :,表示接下来是满足条件后要执行的语句块。
4.在Python中没有switch – case语句。

num = int(input("输入一个数字:"))
if (num % 2 == 0):
    if (num % 3 == 0):
        print("%d可以被2和3整除" % num)
    elif (num % 5 == 0):
        print("%d可以被2和5整除" % num)
    else:
        #else匹配的代码块包含这两个print输出
        print("%d不可以被3和5整除" % num)  
        print("%d可以被2整除" % num)
else:
    print("%d不能被2整除" % num)

print("不缩进,和谁都不对齐,那你每次都来吧")
2.循环结构

1.Python中的循环语句有 for 和 while,没有do-while。
2.while,for 后面要使用冒号 :,表示接下来是满足条件后要执行的语句块。
3.while-else语句,当不满足while条件时,执行else代码块
4.range()函数可以控制循环的开始方式以及执行次数
5.continue,break可以正常使用
6.for-else,当for循环执行完毕,执行else代码块(for循环内不使用break,否则跳出整个for-else循环)

count=5
while(count>0):
    print("循环内:count:%d"%count)
    count-=1
else:
    print("不满足循环条件:count:%d"%count)

输出:
循环内:count:5
循环内:count:4
循环内:count:3
循环内:count:2
循环内:count:1
不满足循环条件:count:0

for i in range(1,3):  
    print(i)

输出:
1
2

for i in range(3): #相当于range(0,3)
    print(i)

输出:
0
1
2

for i in range(0,10,3):
    print(i)

输出:
0
3
6
9

for x in ["A","B","C"]:
    print(x)

输出:
A
B
C

for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            break
    else:
        # 循环中没有找到元素
        print(n, ' 是质数')

print("---------------")
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            break
        # 循环中没有找到元素
    if(n-1==x or n==2):
        print(n, ' 是质数')

输出:
2 是质数
3 是质数
5 是质数
7 是质数
---------------
2 是质数
3 是质数
5 是质数
7 是质数

五.函数

1.函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
2.任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
3.函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
4.函数内容以冒号起始,并且缩进。
5.return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
6.函数参数类型可以是:必需参数,关键字参数,默认参数,不定长参数
7.因为python没有变量定义和使用的区分,所以同名局部变量和全局变量其实是两个变量,除非用global声明。

可更改(mutable)与不可更改(immutable)对象
在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。
所谓可变不可变,是指修改之后是否建立在原数据基础上。
不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变a的值,相当于新生成了a。
可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

a=1
b=a  
print(id(a),id(b),a,b)
a=2  #number是不可变类型,修改a的值是原数据1不改变,重新创建一个给2给a用
print(id(a),id(b),a,b)

a=['a','b']
b=a
print(id(a),id(b),a,b)
a.append('c') #list是可变类型,修改a的值是在原数据中添加c
print(id(a),id(b),a,b)

def showParam(p):
    print(p,id(p))  #原来数据和地址
    p+=p
    print(p,id(p))  #修改后数据和地址

a=1
b=['a','b']
showParam(a)
showParam(b)

python 函数的参数传递:
不可变类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。
可变类型:类似 c++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响
python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

def changenum(num):
    #这里表示函数说明,Pycharm可以自动帮补全,调用时查看(F1)会显示
    """
    改变数据
    :param num: 数字
    """  
    num = 10

def changeDict(dict):
    dict.append("c")

num = 2
print(num)
changenum(num)
print(num)

dict = ["a", "b"]
print(dict)
changeDict(dict)
print(dict)

def changenum(n):
    print("传递参数n地址:%s"%id(n))
    n=[5,6]   #n=[5,6]相当于重新定义变量n,而不是改变原来的num
    print("n赋值之后地址:%s"%id(n))

num=[1,2,3]
print(num)
changenum(num)
print(num)

def changenum(n):
    n+=[5,6]  #+=相当于调用extend

num=[1,2,3]
print(num)
changenum(num)
print(num)

输出:
2
2
['a', 'b']
['a', 'b', 'c']

[1, 2, 3]
传递参数n地址:4519264072
n赋值之后地址:4520233032
[1, 2, 3]

[1, 2, 3]
[1, 2, 3, 5, 6]

def argTest(param1, param2, param3="Z"):
    print(param1 + param2 + param3)

#必须参数方式
argTest("A", "+", "D")

#关键字参数方式,可以改变传递顺序
argTest(param2="+", param3="D", param1="A")

#使用params3="Z" 这个默认参数,写在末尾
argTest("a", "c")
argTest(param1="a", param2="+")

#使用不定长参数
def test(*params):  # *params代表接受把不定长参数作为元组
    print(params)
    res=0
    for param in params:
        res+=param
    print(res)
def test2(**params):  # **params代表接受把不定长参数作为字典
    for key in params:
        print("key:%s value:%s"%(key,params[key]))

#*params代调用方式1
test(1,2,34)
#*params代调用方式2(拆包语法)
tup=(3,4,5)
test(*tup)

#**params代调用方式1
test2(key1="abc",key2="def",key3="hgj")
#**params代调用方式2(拆包语法)
dict={"k":123,"k2":321}
test2(**dict)

输出:
A+D
A+D
acZ
a+Z

(1, 2, 34)
37
(3, 4, 5)
12
key:key1 value:abc
key:key2 value:def
key:key3 value:hgj
key:k value:123
key:k2 value:321

全局变量:

num=10
def test1():
    num=100
    print(num)

test1()  #输出100
print(num)  #输出10

num=10
def test1():
    global num #声明num为全局变量
    num=100
    print(num)

test1()  #输出100
print(num)  #输出100

匿名函数:

#匿名函数 lambda:简化一行代码一个返回值的函数写法

def sum(a,b):
    return a+b
#等价
s=lambda a,b:a+b
print(s(1,2))

stu=[{"name":"A","age":18},
     {"name":"C","age":38},
     {"name":"B","age":28}]

#sort函数把lambda函数作为参数
stu.sort(key=lambda x:x["name"])
print(stu)
stu.sort(key=lambda x:x["age"],reverse=True)
print(stu)

高阶函数:

#函数作为参数
def sum(a,b,func):
    return func(a)+func(b)

print(sum(-2,3,abs))
print(sum(-2.1,3.2,round))

#map(func,lst)-(函数,序列) 每一个序列元素进行函数运算

def func(x):
    return x**2
numberList=[1,2,3,4]
res=map(func,numberList)
print(list(res))

#reduce(func,lst)-(函数,序列) 每个序列元素和后一个元素进行函数累计运算
import functools
def func2(a,b):
    return a+b
numberList2=[1,2,3,4]
res2=functools.reduce(func2,numberList2)
print(res2)

#filter(func,lst)-(函数,序列) 每一个序列元素进行函数过滤运算
def func3(x):
    return x%2==0
numberList3=[1,2,3,4]
res3=filter(func3,numberList3)
print(list(res3))

深浅拷贝:

import copy

#不可变数据的浅拷贝(copy)和深拷贝(deepcopy)
str="abc"
str2=str
str3=copy.copy(str)
str4=copy.deepcopy(str)
#字符串地址信息
print("(%s-%s-%s-%s)"%(id(str),id(str2),id(str3),id(str4)))
a=(1,2)
b=a
c=copy.copy(a)
d=copy.deepcopy(a)
#元组地址信息
print("(%s-%s-%s-%s)"%(id(a),id(b),id(c),id(d)))


#可变数据的浅拷贝(copy)和深拷贝(deepcopy)
temp=[4,5]
list1=[1,2,3,temp]
#赋值地址不变
list2=list1
#copy地址信息改变
list3=copy.copy(list1)
#相当于copy
list4=list1[:]
#deepcopy地址信息改变,且内部数据也被拷贝
list5=copy.deepcopy(list1)
temp.append(6)
list1.append(7)
#copy-拷贝一个list1的指向,但是list1的元素仍然指向copy当时的原list1中元素
#deepcopy-拷贝一个list1的指向并把数据完全拷贝(与原数据地址不同)
print("(%s-%s-%s-%s-%s)"%(id(list1),id(list2),id(list3),id(list4),id(list5)))
print(list1,list2,list3,list4,list5)

六.模块和包

  • 模块

1.每一个.py文件都是一个自定义模块
2.使用import 可以导入文件,也可以使用from..import导入具体变量,函数,类。
3.导入模块优先查找项目目录,再查找系统目录。
4.配合name保证可执行代码不被执行

#File:Helloworld.py
def helloworld():
    print("hello world")
class Person():
    def show(self):
        print("Person")

直接导入方式import

#File:Test.py
import Helloworld
#模块名.属性方法名调用
Helloworld.helloworld()  #hello world
Helloworld.Person().show()  #Person

导入部分模块 from import

#File:Test2.py
from Helloworld import helloworld
from Helloworld import Person
#from Helloworld import * # 可以使用*导入文件所有模块
#不需要模块名.直接属性方法名调用
helloworld()
Person().show()

指定别名

import Helloworld as PersonClass  #Helloworld指定PersonClass别名
from Helloworld  import helloworld as hw #helloworld指定hw别名
#别名.属性方法调用 or 别名属性方法调用
PersonClass.helloworld()  #hello world
PersonClass.Person().show()  #Person
hw()

注:如果不同文件有同名函数或者类,后面导入的会覆盖之前的导入的函数或者类

“”“
Test.py 导入Helloworld.py模块
运行Test.py只会输出*************************,而不会输出"文件中可执行代码"
运行Helloworld.py,可以输出"文件中可执行代码"
为了确保可执行代码可以正常执行,且被其他模块引用不会执行。
”“”
#File:Helloworld.py
if(__name__=="__main__"):
    print("文件中可执行代码")

#File:Test.py
from Helloworld import *
print("*"*50)

注意事项:
如果使用import导入

File:module1.py
#公有变量
GProperty=1
GList=[1,2,3]

File:module2.py
import module1

def shoowGP():
    print(module1.GProperty)

def showGL():
    print(module1.GList)

File:TestModule.py
import module1
import module2

module1.GProperty=2  
module1.GList.append(4)
print(module1.GProperty)  #2
print(module1.GList)  #[1,2,3,4]

module2.shoowGP() #2
module2.showGL() #[1,2,3,4]

如果使用from import导入

from module1 import *
import module2
#相当于再次定义了一个GProperty的变量,不会修改module1中GProperty的值
GProperty=2
#不赋值,则改变仍然是module1中GList的值
GList.append(4)
print(GProperty) #2
print(GList) #[1,2,3,4]

module2.shoowGP() #1
module2.showGL() #[1,2,3,4]

all列表

#定义在all列表的中的方法才能被外部使用from mymodule import *调用方式调用
__all__=["testA"]

def testA():
    print("testA")

def testB():
    print("testB")

包就是多个模块的集合,包含一个__init__.py的宏文件,包含包中的模块,使用时直接import包中具体模块就行

myPackage.file:
    __init__.py:
        __all__ =["myModule1"]
    myModule1.py:
        def show1():
            print("show1")
    myModule2.py:
        def show2():
            print("show2")

#import方式
import myPackage.myModule1
import myPackage.myModule2
#包名.模块名.方法属性名调用
myPackage.myModule1.show1()
myPackage.myModule2.show2()

#from import * 方式
from myPackage import *
#模块名.方法属性名调用
myModule1.show1()
#使用from import * 的方式导入依赖all列表中允许导入模块的权限
# myModule2.show2()  #不能导入

七.类

1.基本类的定义和使用

不使用构造函数的情况

class ClassName:

    publicProperty="publicProperty"  #公有属性
    __privateProperty="__privateProperty"  #私有属性

    def publicMethod(self):  #公有方法
        print("publicMethod")
    def __privateMethod(self):  #私有方法
        print("__privateMethod")


test=ClassName()
print(test.publicProperty)  #访问公有属性
test.publicMethod()   #访问公有方法
print(test._ClassName__privateProperty)  #访问私有属性(利用解释器规则)
test._ClassName__privateMethod()    #访问私有方法(利用解释器规则)

使用构造函数的情况

class Person:
    def __init__(self,name,age):
        self.name=name  #在init中定义成员属性  公有属性
        self.__age=age  #__age为私有属性,子类无法访问
    def walk(self):
        self.__run()
        print(self.name,"walk")

    def __run(self):
        print(self.name,"run")  #对象和子类无法访问,内部使用

#习惯使用set/get修改私有属性的值
    def setAge(self,age):
        self.__age=age
    def getAge(self):
        return self.__age

person=Person("zz",18)
person.walk()
person.setAge(20)
print(person.getAge())
# person.__run() #无法访问

2.类中已定义方法和属性

属性

  • __class__ : 对象所属类
  • __module__ : 对象所属模块
    方法
  • __init__ : 构造函数,在生成对象时调用
  • __del__: 析构函数,释放对象时使用
  • __repr__ : 打印,转换
  • __setitem__ : 按照索引赋值
  • __getitem__: 按照索引获取值
  • __len__: 获得长度
  • __cmp__: 比较运算
  • __call__: 函数调用
  • __add__: 加运算
  • __sub__: 减运算
  • __mul__: 乘运算
  • __truediv__: 除运算
  • __mod__: 求余运算
  • __pow__: 乘方
class Person:
    def __init__(self):
        print("Person初始化")
    # 重写init
    # def __init__(self,age,name):
    #     self.age=age
    #     self.name=name
    def __del__(self):
        print("Person析构")

    def __str__(self):
        return "Person对象"

    def __cmp__(self, other):
        print("对象比较")

3.继承

1.Python 同样支持类的继承,包括多继承。
2.子类继承可使用父类所有公有属性方法(包括系统__方法(比如init),如果同名,按照优先级调用,具体规则是3中所写)
3.需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。(MRO查找法-子类-继承父类左到右,继承父类左到右...object基类)

class Person():   #定义父类
    __idcard=140108  #子类不能访问父类私有属性和私有方法
    def __sayId(self):
        print(self.__idcard)
    def sayName(self):   #子类可以通过父类公有方法间接访问私有属性和私有方法
        print("Person sayName%d"%self.__idcard)


class Student(Person):  #定义子类
    def sayGrade(self):
        print("Student sayGrade")
    #重写父类方法
    def sayName(self):
        #super().sayName()   #super()调用父类方法
        #Person.sayName(self)   #父类名调用父类方法
        print("Student sayName")

student=Student()
student.sayName()
student.sayGrade()
class Human():
    def sayTest(self):
        print("Human test")

class Person(Human):   #定义父类
    def sayTest(self):
       print("Person test")

class Booker():
    def sayTest(self):
        print("Booker test")

class Student(Booker,Person):  #定义子类
    pass

student=Student()
student.sayTest()
print(Student.__mro__)  #MRO 调用方法查找方式
print(dir(student))  #查看student方法和属性(包含父类)

输出:
Booker test
(<class 'main.Student'>, <class 'main.Booker'>, <class 'main.Person'>, <class 'main.Human'>, <class 'object'>)
['class', 'delattr', 'dict', 'dir', 'doc', 'eq', 'format', 'ge', 'getattribute', 'gt', 'hash', 'init', 'init_subclass', 'le', 'lt', 'module', 'ne', 'new', 'reduce', 'reduce_ex', 'repr', 'setattr', 'sizeof', 'str', 'subclasshook', 'weakref', 'sayTest']

多继承中子类调用父类

"""
类名.__init__方式调用父类,可能会导致顶级父类会调用多次init
"""
class Person():
    def __init__(self):
        print("Person init")

class Man(Person):
    def __init__(self,name):
        print("Man init")
        Person.__init__(self)
        self.name=name

class Woman(Person):
    def __init__(self,age):
        print("Woman init")
        Person.__init__(self)
        self.age=age

class LadyBoy(Man,Woman):
    def __init__(self,name,age):
        print("LadyBoy init")
        Man.__init__(self,name)
        Woman.__init__(self,age)

lb=LadyBoy("heihei",18)
print(lb.age,lb.name) 

LadyBoy init
Man init
Person init
Woman init
Person init
18 heihei

"""
super()的使用,会按照MRO顺序调用,顶级父类init只调用一次
使用*args,**kwargs可变参数定义init,否则super.init不知道找哪个方法
"""
class Person():
    def __init__(self,*args,**kwargs):
        print("Person init")

class Man(Person):
    def __init__(self,name,*args,**kwargs):
        print("Man init")
        #调用LadyBoy.__mro__当前类(Man)后面类(Woman)init方法
        super().__init__(*args,**kwargs)
        self.name=name


class Woman(Person):
    def __init__(self,age,*args,**kwargs):
        print("Woman init")
       #调用LadyBoy.__mro__当前类(Woman)后面类(Person)init方法
        super().__init__(*args,**kwargs)
        self.age=age


class LadyBoy(Man,Woman):
    def __init__(self,name,age):
        print("LadyBoy init")
        #调用LadyBoy.__mro__当前类(LadyBoy)后面的类(Man)init方法
        super().__init__(name,age)


lb2=LadyBoy("hxi",19)
print(lb2.age,lb2.name)

LadyBoy init
Man init
Woman init
Person init
19 hxi

4.多态
class Dog():
    def work(self):
        print("dog work")

class ArmyDog(Dog):
    def work(self):
        print("army dog work")

class DrugDog(Dog):
    def work(self):
        print("drug dog work")

class Person():
    def callDog(self,dog):
        dog.work()

d=Dog()
ad=ArmyDog()
dd=DrugDog()
person=Person()
#多态:传入子类对象,调用子类方法
person.callDog(d)
person.callDog(ad)
person.callDog(dd)
5.类属性方法和实例属性方法,静态方法

1.类属性和实例属性在使用方式上进行区分,调用者是类,那么这个时候就是类属性,调用者是对象,那么就是实例属性。也因此在定义的时候没办法区分,如果定义一个,但使用类.属性和对象.属性两种方式使用,其实生成了两个同名属性。
2.类方法和静态方法定义方式不同,一般使用类属性的方法定义为类方法,既不使用类属性也不使用实例属性,可以定义为静态方法,都使用的定义为实例方法。

在类中有三种方式定义属性

  • 第一种(不推荐)
class Dog:
    pass

dog=Dog()
#name直接绑定到dog实例属性
dog.name="xiaoxiao"
#age直接绑定到Dog类属性
Dog.age=19
print(Dog.age)
print(dog.name)
  • 第二种(可用来绑定实例属性)
class Booker:
    def __init__(self,name):
        self.name=name  #实例属性,因为使用self.绑定

booker=Booker("aa")
print(booker.name)
#print(Booker.name) # 报错
  • 第三种(可以定义类属性)
class Person:
    count=100 #这里定义并不确定是类属性还是实例属性,如果出现同名,优先作为类属性

p=Person()
print(Person.count)  #100  -作为类属性输出
print(p.count)   #100  -作为实例属性输出
p.count=2  #相当于用第一种方式绑定了一个新的count实例属性
print(id(p.count))
print(id(Person.count))
print(Person.count)  #100
print(p.count)   #2

类方法和静态方法

class Person:
    count=0
    @classmethod  #类方法定义
    def showCount(cls):
        print(cls.count)

    @staticmethod  #静态方法定义
    def showHi():
        print("Hi")
    def __init__(self):
        Person.count+=1

p=Person()
p2=Person()
p3=Person()
Person.showCount() #类方法调用
Person.showHi()  #静态方法调用

property关键字的使用:

把无参方法转成属性进行使用,方法返回值就是属性的值,同时可以通过property-setter方式给该属性赋值

#方式一:@property装饰器
class Person():
    def __init__(self,pagesize):
        self.__pagesize=pagesize

    #getter,返回私有属性__pagesize=
    @property
    def pagesize(self):
        return self.__pagesize

    #setter,通过setter方法设置赋值属性__pagesize=,并做处理
    @pagesize.setter
    def pagesize(self,value):
        if not isinstance(value,int):
            print("请输入int类型数据")
        self.__pagesize=value


person=Person(1)
print(person.pagesize)  #1
person.pagesize=2
print(person.pagesize)  #2

#方式二:property(getter,setter)
class Student():
    def __init__(self,pagesize):
        self.__pagesize=pagesize

    def getPagesize(self):
        return self.__pagesize

    def setPagesize(self,value):
        if not isinstance(value,int):
            print("请输入int类型数据")
        self.__pagesize=value

    pagesize=property(getPagesize,setPagesize)

student=Student(10)
print(student.pagesize)  #10
student.pagesize=20
print(student.pagesize)  #20
6.单例模式
class Person:
    instance=None
    init_flag=False
    def __new__(cls, *args, **kwargs):
        if(cls.instance is None):
            cls.instance=super().__new__(cls)
            return cls.instance
        else:
            return cls.instance

    def __init__(self):
        if(self.init_flag==False):
            #初始化
            return
        self.init_flag=True


p1=Person()
p2=Person()
print(id(p1))
print(id(p2))

八.异常

try:
    1/0
except ZeroDivisionError:  #知道异常类型捕获异常
    print("除数不能为0")

try:
    1/0
except:   #笼统处理异常信息
    print("异常")

try:
    1 / 0
except Exception as e:  # 未知异常信息捕获异常
        print("异常%s"%e)

try:
    raise Exception("抛出异常") #主动抛出异常给调用者
except Exception as e:
    print("异常:%s"%e)


"""完整异常结构"""
try:
    int(input("输入一个数字"))
except Exception as res:
    print("异常数字%s"%res)
else:
    print("无异常数字")
finally:
    print("无论是否异常都执行的代码")

"""自定义异常"""
#继承Exception类
class MyException(Exception):
    def __init__(self,size):
        self.size=size

    def __str__(self):
        return "我的异常 %s"%self.size

try:
    raise MyException(10)
except Exception as ex:
    print(ex)

除了finally可以处理清理行为,还有预定义的清理行为
关键词 with 语句就可以保证诸如文件之类的对象在使用完之后一定会正确的执行他的清理方法:

with open("myfile.txt") as f:
    for line in f:
        print(line, end="")

以上这段代码执行完毕后,就算在处理过程中出问题了,文件 f 总是会关闭(不需要file.close())。

九.文件操作

  • 打开文件

open(filename, mode)
filename:包含了你要访问的文件名称的字符串值。
mode:决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读(r)。

不同模式打开文件的完全列表:
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。


  • f.read() #读取所有内容返回
    f.readline() #读取一行内容返回(一般用于大文件)
    f.readlines() #读取所有内容,按行返回数组
file=open("readme","r")
while True:
    content=file.readline()
    if(not content):
        break
    else:
        print(content)

file.close()

  • f.write()

  • 关闭
    f.close()

  • 文件指针
    f.tell() #返回文件对象当前所处的位置, 它是从文件开头开始算起的字节数。
    f.seek() #改变文件指针
    seek(x,0) : 从起始位置即文件首行首字符开始移动 x 个字符
    seek(x,1) : 表示从当前位置往后移动x个字符
    seek(-x,2):表示从文件的结尾往前移动x个字符

  • os模块包含文件目录的操作

import os

os.mkdir() #创建目录
os.listdir() #查看目录
os.rmdir() #删除目录
os.path.isdir() #判断是不是目录

十.网络编程

使用socket模块处理网络编程

UDP发收数据:
1.要求传输的数据是二进制
2.发送之后,接收方需要主动接收才能获取数据,如果不调用接收方法,可能出现丢包(类似取快递)。
3.不建立链接通道,可能发送的消息被其他Port中断(快递丢了)。因为没有通道,所以每次发送消息都需要指定地址,而接收到的
消息也都有发送端的地址。
4.接收消息为阻塞方法,如果没有收到信息,则发生代码阻塞。

发送端代码:

import socket

def sendInfo(udpSocket):
    sendStr = input("请输出发送内容")
    #传输的数据格式需要编码成为二进制字节
    sendData = sendStr.encode("utf-8")
    # 指定接收端的IP和Port
    udpSocket.sendto(data=sendData, address=("192.168.1.35", 8899))

def main():
    udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    while True:
       sendInfo(udp_socket)
    udp_socket.close()

if __name__ == '__main__':
    main()

接收方代码:

import socket

def receiveInfo(udpSocket):
    #该方法为阻塞方法,指定接收最大字节1024
    recv_data = udpSocket.recvfrom(1024)
    #接收到元组数据,0-二进制数据 1-地址
    send_Data = recv_data[0]
    send_Addr = recv_data[1]
    print("接收到地址:%s 接收到数据:%s" % (send_Addr, send_Data.decode("utf-8")))

def main():
    udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    #绑定socket接收Port
    udp_socket.bind(("",8899))
    #不点击1接收消息的时候,收不到来自发送方的消息
    while True:
        print("-"*50)
        print("1.接收消息")
        print("0.退出系统")
        op=input("输入操作序号:")
        if(op=="1"):
            receiveInfo(udp_socket)
        elif(op=="0"):
            break
        else:
            print("输入有误")
    udp_socket.close()

if __name__ == '__main__':
    main()

TCP发收数据:
1.要求传输的数据是二进制
2.严格区分服务器端和客户端,稳定链接(客户端类似个人手机,服务端类似10086,每次建立通话后,10086会安排一个客服专
门处理个人业务)。
3.建立链接通道,所以只需要确认连接之后,接发消息就不需要再去获取和设置地址信息了。
4.socket接收客户端和客户端socket接收消息为阻塞方法。

服务器端:

def tcpmain():
    tcp_socket=socket.socket(
        socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket.bind(("127.0.0.1",7799))
    #设置最大连接数,超过后排队
    tcp_socket.listen(5)
    #循环监听所有客户端的连接请求
    while True:
        #该方法为阻塞方法,当有客户端连接执行,有客户端连接过来会产生一个客户端Socket为客服端服务
        clientSocket,clientAddr=tcp_socket.accept()
        print("客户端地址信息%s"%str(clientAddr))
        #循环为每个客户端处理请求
        while True:
            #该方法为阻塞方法,当收到客户发送消息执行
            recv_Data=clientSocket.recv(1024)
            print("接收到客户端数据:%s",str(recv_Data))
            #客户端收到数据,服务端给客户端发送一个消息
            if recv_Data:
                msg="客户端连接到服务器"
                #这个消息客户端在单线程环境下收不到(需单开一个线程收服务器消息)
                clientSocket.send(msg.encode("utf-8"))
            #客户端断开连接,跳出此次循环
            else:
                break
        clientSocket.close()
    tcp_socket.close()

if __name__ == '__main__':
    tcpmain()

客户端:

def tcpSendInfo(socket,inputStr):
    socket.send(inputStr.encode("utf-8"))

def tcpReceiveInfo(socket):
    recv_data = socket.recv(1024)
    recv_str = recv_data.decode("utf-8")
    print("tcp接收信息:%s" % recv_str)


def tcpmain():
    tcp_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    host="127.0.0.1"
    port = 7799
    #向服务器发起连接请求
    tcp_socket.connect((host,port))
    while True:
        info=input("发送信息:")
        if(info=="q"):
            exit()
        #向服务器发送消息
        tcpSendInfo(tcp_socket,info)
    tcp_socket.close()

if __name__ == '__main__':
    tcpmain()

十一.多任务

1.多任务计算机执行方式分为并发和并行。并发是指单核CPU交替分时执行多任务;并行是指多核CPU分别执行多任务,并发是多线程假象,并行才是真正多线程。
2.Python中多任务可以使用多进程,多线程,协程三种方式。

1.多进程

使用multiprocessing模块处理多进程

1.使用multiprocessing.Process()创建多进程
2.可以使用进程池multiprocessing.Pool()管理进程
3.进程之间不共享全局属性(资源复制导致)
4.主进程默认等待子进程执行完毕才结束,对于长时间子进程强制杀死,可以通过子进程依赖主进程守护进程和杀死子进程来控制。

import multiprocessing
import os
import time

def sing():
    #打印进程
    print("sing %s"%os.getpid())

def dance():
    for i in range(100):
        print("dance")
        time.sleep(1)

def main():
    #创建进程并执行,可以通过命令行进行查看
    multiprocessing.Process(target=sing).start()
    multiprocessing.Process(target=dance).start()

def poolmain():
    #创建进程池,最大包括3个进程
    pool=multiprocessing.Pool(3)
    for i in range(10):
        #执行进程
        pool.apply_async(sing)
    #关闭进程池
    pool.close()
    #等待所有进程池中进程执行完毕
    pool.join()
    print("所有进程执行完毕")

if __name__ == '__main__':
    #进程基本使用
    #main()
    #进程池基本使用
    poolmain()

进程之间不共享全局变量

import multiprocessing
import time

num=5
#子进程1递加数据
def addData():
    global num
    for i in range(10):
        num+=1
        time.sleep(0.3)
        print("子进程1中的num=", num)

#子进程2读取数据
def readData():
    global num
    print("子进程2中的num=", num)

#加main函数入口是因为阻止子进程复制创建子进程方法无限递归
if __name__ == '__main__':
    """
    在主进程中创建一个子进程,相当于把主进程中所有资源复制一遍到子进程中。
    所以,资源(属性)本身不共享,每个进程都有一个同名num变量
    main中的资源不复制。
    """
    addPro = multiprocessing.Process(target=addData)
    readPro = multiprocessing.Process(target=readData)
    addPro.start()
    # 当前进程(主进程)等待addPro进程执行完毕,再向下执行
    addPro.join()
    readPro.start()
    # 当前进程(主进程)等待readPro进程执行完毕,再向下执行
    readPro.join()
    print("主进程中的num=", num)

子进程销毁的方式

import multiprocessing
import time

def subDo():
    for i in range(10):
        print("子进程do")
        time.sleep(0.3)

if __name__ == '__main__':
    subPro=multiprocessing.Process(target=subDo)
    # 1.设置子进程为当前主进程的守护进程,随着主进程死亡而死亡
    # subPro.daemon=True
    subPro.start()
    time.sleep(1)
    # 2.在主进程快结束时,手动杀死子进程
    # subPro.terminate()
    print("主进程结束")

2.多线程

使用threading模块处理多线程
thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 "_thread"。

1.可以通过threading.Thread快速创建线程,也可以通过继承threading.Thread创建线程
2.主进程默认开启一个主线程,进程可以开启多个线程。
3.线程之间共享全局属性(共用进程资源),只不过有线程安全问题。
4.主线程默认等待子线程执行完毕才结束,对于长时间子线程强制杀死,可以通过子线程依赖主线程守护线程来控制。

基本使用

"""创建线程"""
def creatThread():
    # 直接使用Thread类创建线程,并指定任务和参数
    t1 = threading.Thread(target=sing, args=("zsy",))
    t1.start()
    # 继承Thread类创建线程,调用start会出发run方法,重写init可以传递参数
    t2 = MyThread("t2")
    t2.start()

子线程销毁的方式

import time
import threading

def sing():
    for i in range(10):
        print("sing")
        time.sleep(0.3)

if __name__ == '__main__':
    singTh = threading.Thread(target=sing)
    # 设置子线程为主线程的守护进程,随主线程销毁而销毁
    # singTh.setDaemon(True)
    singTh.start()
    time.sleep(0.5)
    print("over")

线程之间共享全局变量

import time
import threading

num=5
#子线程1递加数据
def addData():
    global num
    for i in range(10):
        num+=1
        time.sleep(0.3)
        print("子线程1中的num=", num)

#子线程2读取数据
def readData():
    global num
    print("子线程2中的num=", num)

if __name__ == '__main__':
    addPro = threading.Thread(target=addData)
    readPro = threading.Thread(target=readData)
    addPro.start()
    # 当前线程(主线程)等待addPro线程执行完毕,再向下执行
    addPro.join()
    readPro.start()
    # 当前线程(主线程)等待readPro线程执行完毕,再向下执行
    readPro.join()
    print("主线程中的num=", num)

线程之间共享全局变量,但是线程不安全

import time
import threading

num=5

#子线程1递加数据
def addData():
    global num
    for i in range(10000000):
        num += 1
    print("子线程1中的num=", num)

#子线程2递加数据
def addData2():
    global num
    for i in range(10000000):
        num += 1
    print("子线程2中的num=", num)

if __name__ == '__main__':
    #两个线程同时递加数据,导致数据不安全修改而出错
    addPro = threading.Thread(target=addData)
    addPro2 = threading.Thread(target=addData2)
    addPro.start()
    addPro2.start()

为了确保线程安全,使用threading.Lock来解决(牺牲效率,本质变成单线程,保证安全)

import time
import threading

num=5
lock=threading.Lock()

#子线程1递加数据
def addData():
    global num
    for i in range(10000000):
        #获得锁 ,acquire/release之间的代码保证只有一个线程在执行
        lock.acquire()
        num += 1
        #释放锁
        lock.release()
    print("子线程1中的num=", num)

#子线程2递加数据
def addData2():
    global num
    for i in range(10000000):
        lock.acquire()
        num += 1
        lock.release()
    print("子线程2中的num=", num)

if __name__ == '__main__':
    #两个线程同时递加数据,导致数据不安全修改而出错
    addPro = threading.Thread(target=addData)
    addPro2 = threading.Thread(target=addData2)
    addPro.start()
    addPro2.start()

对于有些全局变量,本身是线程安全的,比如queue(修改数据时内部自己实现了Lock)

import time
import threading
import queue

num=0
#创建线程锁,锁定非线程安全的共享数据(num)
lock=threading.Lock()
#队列是线程安全的(内部自动添加了线程锁)
q=queue.Queue()

def dealQueue(name):
    for i in range(1000000):
        q.put(i)
    print("%s:queue:%d"%(name,q.qsize()))

def dealNum(name):
    global num
    for i in range(1000000):
        #线程获取锁
        lock.acquire()
        num+=1
        #做完+1操作后释放锁
        lock.release()
    print("%s:num:%d"%(name,num))

def lockThread():
    t1 = threading.Thread(target=dealNum,args=("1",))
    t1.start()
    t2 = threading.Thread(target=dealNum,args=("2",))
    t2.start()
    #保证线程1,2都执行完打印结果
    """join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。"""
    t1.join()
    t2.join()
    print("main:num:%d"%num)

def lockThread2():
    t1 = threading.Thread(target=dealQueue,args=("1",))
    t1.start()
    t2 = threading.Thread(target=dealQueue,args=("2",))
    t2.start()
    t1.join()
    t2.join()
    print("main:q:%d" %q.qsize())

if __name__ == '__main__':
    lockThread()
    #lockThread2()

1.为客户端创建一个tcp收消息线程,一个tcp发消息线程。解决tcp服务端发送消息,客户端收不到的问题。
2.socket是全双工,可以同时收和发,使用同一个socket就行。

“”“客户端代码”“”
def tcpSendInfo(socket,inputStr):
    socket.send(inputStr.encode("utf-8"))

def tcpReceiveInfo(socket):
    recv_data = socket.recv(1024)
    recv_str = recv_data.decode("utf-8")
    print("tcp接收信息:%s" % recv_str)


class SendThread(threading.Thread):
    def __init__(self,socket):
        threading.Thread.__init__(self)
        self.socket=socket
    def run(self):
        while True:
            info = input("发送信息:")
            if (info == "q"):
                exit()
            # 向服务器发送消息
            tcpSendInfo(self.socket, info)

class ReceiveThread(threading.Thread):
    def __init__(self,socket):
        threading.Thread.__init__(self)
        self.socket=socket
    def run(self):
        while True:
            tcpReceiveInfo(self.socket)

def tcpmain():
    tcp_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    host="127.0.0.1"
    port = 8890
    #向服务器发起连接请求
    tcp_socket.connect((host,port))
    #创建发消息线程
    sendThread=SendThread(tcp_socket)
    #创建收消息线程
    receiveThread=ReceiveThread(tcp_socket)
    #启动收发消息线程
    sendThread.start()
    receiveThread.start()

if __name__ == '__main__':
    tcpmain()

补充一个单线程实现TCP服务器端(设置TCP阻塞方法不阻塞,收不到抛出异常):

def tcpmain2():
    tcp_socket=socket.socket(
        socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket.bind(("127.0.0.1",7799))
    #为tcp_socket设置解阻塞方法
    tcp_socket.setblocking(False)
    tcp_socket.listen(5)
    #创建客户端socket接受列表
    client_lists=list()
    while True:
        #因为tcp_socket设置为解阻塞,accept时如果收不到会抛出异常,不会阻塞
        try:
            clientSocket, clientAddr = tcp_socket.accept()
        except Exception as e:
            print("没有接受到客户端连接")
        else:
            print("客户端地址信息%s" % str(clientAddr))
            #把服务于客户端socket放入列表,同时也设置为解阻塞
            client_lists.appned(clientSocket)
            clientSocket.setblocking(False)

        #循环客户端列表处理客户端请求
        for client in client_lists:
            #因为client设置为解阻塞,recv时如果收不到会抛异常,不会阻塞
            try:
                recv_Data = client.recv(1024)
            except Exception as e:
                print("客户端没有发送消息")
            else:
                if recv_Data:
                    print("接收到客户端数据:%s", str(recv_Data))
                else:
                    #客户端断开时,从列表移除并close
                    clientSocket.close()
                    client_lists.remove(client)
    tcp_socket.close()
3.协程

Python中协程是利用yield在同一个线程中,通过暂停来实现任务之间的调度。
协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

  • 迭代器

1.迭代是Python最强大的功能之一,是访问集合元素的一种方式。
2.可以使用for循环的对象都是可迭代的,但不一定是迭代器;迭代器对象都可以使用for语句进行遍历。
3.把一个类作为一个迭代器使用需要在类中实现两个方法 iter() 与 next() 。

from collections.abc import Iterable
from collections.abc import Iterator

class Iter1:
    def __iter__(self):
        pass

class Iter2:
    def __iter__(self):
        pass
    def __next__(self):
        pass

#列表,元组是可迭代,但不是迭代器
print(isinstance((1,2),Iterable))   #True
print(isinstance((1,2),Iterator))   #False
#实现__iter__是可迭代的,实现__iter__,__next__是迭代器
print(isinstance(Iter1(),Iterable)) #True
print(isinstance(Iter1(),Iterator)) #False
print(isinstance(Iter2(),Iterable)) #True
print(isinstance(Iter2(),Iterator)) #True

给一个类实现迭代器

#创建迭代器对象,输入内部list
class TestIter:
    def __init__(self):
        self.list=[]
    def add(self, name):
        self.list.append(name)
    def __iter__(self):
        #定义index在迭代器记录位置
        self.index = 0
        return self
    def __next__(self):
        #指定迭代器退出条件
        if self.index<len(self.list):
            name=self.list[self.index]
            self.index+=1
            return name
        else:
            raise StopIteration

testIter=TestIter()
testIter.add("zs")
testIter.add("ss")
testIter.add("tt")
#使用for循环输出实现迭代器的对象
for i in testIter:
    print(i)

#使用iter()方法转化成迭代器对象,使用next()函数输出
testIter_it=iter(testIter)
#print(next(testIter_it))
while True:
    try:
        print (next(testIter_it), end=" ")
    except StopIteration:
       break
  • 生成器

1.在 Python 中,使用了 yield 的函数被称为生成器(generator)。
2.跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
3.在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行
next() 方法时从当前位置继续运行。
4.调用一个生成器函数,返回的是一个迭代器对象。

#创建一个生成器函数
def testGer(count):
    a=0
    while a<count:
        """
        yield的含义就是暂时中断函数执行,并返回a给next()
        下次调用next(),执行中断之后的代码,然后遇到yield再次中断返回值
        依次循环
        """
        yield  a
        a+=1

    #return "end"

#函数返回值是一个迭代器
tg=testGer(10)

#for循环打印数据
for i in tg:
    print(i)

#使用next输入数据
while True:
    try:
        res=next(tg)
        print(res,end="")
    except Exception as e:
        #如果迭代器有返回值,使用异常捕获输出
        print(e.value)
        break

send() 方法。这让我们不仅可以暂停生成器,而且能够传递值到生成器暂停的地方。

#创建一个生成器函数
def testGer(count):
    a=0
    while a<count:
        #jump接收send()的值,接收到之后继续向下执行生成器函数
        jump=yield  a
        if jump==None:
            jump=1
        a+=jump

print(next(tg))  #0
print(next(tg))  #1
print(tg.send(2)) #3
print(tg.send(5)) #8
  • gevent

对yield方式进行封装的协程序模块

#让所有阻塞方法使用gevent阻塞方法
monkey.patch_all()

def dosome(name):
    for i in range(10):
        print("name:%s:%d"%(name,i))
        time.sleep(0.5)
        #如果使用gevent模块的阻塞方法就不需要monkey.patch_all()
        #gevent.sleep(0.5)

#执行任务
e1=gevent.spawn(dosome,"e1")
e2=gevent.spawn(dosome,"e2")
"""
等价于 
#e1.join()  
#e2.join()
"""
gevent.joinall((e1,e2))
print("执行完毕")

那么,到底使用线程还是进程呢,或者是协程呢?
1.先了解一下GIL
http://www.runoob.com/w3cnote/python-single-thread-multi-thread-and-multi-process.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容