面向对象的程序设计
面向过程和面向对象程序设计的区别:
1、面向过程:
优点:极大的降低了程序的复杂程度
缺点:一套流水线或者流程就是用来解决一个问题,可扩展性很差
应用场景:一旦完成基本很少改变的场景,后期基本上不需要进行扩展
2、面向对象:
优点:解决了程序的扩展性,对某一个对象单独修改,会立刻反映到整个体系中
缺点:可控性差,无法像面向过程的程序设计流水线式的可以很精确的预测问题的处理流程与结果
应用场景:需求经常变化、扩展性很强的软件
类和对象
类:从一组对象中提取相似的部分,是数据(即变量)与函数的结合体,数据属性共享给所有的实例(即对象),所有的函数都以绑定的形式提供给实例使用,
绑定后,实例会把自己本身当作一个参数传递给所有函数的第一个参数(self)
功能:1、实例化
2、属性引用,包括数据属性和函数属性(调用方法:"类名."的形式调用)
对象:类实例化得到的结果就是对象(即实例)
功能:属性引用,实例本身只拥有数据属性,也可以用"对象名."的方式调用类的属性
对象之间的交互
1 class A: 2 camp = "A" 3 def __init__(self,name,gongji,shengming): 4 self.name = name 5 self.gongji = gongji 6 self.shengming = shengming 7 def attack(self,enemy): 8 print("开始攻击") 9 class B: 10 camp = "B" 11 def __init__(self,name,gongji,shengming): 12 self.name = name 13 self.gongji = gongji 14 self.shengming = shengming 15 def attack(self,enemy): 16 print("开始攻击") 17 enemy.shengming -= self.gongji #self=b enemy=a 18 a = A("A",82,100) 19 b = B("B",45,200) 20 b.attack(a) #b攻击了a一下,两个类进行了交互 21 print(a.shengming) 22 23 24 25 结果: 26 开始攻击 27 55
继承与派生
继承:解决代码的重复性,父类不可以调用子类的参数,子类对父类同名参数的修改不会影响父类参数的变化;还可以做出自己的变化或者扩展
1 class A: 2 pass 3 class B(A): 4 pass 5 6 print(A.__base__) 7 print(B.__base__) 8 9 10 结果: 11 <class 'object'> 12 <class '__main__.A'>
继承的顺序:
1 class A(object): 2 def test(self): 3 print('from A') 4 5 class B(A): 6 def test(self): 7 print('from B') 8 9 class C(A): 10 def test(self): 11 print('from C') 12 13 class D(B): 14 def test(self): 15 print('from D') 16 17 class E(C): 18 def test(self): 19 print('from E') 20 21 class F(D,E): 22 # def test(self): 23 # print('from F') 24 pass 25 f1=F() 26 f1.test() 27 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 28 29 30 结果: 31 from D 32 (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
新式类继承顺序:F->D->B->E->C->A
经典类继承顺序:F->D->B->A->E->C
python3中统一都是新式类
pyhon2中才分新式类与经典类
===================================
子类调用父类的方法:
方法一:
1 class Vehicle: #定义交通工具类 2 Country='China' 3 def __init__(self,name,speed,load,power): 4 self.name=name 5 self.speed=speed 6 self.load=load 7 self.power=power 8 9 def run(self): 10 print('开动啦...') 11 12 class Subway(Vehicle): #地铁 13 def __init__(self,name,speed,load,power,line): 14 Vehicle.__init__(self,name,speed,load,power) 15 self.line=line 16 17 def run(self): 18 print("欢迎乘坐%s,本%s车时速%s,每节车厢人数上限:%s"%(self.name,self.power,self.speed,self.load)) 19 print('地铁%s号线欢迎您' %self.line) 20 Vehicle.run(self) 21 22 line13=Subway('中国地铁','180m/s','1000人/箱','电',13) 23 line13.run() 24 print(line13.line) 25 26 27 结果: 28 欢迎乘坐中国地铁,本电车时速180m/s,每节车厢人数上限:1000人/箱 29 地铁13号线欢迎您 30 开动啦... 31 13
方法二:
1 class Vehicle: #定义交通工具类 2 Country='China' 3 def __init__(self,name,speed,load,power): 4 self.name=name 5 self.speed=speed 6 self.load=load 7 self.power=power 8 9 def run(self): 10 print('开动啦...') 11 12 class Subway(Vehicle): #地铁 13 def __init__(self,name,speed,load,power,line): 14 #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self) 15 super().__init__(name,speed,load,power) 16 self.line=line 17 18 def run(self): 19 print("欢迎乘坐%s,本%s车时速%s,每节车厢人数上限:%s"%(self.name,self.power,self.speed,self.load)) 20 print('地铁%s号线欢迎您' %self.line) 21 super(Subway,self).run() 22 23 line13=Subway('中国地铁','180m/s','1000人/箱','电',13) 24 line13.run() 25 print(line13.line) 26 27 28 结果: 29 欢迎乘坐中国地铁,本电车时速180m/s,每节车厢人数上限:1000人/箱 30 地铁13号线欢迎您 31 开动啦... 32 13
======================================================
类的组合:也是解决代码的重复性,在一个类中以另一个类的对象作为数据属性
1 class A: 2 def __init__(self,name): 3 self.name = name 4 self.birth = B(1999,1,22) 5 6 class B: 7 def __init__(self,year,mon,day): 8 self.year = year 9 self.mon = mon 10 self.day = day 11 12 a = A("syan") 13 print(a.birth.year) 14 15 结果:1999
1 class A: 2 def __init__(self,name,birth): 3 self.name = name 4 self.birth = birth 5 6 class B: 7 def __init__(self,year,mon,day): 8 self.year = year 9 self.mon = mon 10 self.day = day 11 12 a = A("syan",B(1999,1,22)) 13 print(a.birth.year) 14 15 结果:1999
注:A中self.birth数据属性直接调用B类中的一个数据属性,第二种方法常用
应用场景:需要调用相同数据属性(代码)时
接口与归一化设计:
声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
抽象类:只用于被继承,不能实例化
与接口的区别:其功能必须要子类来实现
import abc #利用abc模块实现抽象类 class A(metaclass=abc.ABCMeta): @abc.abstractmethod #强制使继承类重新定义该功能,否则报错 def read(self): pass class B(A): def read(self): pass b = B()
多态与多态性:
多态:是一类事物有多种形态
多态性:是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数
优点: 1.增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
2.增加了程序额可扩展性
封装的两个层面:
1 class A: 2 def __init__(self,name,money): 3 self.name = name 4 self.__money = money #__money就是将该参数进行隐藏 5 6 def get_money(self): 7 print(self.__money) #被隐藏的参数可以在该类的内部被调用(_A__money) 8 9 a = A("syan",10000) 10 a.get_money() #如果没有get_money该函数调用,则此处无法查看 11 12 结果:10000
1 class A: 2 x = 1 3 def __init__(self,name,money): 4 self.name = name 5 self.__money = money 6 7 def get_money(self): 8 print(self.__money) 9 10 a = A("syan",10000) 11 print(a.__dict__) 12 13 print(A.__dict__) 14 15 结果: 16 {'name': 'syan', '_A__money': 10000} 17 18 {'__module__': '__main__', 'x': 1, '__init__': <function A.__init__ at 0x102245378>, 'get_money': <function A.get_money at 0x102245488>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
1 class A: 2 x = 1 3 def __init__(self,name,money): 4 self.name = name 5 self.__money = money 6 7 def get_money(self): 8 print(self.__money) 9 self.__spam() #直接调用类中被隐藏的参数 10 11 def __spam(self): 12 print("in spam") 13 14 a = A("syan",10000) 15 a.get_money() 16 17 结果: 18 10000 19 in spam
隐藏参数只在定义时才会执行
继承层面的封装:
1 class A: 2 def spam(self): 3 print("a.spam") 4 def test(self): 5 print("a.test") 6 self.spam() 7 class B(A): 8 def spam(self): 9 print("b.spam") 10 11 b = B() 12 b.test() 13 14 结果: 15 a.test 16 b.spam 17 18 如果就想调用A里面的spam: 19 20 class A: 21 def __spam(self): 22 print("a.spam") 23 def test(self): 24 print("a.test") 25 self.__spam() 26 class B(A): 27 def spam(self): 28 print("b.spam") 29 30 b = B() 31 b.test() 32 33 结果: 34 a.test 35 a.spam
特性(property):property是一个内置函数
特性是用来提供接口的一种方式
使用方法:
1 import math 2 class A: 3 def __init__(self,radius): #radius圆的半径 4 self.radius = radius 5 @property 6 def area(self): 7 return math.pi * self.radius ** 2 #计算面积 8 @property 9 def perimeter(self): 10 return 2 * math.pi * self.radius #计算周长 11 12 a = A(10) 13 print(a.area) #如果没有@property,则调用方式则需要加(),即print(a.area()) 14 print(a.perimeter) #如果没有@property,则调用方式则需要加(),即print(a.perimeter()) 15 16 结果: 17 314.1592653589793 18 62.83185307179586
property的set用法:
1 class A: 2 def __init__(self,name): 3 self.__name = name 4 @property 5 def name(self): 6 return self.__name 7 8 a = A("syan") 9 print(a.name) 10 结果:syan 11 12 ------------------------------------------------------------------------- 13 14 class A: 15 def __init__(self,name): 16 self.__name = name 17 @property 18 def name(self): 19 return self.__name 20 @name.setter 21 def name(self,value): 22 print("in name.setter") 23 24 a = A("syan") 25 a.name = 10 26 print(a.name) 27 结果: 28 in name.setter 29 syan 30 31 ---------------------------------------------------------------------- 32 33 class A: 34 def __init__(self,name): 35 self.__name = name 36 @property 37 def name(self): 38 return self.__name 39 @name.setter 40 def name(self,value): 41 # print("in name.setter") 42 self.__name = value 43 44 a = A("syan") 45 a.name = 10 46 print(a.name) 47 结果:10
封装与扩展性:
1 class Room: 2 def __init__(self,name,owner,width,length,high): 3 self.name=name 4 self.owner=owner 5 self.__width=width 6 self.__length=length 7 self.__high=high 8 def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积 9 return self.__width * self.__length
1 r1=Room('卧室','egon',20,20,20) 2 print(r1.tell_area()) #使用者调用接口tell_area 3 4 结果:400
扩展:
1 class Room: 2 def __init__(self,name,owner,width,length,high): 3 self.name=name 4 self.owner=owner 5 self.__width=width 6 self.__length=length 7 self.__high=high 8 def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了 9 return self.__width * self.__length * self.__high
1 r1=Room('卧室','egon',20,20,20) 2 print(r1.tell_area()) #使用者依然使用原来的接口就可以实现新的功能 3 4 结果:8000
专为类使用的静态方法和类方法:
静态方法应用(staticmethod)
被staticmethod装饰过的函数,只是名义上属于某个类,实际不受类的控制,相当于独立运行
如果有self,则self只是这个函数的形参,如下面的now函数
1 import time 2 class Data: 3 def __init__(self,year,mon,day): 4 self.year = year 5 self.mon = mon 6 self.day = day 7 @staticmethod 8 def now(): 9 t = time.localtime() 10 return Data(t.tm_year,t.tm_mon,t.tm_mday) 11 12 d = Data(1991,1,1) 13 d1 = Data.now() 14 print(d.year) 15 print(d1.year) 16 17 18 结果: 19 1991 20 2017
类方法应用(classmethod)
只能访问类变量,不能访问或调用实例变量
1 ------------------------------------------ 2 class A: 3 def __init__(self,name): 4 self.name = name 5 def __str__(self): 6 print("---->str") 7 return "name:%s"%self.name 8 9 a = A("syan") 10 print(a) 11 结果: 12 ---->str 13 name:syan 14 15 注:__str__(self)只有在打印的时候才会触发执行,并且返回值必须是字符串的形式 16 ------------------------------------------------------- 17 18 import time 19 class Data: 20 def __init__(self,year,mon,day): 21 self.year = year 22 self.mon = mon 23 self.day = day 24 @classmethod 25 def now(cls): #那个类调用,cls就等于那个类 26 t = time.localtime() 27 return cls(t.tm_year,t.tm_mon,t.tm_mday) 28 29 d = Data(1991,1,1) 30 print(d.year) 31 d1 = Data.now() 32 print(d1.mon) 33 结果: 34 1991 35 3
静态方法和类方法的区别
1 import time 2 class Data: 3 def __init__(self,year,mon,day): 4 self.year = year 5 self.mon = mon 6 self.day = day 7 @staticmethod 8 def now(): 9 t = time.localtime() 10 return Data(t.tm_year,t.tm_mon,t.tm_mday) 11 12 class B(Data): 13 def __str__(self): 14 return "year:%s mon:%s day:%s"%(self.year,self.mon,self.day) 15 16 17 b = B.now() 18 print(b) 19 结果: 20 <__main__.Data object at 0x102243be0>
1 import time 2 class Data: 3 def __init__(self,year,mon,day): 4 self.year = year 5 self.mon = mon 6 self.day = day 7 @classmethod 8 def now(cls): #谁调用Data,cls就等于谁 9 t = time.localtime() 10 return cls(t.tm_year,t.tm_mon,t.tm_mday) 11 12 class B(Data): 13 def __str__(self): 14 return "year:%s mon:%s day:%s"%(self.year,self.mon,self.day) 15 16 b = B.now() 17 print(b) 18 19 结果: 20 year:2017 mon:3 day:7
面向对象的开发过程:
1.面向对象分析(object oriented analysis ,OOA)
软件工程中的系统分析阶段,要求分析员和用户结合在一起,对用户的需求做出精确的分析和明确的表述,从大的方面解析软件系统应该做什么,而不是怎么去做。面向对象的分析要按照面向对象的概念和方法,在对任务的分析中,从客观存在的事物和事物之间的关系,贵南出有关的对象(对象的‘特征’和‘技能’)以及对象之间的联系,并将具有相同属性和行为的对象用一个类class来标识。
建立一个能反映这是工作情况的需求模型,此时的模型是粗略的。
2 面向对象设计(object oriented design,OOD)
根据面向对象分析阶段形成的需求模型,对每一部分分别进行具体的设计。
首先是类的设计,类的设计可能包含多个层次(利用继承与派生机制)。然后以这些类为基础提出程序设计的思路和方法,包括对算法的设计。
在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述
3 面向对象编程(object oriented programming,OOP)
根据面向对象设计的结果,选择一种计算机语言把它写成程序,可以是python
4 面向对象测试(object oriented test,OOT)
在写好程序后交给用户使用前,必须对程序进行严格的测试,测试的目的是发现程序中的错误并修正它。
面向对的测试是用面向对象的方法进行测试,以类作为测试的基本单元。
5 面向对象维护(object oriendted soft maintenance,OOSM)
正如对任何产品都需要进行售后服务和维护一样,软件在使用时也会出现一些问题,或者软件商想改进软件的性能,这就需要修改程序。
由于使用了面向对象的方法开发程序,使用程序的维护比较容易。
因为对象的封装性,修改一个对象对其他的对象影响很小,利用面向对象的方法维护程序,大大提高了软件维护的效率,可扩展性高。
在面向对象方法中,最早发展的肯定是面向对象编程(OOP),那时OOA和OOD都还没有发展起来,因此程序设计者为了写出面向对象的程序,还必须深入到分析和设计领域,尤其是设计领域,那时的OOP实际上包含了现在的OOD和OOP两个阶段,这对程序设计者要求比较高,许多人感到很难掌握。
现在设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这个5个阶段的工作不是由一个人从头到尾完成的,而是由不同的人分别完成,这样OOP阶段的任务就比较简单了。程序编写者只需要根据OOd提出的思路,用面向对象语言编写出程序既可。
在一个大型软件开发过程中,OOP只是很小的一个部分。
对于全栈开发的你来说,这五个阶段都有了,对于简单的问题,不必严格按照这个5个阶段进行,往往由程序设计者按照面向对象的方法进行程序设计,包括类的设计和程序的设计
面向对象进阶:
1 class Foo: 2 """ 描述类信息,这是用于看片的神奇 """ 3 4 def func(self): 5 pass 6 7 print Foo.__doc__ 8 结果:描述类信息,这是用于看片的神奇
1 在lib目录下创建一个名字为c的类: 2 class C: 3 def __init__(self): 4 self.name = 'syan' 5 在另外一个程序里导入该模块: 6 from lib.aa import C 7 8 obj = C() 9 print(obj.__module__) # 输出 lib.aa,即:输出模块 10 print(obj.__class__) # 输出 lib.aa.C,即:输出类 11 结果: 12 lib.aa 13 <class 'lib.aa.C'>
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
1 __import__("lib.aa")等同于from lib import aa
1 class Foo: 2 def __init__(self,name): 3 self.name = name 4 5 def __call__(self, *args, **kwargs):#定义此函数,就能使实例变成可调用对象(即可加括号执行) 6 print("---->call") 7 8 f = Foo("syan") 9 print(f.name) 10 f() 11 12 结果: 13 syan 14 ---->call
1 class Foo: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 def __getitem__(self, item): #当实例用中括号方式执行当时候触发该函数执行 6 print("in----->getitem") 7 print(self.__dict__) # 8 print(item) #实例中括号中的参数作为字典的key传给item 9 return self.name,self.age,self.__dict__[item] #使用return可以返回自己想要的结果,并以元组的形式返回结果 10 11 def __setitem__(self, key, value):#给此类设置变量的时候会触发该函数运行 12 print("in------>setiten") 13 self.__dict__[key] = value #以字典的形式列出此类函数中所有的参数 14 15 def __delitem__(self, key): #以del obj[key]的方式删除变量时触发该函数运行 16 print("del obj[key]时,我执行") 17 self.__dict__.pop(key) #删除del obj[key]所指定的值 18 19 print("---------getitem用法---------") 20 f = Foo("syan",22) 21 print(f["name"]) 22 print("******************************") 23 print("---------setitem用法---------") 24 f["x"] = 1 25 print(f.__dict__) 26 print("******************************") 27 print("---------delitem用法---------") 28 del f["x"] 29 print(f.__dict__) 30 31 32 结果: 33 ---------getitem用法--------- 34 in----->getitem 35 {'name': 'syan', 'age': 22} 36 name 37 ('syan', 22, 'syan') 38 ****************************** 39 ---------setitem用法--------- 40 in------>setiten 41 {'name': 'syan', 'age': 22, 'x': 1} 42 ****************************** 43 ---------delitem用法--------- 44 del obj[key]时,我执行 45 {'name': 'syan', 'age': 22}
1 class MyType(type): 2 def __init__(self,*args,**kwargs):#__new__函数执行完毕后,会去执行MyType的__init__函数 3 print("MyType--->__init__") 4 5 def __call__(self,*args,**kwargs):#当Foo实例加上括号执行的时候,会触发MyType类中的__call__函数的运行 6 print("MyType--->__call__") 7 obj = self.__new__(self)#相当于Foo中的__new__函数,生成一个内存地址 8 self.__init__(obj,*args,**kwargs)#相当于Foo中的__init__函数,将上面的内存地址作为参数传进来 9 return obj#最后将结果返回给实例f 10 11 def __new__(cls,*args,**kwargs):#MyType类会首先运行__new__函数 12 print("MyType---->__new__") 13 obj = type.__new__(cls,*args,**kwargs)# 14 return obj 15 16 class Foo(metaclass=MyType):#程序启动后通过metaclass执行MyType类,在python2中的写法:__metaclass__ = MyType 17 def __init__(self,name): 18 self.name = name 19 print("Foo--->__init__") 20 21 f = Foo("syan")#当Foo实例加上括号执行的时候,会触发MyType类中的__call__函数的运行
1、程序启动后通过metaclass执行MyType类
2、MyType类会首先运行__new__函数
__new__函数执行的时候会生成一个内存地址,并返回
3、__new__函数执行完毕后,会去执行MyType的__init__函数,执行完毕后就会生成一个Foo的实例
4、当Foo实例加上括号执行的时候,会触发MyType类中的__call__函数的运行
5、MyType类中__call__函数运行的时候首先会执行Foo中的__new__函数
6、将Foo中__new__函数生成的内存地址当作参数传给Foo类中的__init__函数
7、最后将结果返回给实例f
1 print("globals----->:",globals())#查看程序启动前系统的变量 2 class MyType(type): 3 def __init__(self, *args, **kwargs): 4 print("MyType--->__init__",self, *args, **kwargs) 5 6 def __call__(self, *args, **kwargs): 7 print("MyType---->__call__",self, *args, **kwargs) 8 obj = self.__new__(self) 9 print("MyType---->obj:",obj) 10 self.__init__(obj, *args, **kwargs) 11 return obj 12 13 def __new__(cls, *args, **kwargs): 14 print("MyType---->__new__",cls, *args, **kwargs) 15 obj = type.__new__(cls, *args, **kwargs) 16 return obj 17 18 class Foo(object,metaclass=MyType):#metaclass原类 19 def __init__(self,name): 20 self.name = name 21 print("Foo--->__init__") 22 23 print("globals----->:",globals())#查看程序运行完系统的变量 24 25 f = Foo("syan") 26 print(f) 27 print(f.name) 28 29 30 31 结果: 32 globals----->: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x1007d9518>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/sos/PycharmProjects/python/2017-3-11/类的创建过程.py', '__cached__': None} 33 MyType---->__new__ <class '__main__.MyType'> Foo (<class 'object'>,) {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x102a45598>} 34 MyType--->__init__ <class '__main__.Foo'> Foo (<class 'object'>,) {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x102a45598>} 35 globals----->: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x1007d9518>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/sos/PycharmProjects/python/2017-3-11/类的创建过程.py', '__cached__': None, 'MyType': <class '__main__.MyType'>, 'Foo': <class '__main__.Foo'>} 36 MyType---->__call__ <class '__main__.Foo'> syan 37 MyType---->obj: <__main__.Foo object at 0x102a43780> 38 Foo--->__init__ 39 <__main__.Foo object at 0x102a43780> 40 syan
异常处理:
1 try: 2 names = ["syan","bob"] 3 info = {1:11} 4 names[3] 5 except ValueError as e: 6 print(e) 7 except ImportError as e: 8 print(e) 9 except Exception as e:#Exception(BaseException)万能异常一般放在异常调试的最后,用意捕捉未知的错误 10 print("未知错误:",e) 11 exit() #程序到此结束,则最后"print("程序执行到一般有了结果,退出后,就不执行")"不会在执行 12 else:#程序没有任何错误的时候会触发以下代码 13 print("什么错误都没有") 14 finally: 15 print("无论程序有什么结果都执行")#即便程序执行到一半退出,也会执行 16 17 print("程序执行到一般有了结果,退出后,就不执行")
1 class MyError(Exception):#用类的方式自定义异常 2 def __init__(self,error,): 3 self.error = error 4 try: 5 raise MyError("My Error")#触发某个异常 6 except MyError as e: #自定义异常 7 print(e) 8 9 结果:My Error
经常用于处理业务逻辑异常
断言:
assert 1 == 1
条件不为真就报错
此条件必须成立程序才能继续执行,如果此条件不成立,程序终止
例如:assert user_is_loggined,表示用户必须登陆才能进行其他操作
反射:
1 class Foo: 2 def __init__(self,name): 3 self.name = name 4 5 def func(self): 6 print("in---->func") 7 8 print(hasattr(Foo,"func")) #判断Foo类中有没有一个func的函数名字 9 print("*******************************") 10 f = Foo("syan") 11 print(hasattr(f,"x")) #判断一个实例里面有没有一个x的属性(参数) 12 print("*******************************") 13 f.x = 2 #设置一个x的属性 14 print(getattr(f,"x",1)) #从一个实例中获取一个x的属性,如果存在则返回对应的值,不存在则返回后面设置的值 15 print(getattr(f,"y","没找到y")) 16 print(getattr(f,"func")) #可以获得func函数的内存地址,使用if判断,如果存在,则可以执行什么样的操作 17 print("*******************************") 18 setattr(f,"z","z的值") #给实例增加一个属性 19 print(f.__dict__) 20 print("*******************************") 21 delattr(f,"z") #删除实例的某个属性 22 print(f.__dict__) 23 print("********类和实例的字典内容*********") 24 print(Foo.__dict__) 25 print(f.__dict__) 26 27 28 结果: 29 True 30 ******************************* 31 False 32 ******************************* 33 2 34 没找到y 35 <bound method Foo.func of <__main__.Foo object at 0x1005db4e0>> 36 ******************************* 37 {'name': 'syan', 'x': 2, 'z': 'z的值'} 38 ******************************* 39 {'name': 'syan', 'x': 2} 40 ********类和实例的字典内容********* 41 {'__module__': '__main__', '__init__': <function Foo.__init__ at 0x102a60378>, 'func': <function Foo.func at 0x102a60488>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None} 42 {'name': 'syan', 'x': 2}
反射应用场景:
1、当调用其他程序员的程序时,即不想改动原有代码,又想实现自己想实现的功能,就可以用if判断的方法,判断该程序员的程序中是否有自己需要的功能,如果有则可以如何处理,如果没有则可以如何处理,这样即便对方改动了源代码,也不影响自己的功能
2、可以动态导入模块,即可以根据用户输入的字符串来导入相应的模块(__import__)
网络(套接字)编程:
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字
面向锁的套接字方法
s.setblocking() 设置套接字的阻塞与非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 得到阻塞套接字操作的超时时间
面向文件的套接字的函数
s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字相关的文件
1 import socket 2 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 4 phone.bind(('127.0.0.1',8080)) 5 phone.listen(5) 6 while True:#链接循环 7 conn,addr = phone.accept() 8 print("client:",addr) 9 while True: #通讯循环 10 try: 11 data = conn.recv(1024) #接收信息 12 if not data:break #针对linux和mac,客户端断开链接的异常处理 13 print('from client msg:%s'%data) 14 conn.send(data.upper()) #发送信息 15 except Exception: 16 break 17 conn.close() 18 phone.close()
1 import socket 2 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 client.connect(('127.0.0.1',8080)) 4 while True: 5 msg = input('>>:') 6 if not msg:continue #当客户端输入为空,则跳出 7 client.send(msg.encode('utf-8')) 8 data = client.recv(1024) 9 print(data) 10 client.close()
远程执行命令:
1 import socket,subprocess 2 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 4 phone.bind(('127.0.0.1',8080)) 5 phone.listen(5) 6 while True:#链接循环 7 conn,addr = phone.accept() 8 print("client:",addr) 9 while True: #通讯循环 10 try: 11 cmd = conn.recv(1024) 12 if not cmd:break 13 print('from client msg:%s'%cmd) 14 res = subprocess.Popen(cmd.decode("utf-8"),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) 15 err = res.stderr.read() 16 if err: 17 back_msg = err 18 else: 19 back_msg = res.stdout.read() 20 conn.send(back_msg) 21 except Exception: 22 break 23 conn.close() 24 phone.close()
1 import socket 2 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 client.connect(('127.0.0.1',8080)) 4 5 while True: 6 cmd = input(">>>:").strip() 7 if not cmd:continue 8 client.send(cmd.encode("utf-8")) 9 res = client.recv(1024) 10 print(res.decode("utf-8")) 11 client.close()
粘包问题处理:
1 import socket,subprocess 2 import struct #struct解决粘包问题的模块 3 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 5 phone.bind(('127.0.0.1',8080)) 6 phone.listen(5) 7 while True:#链接循环 8 conn,addr = phone.accept() 9 print("client:",addr) 10 while True: #通讯循环 11 try: 12 cmd = conn.recv(1024) 13 if not cmd:break 14 print('from client msg:%s'%cmd) 15 res = subprocess.Popen(cmd.decode("utf-8"),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) 16 err = res.stderr.read() 17 if err: 18 back_msg = err 19 else: 20 back_msg = res.stdout.read() 21 22 conn.send(struct.pack("i",len(back_msg)))#发数据之前封装一个抱头,将数据包以4个字节的长度进行打包 23 conn.sendall(back_msg) #sendall表示如果数据过大,则循环调send发送 24 except Exception: 25 break 26 conn.close() 27 phone.close()
1 import socket 2 import struct #struct解决粘包问题的模块 3 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4 client.connect(('127.0.0.1',8080)) 5 6 while True: 7 cmd = input(">>>:").strip() 8 if not cmd:continue 9 client.send(cmd.encode("utf-8")) 10 data = client.recv(4) 11 data_size = struct.unpack("i",data)[0] 12 #如果数据包过大,则需要循环接收并最后拼接 13 recv_size = 0 #初始接收的数据包为0 14 recv_bytes = b'' #定义一个拼接的变量 15 while recv_size < data_size: 16 res = client.recv(1024) #如果recv_size小于数据包的大小,则以1024的长度接收 17 recv_bytes += res #拼接接收到的数据 18 recv_size += len(res) 19 print(recv_bytes.decode("utf-8")) #最后接收的是拼接完整的数据包,即:recv_bytes 20 client.close()
自定制抱头解决粘包:
1 import socket,subprocess 2 import struct #struct解决粘包问题的模块 3 import json 4 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 5 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 6 phone.bind(('127.0.0.1',8080)) 7 phone.listen(5) 8 while True:#链接循环 9 conn,addr = phone.accept() 10 print("client:",addr) 11 while True: #通讯循环 12 try: 13 cmd = conn.recv(1024) 14 if not cmd:break 15 print('from client msg:%s'%cmd) 16 res = subprocess.Popen(cmd.decode("utf-8"),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) 17 err = res.stderr.read() 18 if err: 19 back_msg = err 20 else: 21 back_msg = res.stdout.read() 22 #第一阶段:制作报头 23 head_dic = { #定义一个字典 24 "data_size":len(back_msg) 25 } 26 27 head_json = json.dumps(head_dic)#序列化字典 28 head_bytes = head_json.encode("utf-8")#将序列化好的字典进行编码 29 conn.send(struct.pack("i",len(head_bytes)))#第二阶段:发送报头长度 30 conn.send(head_bytes)#第三阶段:发报头 31 conn.sendall(back_msg)#第四阶段:发送真实数据 32 except Exception: 33 break 34 conn.close() 35 phone.close()
1 import socket 2 import struct 3 import json 4 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 5 client.connect(('127.0.0.1',8080)) 6 7 while True: 8 cmd = input(">>>:").strip() 9 if not cmd:continue 10 client.send(cmd.encode("utf-8")) 11 #收取报头长度 12 head = client.recv(4) 13 # print("----",head) 14 head_size = struct.unpack("i",head)[0]#解出抱头的内容,并取出值,即将要接收抱头的长度 15 16 #收取报头(根据抱头长度) 17 head_bytes = client.recv(head_size)#根据抱头长度收取抱头(bytes类型) 18 head_json = head_bytes.decode("utf-8")#解码抱头 19 head_dic = json.loads(head_json)#反序列化得到传送过来的字典 20 data_size = head_dic["data_size"]#取出真实数据的长度,即服务器端字典里定义的key(data_size) 21 print("---",data_size,"------") 22 #收取真实数据 23 recv_size = 0#初始接收的数据包为0 24 recv_bytes = b''#定义一个拼接的变量 25 while recv_size < data_size: 26 res = client.recv(1024) #如果recv_size小于数据包的大小,则以1024的长度接收 27 recv_bytes += res #拼接接收到的数据 28 recv_size += len(res) 29 print(recv_bytes.decode("utf-8")) #最后接收的是拼接完整的数据包,即:recv_bytes 30 client.close()
多并发:
1 import socketserver 2 3 class Ftpserver(socketserver.BaseRequestHandler): 4 def handle(self): 5 print(self.request) #就是conn 6 print(self.client_address) #就是addr 7 while True:#通讯循环 8 data = self.request.recv(1024) 9 self.request.send(data.upper()) 10 11 12 if __name__ == '__main__': 13 s = socketserver.ThreadingTCPServer(("127.0.0.1",8080),Ftpserver) 14 s.serve_forever()#链接循环
1 from socket import * 2 client = socket(AF_INET,SOCK_STREAM) 3 client.connect(("127.0.0.1",8080)) 4 5 while True: 6 msg = input(">>>:") 7 client.send(msg.encode("utf-8")) 8 data = client.recv(1024) 9 print(data)