- 1.面向对象编程
- 1.1类:
- 1.2如何使用类
- 1.3 如何使用对象
- 1.4 类的继承与重用性
- 1.5多态与多态性
- 1.6封装(私有方法)
- 1.7绑定方法和非绑定方法(成员)
- 1.8反射
- 1.9内置方法
- 1.9.1 isinstance(obj,cls) 判断对象是否是某个类或其子类的实例
- 1.9.3 —item—系列 (模拟成字典去操作)
- 1.9.4 —str— 打印对象的时候触发,
- 1.9.5 —del—
- 1.9.6—new—构造方法,在init方法之前触发
- 1.9.7—call—方法 对象加括号会执行该方法
- 1.9.8—dict—方法,获取对象中的实例变量,以字典形式返回
- 1.9.9 —enter— 和—exit—,让对象支持with语法(上下文管理)
- 1.9.10 —add—方法,对象相加时触发
- 1.9.11 —iter—
- 可迭代对象
- 1.9.12 classmethod ,staticmethod,property
- 1.9.13 callable ,是否可在后面加括号执行(True,False)内部有—call—方法
- 1.9.14 super 按照self当前类的mro继承关系向上找成员
- 1.9.15 issubclass,判断是否是某个类的子孙类
- 1.10 元类介绍
- 1.11 单例模式
- 1.12 异常处理
- 1.13 socket
1.面向对象编程
面向过程编程,过程是解决问题的步骤,一步一步规划好,先干嘛后干嘛
优点:编程简单,复杂的问题流程化
缺点:扩展性差
def denglu:输入密码账户name = input(">>>>>用户")passwd = input(">>>>>密码")return {"name:name,"pwd":pwd}def check(user_info):检查正确与否is_valis = TRUEif len(user_info['name']) == 0:print('用户名并不能为空')is_valis = Falseif len(user_info['pwd']) < 6 :print('密码不能少于6位')valis = Falsereturn {'is_valis':valis,'user_info':user_info}def regeister(check_info):if check_info['is_valid']:with open('xxx.text','w',encoding='utf-8') as f:def main():user info = denglu()check info = check(user info)regeister (check info)if __name__ == '__main__'main------------------------------------------------__开头和__结尾就是python内置的,在特定的时候触发
面向对象编程: 核心就是对象(python一切皆对象)
什么是对象:特征与技能的结合优点:可扩展性好缺点:编程复杂应用场景:客户需求经常变化,互联网应用,游戏,企业内部应用
1.1类:
一系列对象相似的特征与技能的结合
类的定义与实例化对象
先定义类根据类实例化对象
class func():def __init__(self,name,age) # 初始化方法,实例化对象的时候会触发name = nameage = age# 对象 = 类名() 会自动执行类中的__init__方法n1 = func() # 实例化一个对象
初识面向对象
定义类 在类中定义方法,在方法中实现具体的功能实例化类并得到一个对象,通过对象去调用执行方法类名首字母大写py3之后默认类都继承object在类中编写的函数叫方法每一个方法的第一个参数是self
对象和self
在每个类中都可以定制特殊的—init—初始化方法,在实例化类创建对象的时候自动执行
self 接收的是调用方法的当前对象,本质就是一个参数
对象,基于类实例化出来的一块内存,默认里面没有数据,经过—init—方法,可以在内存中初始化一些数据
根据类创建对象,也就是内存的一块区域执行--init--方法,模块会将创建的那块区域的内存地址当参数传递给self
常见成员:
实例变量,属于对象,只能通过对象调用,__init__方法下的变量就是实例变量类变量,在类中定义的变量绑定方法 属于类 绑定给对象和绑定到类 可以用对象或类调用
1.2如何使用类
class func:school = "lufei" # 数据属性 类变量def learn(self): # 函数属性print("learn")def eat(self): #函数属性print("eat")
查看类的名称空间 print(func.__dict__)类定义完成后对类的属性的增删改查方法 就是操作func.__dict__这个字典增加一个数据属性 func.county = 'china'删除 del func.county改 func.school = 'luffycity'查 print(func.__dict__[school])实例化出对象 obj = func() # 实例化出来的类有自己独立的存储空间
1.3 如何使用对象
—init—方法用来为对象定制对象自己独有的特征(在实例化对象的时候触发)
init方法实例化的步骤
1.先产生一个空对象2.参数传值3.根据init方法初始化
class func:school = 'liu'def __init__(self,name,age) # self接收的是objself.name = nameself,age = agedef learn(self):def eat(self):obj = func('utr',19)
对象的增删改查增加 obj.class_name = 'python'删除 del obj.age改 obj.name = "luffycity"查 obj.name
1.3.1属性查找与绑定方法
类中的数据属性也就是变量是所有对象共有的,指向的是一个内存地址类中的函数属性 :是绑定给对象的,绑定到不到的对象是不同的,内存地址是不一样的类调用自己的函数属性,(self)需要手动传值类中的函数属性是绑定的对象使用的,哪个对象来调用,就将对象当作第一个参数传入对象使用属性的时候,现在自己的空间寻找,没有去类的空间寻找,有继承就去父类寻找
1.3.2 一切皆对象
站在不同的角度,定义出来的类是不同的现实当中的类并不等于代码当中的类,如现实中的公司类,在程序中需要拆分成部门类业务等
1.3.3面向对象可扩展性强
1.3.4 对象的嵌套
class student(object):def __init__(self):self.name =nameself.age =agedef message =(self):data = '学生{},{}'.fornat(self.name,self.age)print(data)stud1 = student('123',18)stud2 = student('456',20)stud3 = student('789',22)class classes(object):def __init__(self,title):self.title = titleself.student_list = []def add_student(self,stud_object):self.student_list.append(stud_object)def add_students(self,stu_object_list)for stu in stu_object_list:self.add_student(stu)def show_members(self):for item in self.student_list:print(item)c1 = classes('三年二班')c1.add_studnet(s1)c1.add_students([s2,s3])print(c1.title)print(c1.student_list)
1.4 类的继承与重用性
继承是什么是什么的关系
将类中公共的方法提取到基类中
查看继承了哪些父类 派生类.—bases— 元组形式
class func1:passclass func2:passclass func3(func1): #func3继承func1的属性 func1是基类 func3叫派生类pass
1.4.1继承的属性查找
首先在自己的空间中寻找,没有则去对应的类中寻找,类中也没有去继承的基类中寻找(关注self是谁,不管找到哪遇到self就从先从自己空间中找起)
1.4.2派生
当派生类中有自己的属性,则首先使用自己的
1.4.3继承的实现原理
定义的类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表 (查看线性列表,派生类.mro())也就是继承顺序的列表python2 区分新式类:继承object的类,以及他的子类都是新式类经典类: 没有继承object,以及他的子类都是叫经典类python3 只有新式类,默认继承object类深度优先:经典类广度优先:新式类
1.4.4派生类重用基类的属性
super(自己的类,self).—init—(属性名) ## 顺着mro列表往后找,依赖继承关系 super(自己的类,self)得到一个对象然后调用基类中的方法
class func:def __init__(self,name,age,igh)self.name = nameself.age = ageself.high = highdef ease(self,food)print("{}的food".formt(self.name))class gurn(func):def att():super(gurn,self).ease('苹果')
class A:def f1(self):print('from A')super().f1()class B:def f1(self):print('from B')class C(A,B):passprint(C.mro()) ## [<class '__main__.C'>,<class '__main__.A'>,<class '__main__.B'>]c =C()c.f1()1. C中没有f1属性,根据继承去A中找2. A中有f1属性先输出from A 然后执行super().f1()执行的是B的f13. 因为super是根据mro往后找也就是B的f1,因为是基于c产生的mro列表,现在A中f1已经找完了mro列表中的下一个是B,所以是输出from B, (根据c产生的mro当前找到哪了继续往后找)
1.4.5抽象类与归一化
抽取相似的地方得到一个父类,子类中的属性必须实现父类中的规范
抽象出来的不定义具体的功能,只写规范
抽象类只能被继承,不能被实例化
子类中必须有抽象类的方法
使用abc第三方模块实现
import abcclass animal(metaclass= abc.ABCMeta):@abc.abstracmethod # 子类中必须有run方法def run(self)pass@abc.abstracmethod # 子类中必须有eat方法def eat(self):passclass Pig(animal):def run(self):class Dog:def zou(self):
1.4.6 mro列表 (继承关系) 与C3算法
class c(object):passclass B(object):passclass A(B,C):print(A.mro()) 列表形式print(A.__mro__) 元组形式
mro(A) = [A] + [B,C] #mroA= [ABC]mro(A)=[A] + merge( mro(B),mro(c),[B,C] )#A的继承关系 首先将[A]放进来 merge接收A左边的继承关系,然后是右边的继承关系,然后是所继承的父类[B,C]mro(A)=[A] + merge( [B,object],[C,object],[B,C] )# 第一个参数mro(A)=[A]+[B,C,object]mro(A) = [A,B,C,object]# merge 计算规则将第一个参数的第一个值(此时为B),与其他参数的除第一个值以外的比较,判断是否在里面如果不在将其提取出来,去掉原值,(就是将参数里面的所有的B剔除掉)如果在里面跳过,进行下一个参数的第一个值开始比较,最后把剩下的提取出来

图中的继承关系使用C3算法计算过程
[^]: 从左到右,深度优先,大小钻石,留住顶端 ,可以更快的找到类的继承关系,钻石是指方形的继承
1.5多态与多态性
python默认支持多态(这种方式称之为鸭子类型)
(在继承的背景使用下有时候也叫多态性)
多态是指同一类物体有多种形态(如文件有文本文件和可执行文件)多态性是值不考虑实例类型(对象类型)的情况下使用实例(对象)比如+ 支持文本相加,数字相加,+本身不用考虑加的是什么类型
增加了程序的灵活性 :使用者都是同一种形式去调用 func(n1)
增加了程序的可扩展性:通过继承创建一个新的类,使用者不用更改自己的代码还是用func(n1)调用
鸭子类型:看起来像就是
def func(arg):v1 = arg.copy()print(v1)func('123')func([11,22,33,44])# 将字符串或者列表传给arg时候,arg可以接收任意类型的数据,只要参数有copy方法就可以# 这种形式就可以理解为多态# 序列类型,列表 元组 字符串l = list([1,2,3])t = tuple((a,b))s = str ('he')l.__len__()t.__len__()s.__len__()
1.6封装(私有方法)
将同一类的方法封装到一个类中
将数据封装到对象中,如—init—
封装相当隐藏 (在属性名字前面加__)对象无法直接调用 又叫成员修饰符
class A:__X = 1 #在类的名称空间中为_A__X 查看名称空间 A.__dict__def __init__(self,name):self.__name = name # 会变形def __foo(self):passA.__C = 2 # __C不会变形可以直接调用print(A.__C)"""特点类的外部无法直接调用 (A.__X) 可以通过调用(A._A__X)类内部可以直接调用 (A.__X)子类无法覆盖父类__开头的属性(覆盖是指两个类中相同的属性名)这种隐藏注意的问题这种变形只在定义阶段发生一次,类定义阶段之后赋值则不会变形在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有方法也就是加__,子类不会继承父类的私有方法"""
封装到底是要干什么
1.6.1封装数据属性干什么:明确的区分内外控制外部对隐藏属性的操作行为
class People:def __init__(self,name,age)self.__name = nameself.__age = agedef tell_info(self):print('name{} age{}'.format(self.__name,self.__age))def set_info(self,name,age):if not isinstance(name,str):print('name必须是字符串')if not isinstance(age,int):print('age必须是数字')self.__name = nameself.__age = agep = people('liu',18) # 外部无法直接调用p.__namep.tell_info() # 通过统一的接口调用p.set_info('123',45)
1.6.2 封装函数属性干什么:隔离复杂度
class ATM:def __card(self):print('插卡')def __auth(self):print('password')def __input(self):pirnt('输入取款金额')def __print_bill('self'):print('打印账单')def __take_money(self):print("取款")def withdraw(self):self.__cardself.__authself.__inputself.__print_billself.__take_moneya = ATM()a.withdraw()
1.6.3封装与扩展性
class Room:def __init__(self,name,owner,height,weight,length)self.name = anmeself.owner = ownerself.__weight = weightself.__length = lengthdef tell_area(self):retuen self.__weight * self.lengtha = Room('o','kiler',10,10,10)a.tell_area()
1.6.4 property 装饰器 (函数,特性)
在方法前面加上@property 对象在调用方法的时候不用加()了
class People:def __init__(self, name, weight, height):self.name = nameself.weight = weightself.height = height@propertydef Bmi(self):return self.weight / (self.height ** 2)a = People('kiler',70,1.74)a.Bmi
class people:def __init__(self,name):self.__name = name@property #访问 是不能给name赋值的,因为name是一个方法def name(self):return (self.__name)@name.setter #修改def name(self,value) #现在是可以赋值的,前提是name被property装饰if not instance(val,str)print("名字必须是字符串")returnself.__name = value@name.deleter #删除def name(self)print('不允许删除')p = People('liu')print(p.name)p.name = 'li' # 执行setterdel p.name # 执行deleter
1.7绑定方法和非绑定方法(成员)
在类定义的方法分为两类绑定方法 (绑定给谁就由谁来调用,将调用者当第一个参数自动传给self)- 绑定到对象的方法:在类定义的时候没有被任何装饰器修饰的- 绑定到类的方法:在方法前面加@classmethod,则就是绑定给类的方法,类在调用的时候,将类当作第一个参数传给cls非绑定方法 又叫静态方法就是不与类或者对象绑定(类可以用对象也可以用,不会自动传值,需要手动传参)就是类中普通的函数在方法前面加@staticmethod
1.7.1绑定方法和非绑定方法的使用
绑定到对象的方法class people:def__init__(slef,name,age,sex):self.name = nameself.age = ageself.sex = sexdef tell_info(self): 绑定到对象的方法print("{},{},{}".format(self.name,self.age,self.sex))p = people('kiler',18,20)p.tell_info()绑定给对象,应该由对象来调用,自动将对象本身当作第一个参数传入p.tell_info(p)
import settings # settings包里面有name age sexclass people:def __init__(self, name, age, sex):self.name = nameself.age = ageself.sex = sexdef tell_info(slef): #绑定到对象的方法print("{},{},{}".format(slef.name, slef.age, slef.sex))@classmethod # 绑定给类的方法def from_setting(cls):obj = cls(settings.name,settings.age,settings.sex)print(obj.name)return obj #是一个对象# 绑定给类,由类调用,自动将类本身当作第一个参数传入p=people.from_setting(people)p = people.from_setting()p.tell_info()
import timeimport hashlibimport settings # settings包里面有name age sexclass people:def __init__(self, name, age, sex):self.id = self.id()self.name = nameself.age = ageself.sex = sexdef tell_info(self): #绑定到对象的方法print("{},{},{}".format(slef.name, slef.age, slef.sex))@classmethod #绑定给类的方法def from_setting(cls):obj = cls(settings.name,settings.age,settings.sex)print(obj.name)return obj #是一个对象@staticmethod #静态方法 非绑定方法def id():m = hashlib.md5(str(time.time()).encode('utf-8'))return m.hexdigest()# 绑定给类,由类调用,自动将类本身当作第一个参数传入p = people.from_setting()p.tell_info()p1 = people('12',12,12)print(p1.id())
1.7.2 属性@property
属性由绑定方法+特殊装饰器组合
在方法前面加上@property 对象在调用方法的时候不用加()了
class People:def __init__(self,name,weight,height)self.name = nameself.weight = weightself.height = height@propertydef Bmi(self):return self.weight/(self.height** 2)a = people('kiler',75,1.9)a.Bmi()a.bmi
class people:def __init__(self,name):self.__name = name@property #访问 是不能给name赋值的,因为name是一个方法def name(self):return (self.__name)@name.setter #修改def name(self,value) #现在是可以赋值的,前提是name被setter装饰if not instance(val,str)print("名字必须是字符串")returnself.__name = value@name.deleter #删除def name(self)print('不允许删除')obj = people('345')obj.name = "123" # 找到 @name.setter 并将123传递给valuedel obj.name()
class c(object):def getx(self):passdef setx(self):passdef delx(self):passx = property(getx,setx,delx,'i am x')obj = c()obj.x # 会执行property中的getx 方法obj.x = 123 # 会执行property中的setx 方法del obj.x # 会执行property中的delx 方法
1.8反射
通过字符串映射到对象属性(’以字符串的形式去对象中进行成员的操作’)
class people:def __init__(self,name,age):self.name = nameself.age = agedef talk(self):print("{}<{}".formt(self.name,self.age))obj = people('kile',18)# 对象 属性名hasattr(obj,'name')#判断obj对象下有没有name属性返回True或False obj.__dict__['name']getattr (obj,'talk',none) # 拿到obj下的talk方法 加括号就可以执行 如果obj没有name属性则返回nonesetattr(obj,'sex','male') #obj.sex = maledelattr(obj,'age') # del boj.age
1.8.1 import_module + 反射
可以以字符串的形式导入模块 #只能导入模块
#导入模块from importlib import import_modulem = import_module('random')m = import_moudle('request.exceptions')v1 = m.randint(1,100)
使用import_module 和 getattr配合实现根据字符串的形式导入模块并获取成员
from omportlib import import_modulepath = "openpyxl.utils.exceptions.InvalidFileException"module_path,class_name = path.rsplit("."maxsplit=1)module_object = import_module(module_path)cls = getattr(module_object,class_name)print(cls)
1.9内置方法
1.9.1 isinstance(obj,cls) 判断对象是否是某个类或其子类的实例
class Foo(object):passobj = Foo()isinstance(obj,Foo) # 判断obj是否是Foo类的实例 对于有继承关系实例出来的对象也是True
1.9.3 —item—系列 (模拟成字典去操作)
class Foo:def __init__(self,name):self.name = namedef __getitem__(self,item): #查看的时候触发print('get')return self.__dict__.get(item)def __setitem__(self,key,value): #设置值的时候触发print('set')self.__dict__[key]=valuedef __delitem__(self,key): #删除的时候触发print("del")del self.__dict__[key]obj = Foo()obj['name'] # obj.__dict__['name'] 触发getitemobj['sex'] =16 #触发setitem sex为key,16为valuedel obj['name']
1.9.4 —str— 打印对象的时候触发,
class people:def __init__(self,anme,age):self.name = nameself.age = agedef __str__(self): # 在打印的时候触发print('str')return '{},{}'.format(self.name,aelf.age) # 必须返回字符串obj = people('si',18)print(obj) # 触发__str__
1.9.5 —del—
回收资源,在对象被删除的时候会自动先触发该方法
class open:def __init__(self,filename):print('open file')self.name = filenamedef __del__(self):print('回收资源')f = open('setting.py')
1.9.6—new—构造方法,在init方法之前触发
class foo:def __init__(self,name):print('第二步,初始化对象,在空对象中创建数据')def __new__(cls,*args,**Kwargs):print('第一步,先创建空对象并返回')return object.__new__(cls)obj = foo('123')
1.9.7—call—方法 对象加括号会执行该方法
class foo:def __call__(self,*args,**kwargs):print('对象加括号会执行call方法')obj = foo('123')obj() #执行call方法
1.9.8—dict—方法,获取对象中的实例变量,以字典形式返回
class foo:def __init__(self,anme,age):self.name = name # 实例变量self.age = ageobj = foo('kil',18)print(obj.__dict__)
1.9.9 —enter— 和—exit—,让对象支持with语法(上下文管理)
class Foo:def __enter__(self):self.连接 = 连接操作return self.连接def __exit__(self,exc_type,exc_val,exc_tb):self.连接关闭obj = Foo()#with 对象 as XX 在内部会执行--enter--方法,并将方法中的返回值给XX#当with缩进中的代码执行完毕后,自动执行--exit--方法with obj as f:操作......-------------------------------------------------#面试题class Comtext:def __enter__(self):return self #将当前对象返回给 ctxdef __exit__(self,exc_type,exc_val,exc_tb):passdef do_something(self):print('内部执行')with Context() as ctx:print("内部执行")ctx.do_something() #当前对象可以执行do_something()方法
1.9.10 —add—方法,对象相加时触发
class foo:def __add__(self,other) #outher接收的是v2这个对象return 99v1 = foo()v2 = foo()#对象+值,内部会执行 对象.--add--方法,并将+后面的值当作参数传递进去v3 = v1+v2print(v3) 99
1.9.11 —iter—
迭代器
定义:1.类中定义了 --iter-- 和 --next--两个方法2.--iter--方法需要返回对象本身,即self3.--next--方法,返回下一个数据,如果没有数据了需要抛出一个stopiteration创建迭代器类型class IT:def --init--(self):self.counter = 0def --iter--(self):return selfdef --next--(self):self.counter +=1if self.counter == 3:raise StopIteration()return self.counter根据类实例化创建一个迭代器对象obj1 = IT()#v1 = obj1.--next--()#v2 = obj1.--next--()#v3 = obj1.--next--() #抛出异常v1 = next(obj1) # obj1.__next__()v2 = next(obj1)v3 = next(obj1)obj2 = IT()for item in obj2: #先执行迭代器对象的--iter--方法并获取返回值,一直反复执行next(对象),直到抛出异常print(item)迭代器对象支持通过next取值,如果取值结束则自动抛出Stopiterarionfor 循环内部在循环时,先执行--iter--,获取一个迭代器对象,然后一直执行next取值,有异常终止
可迭代对象
#一个类中有--iter--方法且返回一个迭代器对象,根据这个类创建的对象叫可迭代对象class foo:def--iter--(slef):return 迭代器对象(生成器对象)obj = foo()#可迭代对象是可以使用for循环,在循环内部是先执行--iter--,获取迭代器对象,在内部指向这个迭代器对象的next功能,逐步取值for item in obj:print(item)======================================================================# 基于可迭代对象和迭代器实现自定义rangeclass Iter:def __iter__(self,name):self.num = numself.counter = -1def __iter__(self):return selfdef __next__(self):self.counter +=1if self.counter == self.num:raise StopIteration()return self.counterclass Xrange:def __init__(self,max_num):self.max_num = max_numdef __iter__(self):return Iter(self.max_num)obj = Xrange(100)for item in obj:print(item)#================================================#基于可迭代对象和生成器,自定义rangeclass Xrange:def __init__(self,max_num):self.max_num - max_numdef __iter__(self):counter = 0while counter < self.max_num:yiled countercounter +=1obj = Xrange(100)for item in obj:print(item)
from collections.abc import Iterator,Iterablev1 = list([1,2,3]) #v1是可迭代对象因为列表中生命了__iter__方法并且返回一个迭代器对象print(instance(v1,Iterator)) 判断是否是迭代器print(instance(v1,Iterable)) 判断是否有__iter__且返回迭代器对象
1.9.12 classmethod ,staticmethod,property
1.9.13 callable ,是否可在后面加括号执行(True,False)内部有—call—方法
1.9.14 super 按照self当前类的mro继承关系向上找成员
class Base(object):def message(self,num):print('base.message',num)class Foo(Base):def message(self,num):print("Foo.meaasge",num)super().message(num+10) #执行base里面的messageobj = Foo()obj.message(1)
1.9.15 issubclass,判断是否是某个类的子孙类
class Top(object):passclass Base(Top):passclass Foo(Base):Passprint(issubclass(Foo,Base)) Trueprint(issubclass(Foo,Top)) True
1.10 元类介绍
产生类的类就是元类 ,type()
默认用class 定义的类,元类是type
exec('字符串形式的的命令',‘全局作用域’,‘局部作用域’)# 为字符串形式的命令定制一个全局作用域(不指定默认用globals())和局部作用域名(不指定默认locals())# globals() 查看全局作用域中的值g = {'x' = 1,'m' = 2,}l = {}exec('''global x,mx = 10m = 100z = 3''',g, l) # 全局作用域是g局部作用域名是l#exec相当于一个函数执行过程中产生的局部变量放到l中,exec执行过程中产生的全局变量放到g中# g = {'x'=10,'m'=100} l={z:3}
python一切皆是对象,对象怎么用可以被引用 X = obj可以当做函数的参数传入可以当作函数的返回值可以当作容器类的元素
class Foo: #类也是对象,对象是实例化出来的 Foo = type()pass
1.10.1 定义类的两种方式
1.class定义的
2.type
定义类的要素:类名 继承 类的名称空间用type定义类class_name = 'chinese' # 类名class_base = (object,) # 继承class_body = """ # 类体代码country = 'china',def __init__(self,name,age):self.name = nameself.age = agedef talk (self):print("{}<{}".format(self.name,self.age))"""class_dic = dict{} # 类的名称空间exec=(class_body,globals(),class_dic) #使用exec生成名称空间chines1 = type(class_name,class_bases,class_dic) #type(类名,继承,名称空间)
自定义元类控制类的创建
自定义的元类只是修改部分方法,所以要继承元类
class meta(type):def __init__(self, class_name, class_base, class_dic):if not class_name.istitle(): # 首字母大写raise TypeError('类型错误') # 抛出异常if "__doc__" not in class_dic or not class_dic['__doc__'].strip():raise TypeError("必须有注释")super(meta,self).__init__(class_name, class_base, class_dic)# __doc__是类空间的注释内容,如果类中有注释则类的名称空间就有__doc__class Chinese(object, metaclass=meta): # metaclass 指定元类,object是默认继承的# Chinese = meta(class_name,class_base,class_dic)"""注释"""country = 'china'def __init__(self, name, age):self.name = nameself.age = agedef talk(self):print("{}<{}".format(self.name, self.age))
1.10.2 自定义元类控制类的实例化行为
__call__ 方法 对象在被调用的时候触发 元类当中也有call方法class Foo:def __call__(self,*args,**kwargs):print(self)print(args)print(Kwargs)obj = Foo(1,2,3,a = 1) #Foo.__call__(Foo,1,2,3,a = 1)#调用类发生的事情1.创建空对象obj = object.__new__(self):2.初始化__init__self.__init__(obj,*args,**Kwargs):3.返回值return obj
class meta(type):def __init__(self, class_name, class_base, class_dic):if not class_name.istitle(): # 首字母大写raise TypeError('类型错误') # 抛出异常if "__doc__" not in class_dic or not class_dic['__doc__'].strip():raise TypeError("必须有注释")super(meta, self).__init__(class_name, class_base, class_dic)def __call__(self, *args, **kwargs):# 调用类发生的事情#1.创建空对象obj = object.__new__(self)#2.初始化__init__self.__init__(obj, *args, **kwargs)print(obj.__dict__)#3.返回值return obj# __doc__是类空间的注释内容,如果类中有注释则类的名称空间就有__doc__# __call__是对象被调用的时候触发的方法,控制对象的实例化class Chinese(object, metaclass=meta): # metaclass 指定元类,object是默认继承的"""注释"""country = 'china'def __init__(self, name, age):self.name = nameself.age = agekwq = Chinese('kil',18)
1.11 单例模式
单例模式就是对象的数据属性值都一样的时候不开辟新的内存空间用一个
class Mysql:__instance = Nonedef __init__(self):self.host = '127.0.0.1'self.port = 3306@classmethoddef singleton(cls):if not cls.__instance:obj = cls()cls.__instance = objreturn cls.__instanceobj1 = Mysql.singleton()obj2 = Mysql.singleton()print(obj1)print(obj2)#obj1 和 obj2的数据属性都是一样的
1.12 异常处理
异常就是程序运行时发生错误的信号,程序出现错误,并且程序没有处理这个错误信息,就会抛出异常
并且程序运行随之终止
错误的种类,语法错误和逻辑错误
try:要检测的代码except 错误类型 as e: #e包含的是try里面检测到的错误print('错误')print('1')print('2')---------------------------检测的代码中有错误时,输出错误,不再往下执行
try ….except 多分支的使用方法
被检测的代码块抛出的异常有多种可能性,并且我们需要针对每一种异常类型都定制专门的处理逻辑
try:print('1')nameprint('2')l = [1,2]l[1000]print('3')d = {}d[name]print('4')except NameError as e:print(e)except IndexError as e:print(e)except KeyError as e:print(e)
万能异常
被检测的代码块抛出的异常有多种可能性,对多有的异常类型定制一种公用的处理逻辑
try:print('1')nameprint('2')l = [1,2]l[1000]print('3')d = {}d[name]print('4')except Exception as e:print('统一的处理方法')else:print('被检测的代码没有发生异常时执行')finally:print('代码有无异常都执行,一般用于释放资源 ')#在try或者except中使用了return,finally中的代码还是会执行# 遇到return是return结果之前执行finally中的代码
自定义异常
class MyException(Exception):def __init__(self,msg):super(Exception,self).__init__()self.msg=msgdef __str__(self):return '{}'.format(self.msg)raise Exception('自定义异常') # 触发异常#raise会显示异常的追踪信息和类型,还会打印这个对象的操作,也就是触发对象下的str方法
断言 assert
info = {}info['name'] = 'kil'info['age'] = 18"""if "name" not in info:raise KeyError('没有name')if "age" not in info:raise KeyError('没有age')"""if info['name'] == 'kil' and ('age') > 10:print('欢迎')assert ('name' in info) and ('age' in info) #功能等于两行if判断
1.13 socket
socket 是在传输层和应用层之间抽象出来的一组接口,把复杂的tcp/ip协议簇隐藏在socket接口后面,对用户来说直接调用socket的接口就可以,socket负责去组织数据以符合指定的协议
套接字工作流程

服务端先初始化socket,然后与端口绑定bind,对端口进行监听listen,调用accept阻塞,等待客户端连接,此时如果有客户端初始化socket,连接服务器connect,如果连接成功,此时就建立起连接,客户端发送数据请求,服务端接受请求并处理请求,然后把回应数据发送到客户端,最后关闭连接,一次交互结束
socket套接字方法
family(socket家族)socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)socket type类型socket.SOCK_STREAM #for tcpsocket.SOCK_DGRAM #for udpsocket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。proto=0 请忽略,特殊用途fileno=None 请忽略,特殊用途服务端套接字函数s.bind() 绑定(主机,端口号)到套接字s.listen() 开始TCP监听s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来客户端套接字函数s.connect() 主动初始化TCP服务器连接s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常公共用途的套接字函数s.recv() 接收数据s.send() 发送数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完,可后面通过实例解释)s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)s.recvfrom() Receive data from the socket. The return value is a pair (bytes, address)s.getpeername() 连接到当前套接字的远端的地址s.close() 关闭套接字socket.setblocking(flag) #True or False,设置socket为非阻塞模式,以后讲io异步时会用socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) 返回远程主机的地址信息,例子 socket.getaddrinfo('luffycity.com',80)socket.getfqdn() 拿到本机的主机名socket.gethostbyname() 通过域名解析ip地址
import socketphone = socket.socket(socket.AF_INET,socket.socket_STREAM) #选择协议phone.bind(('127.0.0.1',22)) #绑定端口0-65535,0-1024是给操作系统用的phone.listen(5) #最大连接数conn,client_addr = phone.accept() #等待连接 conn是连接对象 client_addr是客户端ip和端口data = conn.recv(1024) #最大接收1024byte 接受数据conn.sendall(data.upper()) #发送数据conn.close() #关闭连接phone.close()#======================================================客户端import socketphone = socket.socket(socket.AF_INET,scoket.socket_STREAM) #选择协议phone.connect(('127.0.0.1',22)) #发起连接phone.send(' hello',encode('utf-8'))data = phone.recv(1024)phone.close
通信循环
import socketphone = socket.socket(socket.AF_INET,soc ket.socket_STREAM) #选择协议phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #重复使用端口phone.bind(('127.0.0.1',22)) #绑定端口0-65535,0-1024是给操作系统用的phone.listen(5) #最大连接数conn,client_addr = phone.accept() #等待连接while True: #通信循环data = conn.recv(1024) #最大接收1024byte 接受数据conn.send(data.upper()) #发送数据conn.close() #关闭连接phone.close()
#当客户端单方面中断连接后,(客户端会发送一个空)服务端会一直卡在接收过程,形成死循环import socketphone = socket.socket(socket.AF_INET,soc ket.socket_STREAM) #选择协议phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #重复使用端口phone.bind(('127.0.0.1',22)) #绑定端口0-65535,0-1024是给操作系统用的phone.listen(5) #最大连接数conn,client_addr = phone.accept() #等待连接while True: #通信循环data = conn.recv(1024) #最大接收1024byte 接受数据if not data:break # 适用linux系统,解决 客户端单方面中断连接后,服务端会一直卡在接收过程,形成死循环"""解决winodws下 客户端单方面中断连接后,服务端会一直卡在接收过程,形成死循环try:data = conn.recv(1024)conn.send(data.upper())except ConnectionResetError:break"""conn.send(data.upper()) #发送数据conn.close() #关闭连接phone.close()-------------------------------------------------------------------import socketphone = socket.socket(socket.AF_INET,scoket.socket_STREAM) #选择协议phone.connect(('127.0.0.1',22)) #发起连接while True: #通信循环msg = input(">>>>").strip() #if not mag:continue #判断输入是否为空,要是空的话contiunephone.send(msg.encode('utf-8'))print('send')data = phone.recv(1024)print("recv")print(data.decode('utf-8'))phone.close
多客户端与多服务端
链接循环
import socketphone = socket.socket(socket.AF_INET,socket.socket_STREAM)phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 避免端口被占用phone.bind(('127.0.0.1',22))phone.listen(5)while True: #连接循环conn, client_addr = phone.accept()while True:# 通信循环data = conn.recv(1024)if not data: breakconn.send(data.upper())conn.close()phone.close()
模拟ssh远程
"""dir :查看某个文件夹下的子文件名和子文件夹 lsipconfig :查看ip信息 ifconfigtasklist :查看运行的进程 ps aux执行系统命令import osos.system("dir C:\")或者import subprocessobj = subprocess.Popen("dir C:\",shell = True,stdout=suprocess.PIPE,stderr= subprocess.PIPE)taskkill python 杀死python端口进程"""import subprocessimport socketphone = socket.socket(socket.AF_INET,socket.socket_STREAM)phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)phone.bind(('127.0.0.1',22))phone.listen(5)while True: #连接循环conn, client_addr = phone.accept()while True:data = conn.recv(1024) # 接收客户端的命令if not data: break# 执行命令并得到结果,正确结果放到stdout=suprocess.PIPE,错误放到stderr= subprocess.PIPEobj = subprocess.Popen(data,shell = True,stdout=suprocess.PIPE,stderr= subprocess.PIPE)stdout = obj.stdout.read()stderr=obj.stderr.read()conn.send(stdout+stderr)conn.close()phone.close()--------------------------------------------------------------------------import socketphone = socket.socket(socket.AF_INET,scoket.socket_STREAM)phone.connect(('127.0.0.1',22))while True:msg = input(">>>>").strip()if not mag:continuephone.send(msg.encode('utf-8'))data = phone.recv(1024) #接收的数据大于1024的时候,不会丢失,在下次执行命令的时候显示出来#粘包现象print(data.decode('utf-8'))phone.close
粘包原理
send和recv都不是直接接收对方的数据,是操作自己的操作系统内存接收的长度小于数据包的长度,导致阻塞在管道中或者因为Nagle算法将多次间隔较小且数据量小的数据合并成一个大的数据块,然后进行封包进行发送一次recv 1.等待对方将数据发送到操作系统的缓冲区 2.recv从网卡缓冲区获取数据send 1.send将数据发送到自己网卡到写缓冲去 2.操作系统调用网卡将缓冲区数据发送给对方网卡到缓冲区--------------------------------------------------------------------------------tcp,是面向连接的面向流的,提供高可靠性,收发两端都要一一成对的socket,发送端为了将多个发往接收端的包,更有效的发往对方,使用了优化算法(Nagle算法),将多次间隔较小且数据量小的数据合并成一个大的数据块,然后进行封包,这样接收端就难于分辨出来,必须提供科学的拆包机制,即面向流的。通信是无消息保护边界的
解决粘包:每次发送数据的时候,将消息划分为头部(固定四个字节的长度)和数据部分发送数据:先发送数据的长度 再发送数据 或拼接起来发送接收数据:先读4字节的长度,再根据长度获取数据struct 模块可以将数值转换为固定的四个字节 范围是-2147483648到2147483648struct.pack('i',199) # i表示是int类型struct.unpack('i',v1)
import subprocessimport socketimport struct#打包模块phone = socket.socket(socket.AF_INET,socket.socket_STREAM)phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)phone.bind(('127.0.0.1',22))phone.listen(5)while True: #连接循环conn, client_addr = phone.accept()while True:data = conn.recv(1024) # 接收客户端的命令if not data: break# 执行命令并得到结果,正确结果放到stdout=suprocess.PIPE,错误放到stderr= subprocess.PIPEobj = subprocess.Popen(data,shell = True,stdout=suprocess.PIPE,stderr= subprocess.PIPE)stdout = obj.stdout.read()stderr=obj.stderr.read()total_size = (len(stdout)+str(stderr))##包的长度conn.send(str(total_size).encode('utf-8')) #发送包的长度#制作固定长度的报头head = struct.pack('i',total_size)#把报头发给客户端conn.send(head)#发送真实的数据conn.send(stdout+stderr)conn.close()phone.close()--------------------------------------------------------------------------import socketphone = socket.socket(socket.AF_INET,scoket.socket_STREAM)phone.connect(('127.0.0.1',22))while True:msg = input(">>>>").strip()if not mag:continuephone.send(msg.encode('utf-8'))#接收报头obj = phone.recv(4)#从报头中解析出真实数据的描述信息(数据的长度)total_size = struct.unpack('i',obj) #i是固定的,int类型 4个字节,unpack是解包,字节转换为数字#接收真实的数据recv_size = 0recv_data = b"while recv_size < total_sizeres = phone.recv(1024)recv_data += resrecv_size +=len(res)print(recv_data.decode('utf-8'))phone.close
最终版import subprocessimport socketimport structphone = socket.socket(socket.AF_INET,socket.socket_STREAM)phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)phone.bind(('127.0.0.1',22))phone.listen(5)while True: #连接循环conn, client_addr = phone.accept()while True:data = conn.recv(1024) # 接收客户端的命令if not data: break# 执行命令并得到结果,正确结果放到stdout=suprocess.PIPE,错误放到stderr= subprocess.PIPEobj = subprocess.Popen(data,shell = True,stdout=suprocess.PIPE,stderr= subprocess.PIPE)stdout = obj.stdout.read()stderr=obj.stderr.read()total_size = (len(stdout)+str(stderr))##包的长度conn.send(str(total_size).encode('utf-8')) #发送包的长度#制作固定长度的报头head_dic = {"file_name":"a.txt",'md5':'Xdfdfdfdfd','total_size':len(stdot)+len(stderr)}header_json = ison.dumps(header_dic)header_byes = header_json.encode('utf-8')struct.pack("i",len(header_bytes))#发送报头长度conn.send(header_bytes)#把报头发给客户端conn.send(head_bytes)#发送真实的数据conn.send(stdout+stderr)conn.close()phone.close()--------------------------------------------------------------------------import socketphone = socket.socket(socket.AF_INET,scoket.socket_STREAM)phone.connect(('127.0.0.1',22))while True:msg = input(">>>>").strip()if not mag:continuephone.send(msg.encode('utf-8'))#接收报头长度obj = phone.recv(4)header_size = struct.unpack('i',obj)[0]#收报头header_bytes = phone.recv(header_size)#从报头中解析出真实的数据表述信息header_json = header_bytes.decode('utf-8')header_dic = json.loads(header_json)total_size = header_dic['total_size']#接收真实的数据recv_size = 0recv_data = b"while recv_size < total_sizeres = phone.recv(1024)recv_data += resrecv_size +=len(res)print(recv_data.decode('utf-8'))phone.close
文件传输功能
文件传输功能import subprocessimport socketimport structphone = socket.socket(socket.AF_INET,socket.socket_STREAM)phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)phone.bind(('127.0.0.1',22))phone.listen(5)while True: #连接循环conn, client_addr = phone.accept()while True:data = conn.recv(1024) # 接收客户端的命令if not data: breakcmds = data.decode('utf-8').split()filename = cmds[1]stdout = obj.stdout.read()stderr=obj.stderr.read()total_size = (len(stdout)+str(stderr))##包的长度conn.send(str(total_size).encode('utf-8')) #发送包的长度#制作固定长度的报头head_dic = {"file_name":"a.txt",'md5':'Xdfdfdfdfd','file_size':os.oath.getsize(filename)}header_json = ison.dumps(header_dic)header_byes = header_json.encode('utf-8')struct.pack("i",len(header_bytes))#发送报头长度conn.send(header_bytes)#把报头发给客户端conn.send(head_bytes)#发送真实的数据with open(filename.'rb') as f:for line in f:conn.send(line)conn.close()phone.close()--------------------------------------------------------------------------import socketphone = socket.socket(socket.AF_INET,scoket.socket_STREAM)phone.connect(('127.0.0.1',22))while True:msg = input(">>>>").strip()if not mag:continuephone.send(msg.encode('utf-8'))#接收报头长度obj = phone.recv(4)header-size = struct.unpack('i',obj)[0]#收报头header_bytes = phone.recv(header_size)#从报头中解析出真实的数据表述信息header_json = header_bytes.decode('utf-8')header_dic = json.loads(header_json)file_size = header_dic['file_size']filename = header_dic['filname']#接收真实的数据with open(filename,'wb') as f:recv_size = 0while recv_size < file_sizeline = phone.recv(1024)f.write(line)recv_size +=len(line)phone.close
UDP 套接字
服务端from socket import *server = socket(AF_INET,SOCK_DGRAM)server.bind('127.0.0.1',8080) # udp是无连接的while True:res1 = server.recvfrom(1024)res2 = server.recvfrom(1024)server.close------------------------------------------------客户端from socket import *client = socket(AF_INET,SOCKET_DGRAM)client.sendto(b'hello',('127.0.0.1',8080))client.sendto(b'word',('127.0.0.1',8080))client.close()
socketserver模块实现并发
import socketserverclass Myserver(socketserver.BaseRequestHandler):def handle(self):# 并发的业务逻辑代码 self.request是客户端的套接字对象while True:client_data = self.request.recv(1024)if client_data.decode('utf8') == 'exit':print('断开连接')breakprint('接受数据', str(client_data, 'utf_8'))response = input('响应数据')self.request.sendall(bytes(response, 'utf8'))self.request.close()# 创建socket对象以及 bind() listen()server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), Myserver)# accept() 等待用户连接server.serve_forever()
非阻塞和Io多路复用
import select # IO多路复用import socketserver=socket.socket(socket.AF_INET,socket.socl_STREAM)server.setblocking(False) # 非阻塞server.bind('127.0.0.1',8090)server.listen(5)inputs = [server,] #socket 对象列表,最多只能放1024个socket对象while True:'''select.select(),把socket对象当作第一个参数传进去,0.05就是在select里面用0.05的时间检测socket对象列表是否有人发连接或数据,如果没有人发起连接 r = [],当有人发起连接,r = [发生变化的那个连接],且r只存放当前有变化的连接w存放成功的连接e检测异常情况,是否有异常'''#io多路复用r,w,e = select.select(inputs,[],[],0.5)foe sock in r:if sock == server:conn,addr = sock.accept() #接收新连接# conn是客户端连接也会放到inputs列表 addr是地址print('有连接')#将客户端发起的连接放进inputs列表inputs.append(conn)else:data = sock.recv(1024)if data:print('收到消息',data)else:#关闭连接inputs.remove(sock)
import socketimport selectimport uuidimport osclient_list = [] # socket对象列表for i in range(5):client = socket.socket()client.setblocking(False)try:# 连接百度,虽然有异常BlockingIOError,但向还是正常发送连接的请求client.connect(('47.98.134.86', 80))except BlockingIOError as e:passclient_list.append(client)recv_list = [] # 放已连接成功,且已经把下载图片的请求发过去的socketwhile True:# w = [第一个socket对象,]# r = [socket对象,]r, w, e = select.select(recv_list, client_list, [], 0.1)for sock in w:# 连接成功,发送数据# 下载图片的请求sock.sendall(b"GET /nginx-logo.png HTTP/1.1\r\nHost:47.98.134.86\r\n\r\n")recv_list.append(sock) #放如r中检测是否有返回数据client_list.remove(sock) # w中检测的是连接成功的对象,无法检测有变化的连接for sock in r:# 数据发送成功后,接收的返回值(图片)并写入到本地文件中data = sock.recv(8196)content = data.split(b'\r\n\r\n')[-1]random_file_name = "{}.png".format(str(uuid.uuid4()))with open(os.path.join("images", random_file_name), mode='wb') as f:f.write(content)recv_list.remove(sock)if not recv_list and not client_list:break
"""基于IO多路复用 + 非阻塞的特性,无论编写socket的服务器和客户端都可以提升性能。IO多路复用,检测socket对象是否有变化,(连接是否成功,是否有数据到来)非阻塞:socket的connect recv过程不再需要等待IO多路复用只能用来监听IO对象是否发生变化,常见的有文件是否可以读写,电脑终端设备的输入和输出"""
os.sep 根据系统判断是/还是\
