面向对象高级
Posted 龚旭1994
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象高级相关的知识,希望对你有一定的参考价值。
一,接口与归一化设计
为什么要用接口?
接口提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数,这么做的意义在于归一化。
什么叫归一化?
就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化的好处:
1.归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
2.归一化使得高层的外部使用者也可以不加区分的处理所有接口兼容的对象集合
(1):就好像linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存,磁盘,网络还是屏幕(当然,对底层设计者,当然也可以区分出‘字符设备’和‘块设备’,然后做出针对性的设计,细致到什么程度,视需求而定)
(2):再比如,我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,等等,他们都实现了汽车接口,这样就好办了,大家只要学会怎么开就行!操作手法(函数调用)都一样。
抽象类((abc.abstractmethod定义父类有read,和write,子类也必须有))
import abc class Interface(metaclass=abc.ABCMeta): all_type=\'file\' @abc.abstractclassmethod def read(self): pass @abc.abstractclassmethod def write(self): pass class Text(Interface): def read(self): pass def write(self): pass t=Text() print(t.all_type)
多态与多态性
多态:同一种事物的多种形态
多态性:可以在不考虑实例类型的前提下使用实例
多态性的好处:增加了程序的扩展性
class Animal: #同一类事物:动物 def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print(\'say hello\') class Dog(Animal): #动物的形态之二:狗 def talk(self): print(\'say wangwang\') class Pig(Animal): #动物的形态之三:猪 def talk(self): print(\'say aoao\') class Cat(Animal): def talk(self): print(\'say miaomiao\') class Bird: def talk(self): print(\'jijiji\') #多态性:可以在不考虑实例类型的前提下使用实例 p1=People() d=Dog() p2=Pig() c=Cat() b=Bird() # p1.talk() # d.talk() # p2.talk() # c.talk() # b.talk() def Talk(animal): animal.talk() #p1.talk() Talk(p1) Talk(d) Talk(p2) Talk(c) Talk(b) #多态性的好处
封装
___隐藏
1.#先看如何隐藏 class Foo: __N=111111 #_Foo__N def __init__(self,name): self.__Name=name #self._Foo__Name=name def __f1(self): #_Foo__f1 print(\'f1\') def f2(self): self.__f1() #self._Foo__f1() f=Foo(\'egon\') # print(f.__N) # f.__f1() # f.__Name # f.f2() #这种隐藏需要注意的问题: #1:这种隐藏只是一种语法上变形操作,并不会将属性真正隐藏起来 # print(Foo.__dict__) # print(f.__dict__) # print(f._Foo__Name) # print(f._Foo__N)
2:这种语法级别的变形,是在类定义阶段发生的,并且只在类定义阶段发生 Foo.__x=123123123123123123123123123123123123123123 print(Foo.__dict__) print(Foo.__x) f.__x=123123123 print(f.__dict__) print(f.__x)
3:在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。 class Foo: def __f1(self): #_Foo__f1 print(\'Foo.f1\') def f2(self): self.__f1() #self._Foo_f1 class Bar(Foo): def __f1(self): #_Bar__f1 print(\'Bar.f1\') b=Bar()b.f2()
#封装不是单纯意义的隐藏 #1:封装数据属性:将属性隐藏起来,然后对外提供访问属性的接口,关键是我们在接口内定制一些控制逻辑从而严格控制使用对数据属性的使用 class People: def __init__(self,name,age): if not isinstance(name,str): raise TypeError(\'%s must be str\' %name) if not isinstance(age,int): raise TypeError(\'%s must be int\' %age) self.__Name=name self.__Age=age def tell_info(self): print(\'<名字:%s 年龄:%s>\' %(self.__Name,self.__Age)) def set_info(self,x,y): if not isinstance(x,str): raise TypeError(\'%s must be str\' %x) if not isinstance(y,int): raise TypeError(\'%s must be int\' %y) self.__Name=x self.__Age=y # p=People(\'gongxu\',18) # p.tell_info() # # # p.set_info(\'Gongxu\',\'19\') # p.set_info(\'Gongxu\',19) # p.tell_info()
2:封装函数属性:为了隔离复杂度
取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱 对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做 隔离了复杂度,同时也提升了安全性 class ATM: def __card(self): print(\'插卡\') def __auth(self): print(\'用户认证\') def __input(self): print(\'输入取款金额\') def __print_bill(self): print(\'打印账单\') def __take_money(self): print(\'取款\') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw()
静态属性
求bmi指数 class People: def __init__(self,name,weight,height): self.name=name self.weight=weight self.height=height @property def bmi(self): return self.weight/(self.height**2) p=People(\'haha\',53,1.63) print(p.bmi
#访问,设置,删除(了解) class Foo: def __init__(self,x): self.__Name=x @property def name(self): return self.__Name @name.setter def name(self,val): if not isinstance(val,str): raise TypeError self.__Name=val @name.deleter def name(self): # print(\'=-====>\') # del self.__Name raise PermissionError f=Foo(\'egon\') # print(f.name) # # # f.name=\'Egon\' # f.name=123123123213 # print(f.name) del f.name print(f.name)
item系列
class Foo: def __getitem__(self, item): print(\'=====>get\') return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key]=value # setattr(self,key,value) def __delitem__(self, key): self.__dict__.pop(key) f=Foo() # f.x=1 # print(f.x) # print(f.__dict__) f[\'x\']=123123123123 # del f[\'x\'] print(f[\'x\'])
打印对象信息__str__
class People: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def __str__(self): #在对象被打印时触发执行 return \'<name:%s age:%s sex:%s>\' %(self.name,self.age,self.sex) p1=People(\'egon\',18,\'male\') p2=People(\'alex\',38,\'male\') print(p1) print(p2)
析构方法__del__
class Foo: def __init__(self,x): self.x=x def __del__(self): #在对象资源被释放时触发 print(\'-----del------\') print(self) f=Foo(100000) del f print(\'=======================>\')
异常处理
报错格式及顺序
try: print("try报错 就匹配执行except类型,没有则报错,Exception指定所有报错") except Exception as e: print(e) else: print("如果try没报错,执行else") finally: print("无论执行try或else 最后都执行finally")
简单实例
#输入1或0 try: s = 1 if s is 0: #print("%s 是错误的"%s) raise NameError("%s 是错误的"%s) #如果引发NameError异常,后面的代码将不能执行 print(len(s)) except TypeError: print("%s 是错误的" % s)
实例
#捕获出错后,提示异常信息,且不退出,捕获错误可多个 #Exception捕获所有错误,写其他错误类型就是捕获具体错误类型,写一种类型加一个except #输入字符串 提示报错并继续 while True: num1 = input("请输入第一位数字:") num2 = input("请输入第二位数字:") try: num1 = int(num1) num2 = int(num2) result = num1 + num2 exit("%s + %s = %s"%(num1,num1,result)) except NameError as e: #捕获具体错误,后操作如下,报错重要性可写入log,也可忽略, print("出现异常,信息如下:") print(e) except Exception as e: #捕获所有错误 print("出现异常,信息如下:") print(e) #invalid literal for int() with base 10: \'asfd\' 这是说第10行输入不是int类型
主动错误模式
try: raise Exception(\'错误了。。。\') except Exception as e: print(e) #错误了。。。
自定义异常模式
class guolm(Exception): #创建类 传参是Exception(所有报错) def __init__(self,msg): self.message = msg def __str__(self): return self.message try: raise guolm("我的异常") #主动异常 except guolm as e: print(e) #我的异常
断言
assert 1 == 1 #条件满足则继续 assert 1 == 2 #条件不满足则报错
错误类型
python2.x捕获异常语法: try: ...some functions... except Exception, e: print(e) python3.x捕获异常语法: try: ...some functions... except Exception as e: print(e) 注意这里 Exception, e 变成了 Exception as e python常见的异常类型 0. Exception 捕获所有错误 1. NameError: 尝试访问一个未申明的变量 >>> v NameError: name \'v\' is not defined 2. ZeroDivisionError: 除数为0 >>> v = 1/0 ZeroDivisionError: int division or modulo by zero 3. SyntaxError: 语法错误 int int SyntaxError: invalid syntax (<pyshell#14>, line 1) 4. IndexError: 索引超出范围 List = [2] >>> List[3] Traceback (most recent call last): File "<pyshell#18>", line 1, in <module> List[3] IndexError: list index out of range 5. KeyError: 字典关键字不存在,试图访问字典里不存在的键 Dic = {\'1\':\'yes\', \'2\':\'no\'} >>> Dic[\'3\'] Traceback (most recent call last): File "<pyshell#20>", line 1, in <module> Dic[\'3\'] KeyError: \'3\' 6. IOError: 输入输出错误:基本上是无法打开文件 >>> f = open(\'abc\') IOError: [Errno 2] No such file or directory: \'abc\' 7. AttributeError: 访问未知对象属性 >>> class Worker: def Work(): print("I am working") >>> w = Worker() >>> w.a Traceback (most recent call last): File "<pyshell#51>", line 1, in <module> w.a AttributeError: \'Worker\' object has no attribute \'a\' 8.ValueError: 数值错误 >>> int(\'d\') Traceback (most recent call last): File "<pyshell#54>", line 1, in <module> int(\'d\') ValueError: invalid literal for int() with base 10: \'d\' 9. TypeError: 类型错误 >>> iStr = \'22\' >>> iVal = 22 >>> obj = iStr + iVal; Traceback (most recent call last): File "<pyshell#68>", line 1, in <module> obj = iStr + iVal; TypeError: Can\'t convert \'int\' object to str implicitly 10. AssertionError: 断言错误 >>> assert 1 != 1 Traceback (most recent call last): File "<pyshell#70>", line 1, in <module> assert 1 != 1 AssertionError 11. MemoryError: 内存耗尽异常 12.ImportError 无法引入模块或包;基本上是路径问题或名称错误 13.IndentationError 语法错误(的子类) ;代码没有正确对齐 14.KeyboardInterrupt Ctrl+C被按下 15.UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 16. NotImplementedError: 方法没实现引起的异常 class Base(object): def __init__(self): pass def action(self): #抛出异常,说明该接口方法未实现 raise NotImplementedError 17. LookupError: 键、值不存在引发的异常 LookupError异常是IndexError、KeyError的基类, 如果你不确定数据类型是字典还是列表时,可以用LookupError捕获此异常 18. StandardError 标准异常 除StopIteration, GeneratorExit, KeyboardInterrupt 和SystemExit外,其他异常都是StandarError的子类。 错误检测与异常处理区别在于:错误检测是在正常的程序流中,处理不可预见问题的代码,例如一个调用操作未能成功结束
更多错误
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError
更多异常
Socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
socket和file的区别:
- file模块是针对某个指定文件进行【打开】【读写】【关闭】
- socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
import socket ip_port = (\'127.0.0.1\',9999) sk = socket.socket() sk.bind(ip_port) sk.listen(5) while True: print \'server waiting...\' conn,addr = sk.accept() client_data = conn.recv(1024) print client_data conn.sendall(\'不要回答,不要回答,不要回答\') conn.close()
import socket ip_port = (\'127.0.0.1\',9999) sk = socket.socket() sk.connect(ip_port) sk.sendall(\'请求回答\') server_reply = sk.recv(1024) print server_reply sk.close()
套接字发展史及分类
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
import socket 2 socket.socket(socket_family,socket_type,protocal=0) 3 socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。 4 5 获取tcp/ip套接字 6 tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 8 获取udp/ip套接字 9 udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 10 11 由于 socket 模块中有太多的属性。我们在这里破例使用了\'from module import *\'语句。使用 \'from socket import *\',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。 12 例如tcpSock = socket(AF_INET, SOCK_STREAM)
参数一:地址簇
socket.AF_INET IPv4(默认)
socket.AF_INET6 IPv6
socket.AF_UNIX 只能够用于单一的Unix系统进程间通信
参数二:类型
socket.SOCK_STREAM 流式socket , for TCP (默认)
socket.SOCK_DGRAM 数据报式socket , for UDP
socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
socket.SOCK_SEQPACKET 可靠的连续数据包服务
套接字格式:
socket(family,type[,protocal]) 使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。
socket类型 |
描述 |
socket.AF_UNIX |
只能够用于单一的Unix系统进程间通信 |
socket.AF_INET |
服务器之间网络通信 |
socket.AF_INET6 |
IPv6 |
socket.SOCK_STREAM |
流式socket , for TCP |
socket.SOCK_DGRAM |
数据报式socket , for UDP |
socket.SOCK_RAW |
原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。 |
socket.SOCK_SEQPACKET |
可靠的连续数据包服务 |
创建TCP Socket: |
|
创建UDP Socket: |
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
参数三:协议
0(默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议
注意点:
1)TCP发送数据时,已建立好TCP连接,所以不需要指定地址。UDP是面向无连接的,每次发送要指定是发给谁。
2)服务端与客户端不能直接发送列表,元组,字典。需要字符串化repr(data)。