NO.7:自学python之路------类的方法、异常处理、socket网络编程
引言
我visual studio 2017就算体积巨大、启动巨慢、功能简陋也不会安装PyCharm的,嘿呀,真香。好吧,为了实现socket网络编程,更换了软件。
正文
静态方法
只是在名义上归类管理,实际上在静态方法里访问不了类或实例中的任何属性。例子。
class Student(object): def __init__(self,name): self.name = name @staticmethod#静态方法 def name(self): print('名字是%s'%(self.name)) a = Student('二狗') a.name()#报错,因为静态方法无法访问实例中的属性
类方法
与静态方法类似,只能访问类变量,不能访问实例变量。
class Student(object): name = '二狗' def __init__(self,name): self.name = name @classmethod def name(self): print('名字是%s'%(name)) print('名字是%s'%(self.name)) a = Student('蛋蛋') a.name()#可以打印类中的名字,之后报错
属性方法
把一个方法变成静态属性。例子。
class Student(object): def __init__(self,name): self.name = name @property def logging(self): print('%s注册成功'%(self.name)) a = Student('二狗') a.logging #无需加括号直接运行
但是,这样无法为属性赋值,也无法删除。为了能够赋值和删除,特增加setter和delte方法。例子。
class Student(object): def __init__(self,name): self.name = name @property def logging(self): print('%s注册成功'%(self.__newname)) @logging.setter def logging(self,name): self.__newname = name @logging.deleter def logging(self): del self.__newname print('删除') a = Student('二狗') a.logging = '蛋蛋' # 无需加括号直接运行 del a.logging # 删除
类的特殊方法
__doc__方法,可以返回类的描述。例子。
class Student(object): '''用于测试''' def __init__(self, name): self.name = name a = Student('二狗') print(a.__doc__)#打印出:用于测试
__module__方法,可以返回类所在的模块。例子。
class Student(object): '''用于测试''' def __init__(self, name): self.name = name a = Student('二狗') print(a.__module__)#打印:__main__
__class__方法,可以返回类和类所在的模块。例子。
class Student(object): '''用于测试''' def __init__(self, name): self.name = name a = Student('二狗') print(a.__class__)#打印 : <class '__main__.Student'>
__call__方法,在类中定义后,可直接由类名() + ()直接运行,或者由对象+()运行。例子。
class Student(object): '''用于测试''' def __init__(self, name): self.name = name def __call__(self): print('in the call') Student('二狗')()# 打印:in the call a = Student('二狗') a()# 打印: in the call
__dict__方法,可以查看类:打印类中所有的属性,不包括实例属性。或者查看对象:打印对象所有实例属性,不包括类属性。例子。
class Student(object): '''用于测试''' def __init__(self, name): self.name = name def __call__(self): print('in the call') print(Student.__dict__)#打印出了所有类属性 a = Student('二狗') print(a.__dict__)#只打印:{'name': '二狗'}
__str__方法,当类中定义了该方法时,打印对象时,默认打印该方法的返回值。例子。
class Student(object): '''用于测试''' def __init__(self, name): self.name = name def __str__(self): return 'in the str' a = Student('二狗') print(a)#打印:in the str
使类字典化:__getitem__方法,__setitem__方法,__delitem__方法。例子。
class Student(object): '''用于测试''' def __init__(self): self.data = {} def __getitem__(self, key): print('__getitem__', key) return self.data.get(key) def __setitem__(self, key, value): print('__setitem__', key, value) self.data[key] = value def __delitem__(self, key): print('__delitem__', key) a = Student() # 实例化 a['name'] = '二狗' # 触发__setitem__ print(a['name']) # 触发__getitem__ del a['name'] # 触发__delitem__
反射
反射可以用于字符串运行类中的方法。主要有四个用法,hasattr,getattr,setattr和delattr。例子。
class Example(object): def __init__(self,name): self.name = name def eat(self): print('%s吃了一个苹果'%(self.name)) d = Dog('二狗') choice = input('>>').strip()#用户输入所需执行的方法 #hasattr,用于判断一个对象是否有名为字符串的方法 print(hasattr(d,choice))#打印:True or False #getattr,根据字符串去获取对象里的字符串名对应的方法的内存地址,所以运行需要加() print(getattr(d,choice))#打印方法的内存地址 getattr(d,choice)()#运行方法 #setattr,将某个函数以字符串的名字添加给类中作为方法。 def drink(self): print('%s在喝'%(self.name)) setattr(d,choice,drink)#将drink以字符串名命名,添加到对象中 #也可用于传入某个属性 setattr(d,choice,None)#将字符串名字的属性传入类中 #delattr,根据字符串去删除对象里的字符串名对应的方法 delattr(d,choice)#删除
动态导入模块
动态导入模块可以以字符串的形式动态将模块导入。例子。
#正常导入 from lib import test #动态导入 mod = __import__('lib.test')#以字符串形式导入 #官方建议使用 import importlib mod = importlib.import_module(lib.test)
类的起源
在Python中,一切皆对象,所以类的创建过程背后,也是通过执行某种类的创建办法创建的。通过打印类的type可以发现,类来源于type。所以存在另外一种创建类的方法。例子。
def func(self): print('hello %s'%(self.name)) def __init__(self,name): self.name = name Foo = type('Foo',(object,),{'talk':func, '__init__':__init__}) f = Foo('二狗') f.talk()
那么type类的起源又是什么呢?这个可以从下面例子中得到。
class MyType(type): '''创建自己的type''' def __init__(self,what,bases=None,dict=None): print('MyType init') super(MyType,self).__init__(what,bases,dict) def __call__(self, *args, **kwargs): print('MyType call')#自定义创建过程 obj = self.__new__(self,*args,**kwargs) self.__init__(obj,*args,**kwargs) class Foo(object): __metaclass__ = MyType def __init__(self,name): self.name = name print('init') def __new__(cls,*args,**kwargs): '''类中自带的方法''' print('new') return object.__new__(cls)
例子中,首先运行MyType中的__init__初始化,之后进入__call__用于创建类的__new__,开辟出self的内存空间。之后进入类中的__new__用于调用类中的init。
异常处理
在编程过程中往往会遇到异常,影响后续程序的运行。通过处理异常可以保证程序在遇到异常后任能够将后续内容执行完毕。方便程序员调试。处理异常的过程如下。例子。
name = ['lala','haha'] data = {} try: name[3] data['name'] except KeyError as e:#将没有Key的错误用e获取 print('没有这个key',e) except (IndexError, KeyError) as e:#将错误用e获取,括号括起来可同时处理多个异常 print('列表操作错误',e) except Exception as e:#可以与之前配合使用,这个用于判断未知错误 print('未知错误',e) else: print('一切正常') finally: # 不管有没有错,都会执行 print('最后一步')
当然,异常也可以自定义,通过自己预先考虑创建一些异常。自定义异常例子。
class NewException(Exception): def __init__(self,msg): self.message = msg def __str__(self): # 默认构建,可以不写 return self.message try: raise NewException('异常描述')#触发异常方法 except NewException as e: print(e)
断言
断言与异常不同,可以根据某个断言来阻止程序继续运行。例子。
a = 'test' assert type(a) is int#断言正确继续运行,错误停止运行 print(a-2)
socket网络编程
这个主要就是实现服务器端与客户端之间的信息交流。具体可以通过例子来学习。
地址簇、类型。需注意官方建议单次传送大小不超过8192。即8K。
#地址簇 #socket.AF_UNIX #unix本机进程间通信 #socket.AF_INET #IPV4 #socket.AF_INET6 #IPV6 #类型 #socket.SOCK_STREAM #tcp/ip #socket.SOCK_DGRAM #udp #socket.SOCK_RAW #伪造IP,DDoS A伪造ip1->服务器->返回:等待一段时间/A不响应,伪造ip2->服务器 #socket.SOCK_RDM #可靠的udp 但是不能保证顺序 #使用 import socket #声明类型 server = socket.socket(AF.INET, sock.SOCK_STREAM) #绑定IP地址和接口 server.bing(localhost,9999) #开始接听
服务器端:
# 服务器端 import socket server = socket.socket() # 声明socket类型,同时生成socket连接对象 server.bind(('localhost', 9998)) # 绑定端口 server.listen() # 监听 print('开始监听') conn, addr = server.accept() # 等电话打进来 # conn客户端连过来而在服务器端为其生成的连接实例链接,端口只能同时和1个人通话 print('听到') while True: data = conn.recv(1024) if not data: print("clinet has lost") break print('收到:', data) conn.send(data.upper()) #转为大写后传回 server.close()
客户机端:
import socket client = socket.socket() # 声明socket类型,同时生成socket连接对象 client.connect(('localhost', 9998)) while True: msg = input('>>').strip() if len(msg): print('不能为空') continue client.send(msg.encode('utf-8')) # sendall循环发送全部文件 data = client.recv(1024) print('recv:', data.decode())