zoukankan      html  css  js  c++  java
  • Python自动化开发课堂笔记【Day08】

    面向对象的高级用法

    1. __str__

    只要执行打印对象的操作,就会触发该对象类中的__str__方法(也就是对象的绑定方法)
    它是一种默认的方法,默认的打印输出为<__main__.Foo object at 0x003EE350>,但是如果将该绑定方法
    在类中重写的话,要求必须有以字符串类型的返回值,返回形式可以自己设定。

    class Foo:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __str__(self):
            return 'name:%s age:%d' % (self.name,self.age)#返回值必须有
    
    obj=Foo('Albert',18)
    print(obj)

    2. __del__(析构函数)

    由类产生的对象是存放在内存中的,程序结束后要释放掉对象,则会触发__del__方法执行

    class Foo:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __del__(self): #注意:必须是程序执行结束后才会执行该方法
            print('__del__')
    
    obj=Foo('Albert',18)
    del obj #此时是主动触发执行__del__方法
    print('after __del__')

    3. __setitem__, __getitem__, __delitem__

    利用字典的方式来操作对象的属性

    #方法修改之前:
    class Foo:
    
        def __init__(self,name):
            self.name = name
    
        def __getitem__(self, item):
            print('getitem')
    
        def __setitem__(self, key, value):
            print('setitem')
    
        def __delitem__(self, key):
            print('delitem')
    
    obj = Foo('egon')
    print(obj.__dict__) #{'name': 'egon'}
    obj['name'] = 'Albert' #虽然可以调用__setitem__方法,但是无法完成真正的修改操作
    print(obj.__dict__) #{'name': 'egon'}
    obj.__dict__['name'] = 'Albert' #正确的修改方式
    print(obj.__dict__) #{'name': 'Albert'}
    
    
    #方法修改之后:
    class Foo:
    
        def __init__(self,name):
            self.name = name
    
        def __getitem__(self, item):
            print('getitem')
            return self.__dict__[item]
    
        def __setitem__(self, key, value):
            print('setitem')
            self.__dict__[key] = value
    
        def __delitem__(self, key):
            print('delitem')
            self.__dict__.pop(key)
    
    obj = Foo('egon')
    obj['name'] = 'Albert' #调用__setitem__
    print(obj['name']) #调用__getitem__
    del obj['name'] #调用__delitem__
    print(obj.__dict__) #结果:{},空字典,属性被删除

    4. __getattr__,__setattr__,__delattr__

    class Foo:
    
        def __init__(self,x):
            self.x = x
    
        def __getattr__(self, item):
            print('getattr')
    
        def __setattr__(self, key, value):
            print('setattr')
            self.__dict__[key] = value
    
        def __delattr__(self, item):
            print('delattr')
            self.__dict__.pop(item)
    
    # obj=Foo()
    # obj.x = 1 #触发__setattr__,但未执行成功
    # print(obj.__dict__)
    # del obj.x #触发__delattr__,但未执行成功
    # print(obj.__dict__)
    # print(obj.x) #触发__getattr__
    
    obj = Foo(10) #触发__setattr__
    print(obj.x) #没有触发__getattr__
    print(obj.__dict__)
    print(obj.y) #当属性不存在的时候才会触发__getattr__
    del obj.x #触发__delattr__
    print(obj.x) #触发__getattr__,说明x已经被删除

    二次加工标准类型

    1. 继承

    需要改写的类型是一个类,可以通过继承的方式实现

    需求:改写list规定只能加入字符串类型数据
    
    class List(list):
    class List(list):
    
        def __init__(self,item_list,tag=False):
            super().__init__(item_list)
            self.tag = tag
    
        def append(self, p_object):
            if not isinstance(p_object,str): #判断要加入的元素是否是字符串,非字符串元素会报错
                raise TypeError('must be str')
            else:
                super().append(p_object) #继承父类的方法
        @property
        def mid_num(self):
            mid_index = len(self) // 2
            return self[mid_index]
    
        def clear(self):
            if not self.tag:
                raise PermissionError('not allowed')#查看是否有清除列表权限
            super().clear() #继承父类的方法
            self.tag = False
    
    l = List([1,2,3])
    l.append('a')
    print(l)
    print(l.mid_num)
    l.tag = True
    l.clear()
    print(l)

    2. 授权

    针对你需要改写的类型它不是一个类,无法用继承的方式实现,只能用授权的方式实现

    import time
    
    class Open:
    
        def __init__(self,filepath,mode='r',encoding='utf-8'):
            self.filepath = filepath
            self.mode = mode
            self.encoding = encoding
            self.ff = open(self.filepath,mode=self.mode,encoding=self.encoding)
    
        def write(self,msg):
            t = time.strftime('%Y-%m-%d %X')
            self.ff.write('%s %s' % (t,msg))
    
        def __getattr__(self, item):
            return getattr(self.ff, item)
    
    obj = Open('a.txt','w',encoding='utf-8')
    
    #未重写write方法时调用方式
    # obj.ff.write('111
    ')
    # obj.ff.write('222
    ')
    # obj.ff.write('333
    ')
    # obj.ff.close()
    
    #重写write方法后的调用方式
    obj.write('aaa
    ')
    obj.write('bbb
    ')
    obj.write('ccc
    ')
    
    print(obj.seek) #<built-in method seek of _io.TextIOWrapper object at 0x0056A530>
    obj.close()

    3. __next__和__iter__实现迭代器协议

    class Foo:
    
        def __init__(self,n_start,n_stop):
            self.n_start = n_start
            self.n_stop = n_stop
    
        def __next__(self):
            if self.n_start >= self.n_stop:
                raise StopIteration #遍历到最后报出异常
            x = self.n_start
            self.n_start += 1
            return x
    
    
        def __iter__(self):
            return self
    
    obj = Foo(0,10)
    # print(next(obj))
    # print(next(obj))
    # print(next(obj))
    for i in obj:
        print(i)
    #相当于
    for i in range(10):
        print(i)

    4. __enter__和__exit__实现上下文管理协议

    class Open:
    
        def __init__(self,name,mode='w',encoding='utf-8'):
            self.name = name
            self.mode = mode
            self.encoding = encoding
            self.f = open(self.name,mode=self.mode,encoding=self.encoding)
    
        def __enter__(self):
            print('__enter__')
            return self.f
    
        def __exit__(self, exc_type, exc_val, exc_tb): #整个代码块结束触发
            print('__exit__')
            # print('exc_type',exc_type) #异常类型
            # print('exc_val',exc_val) #异常的值
            # print('exc_tb',exc_tb) #异常的追踪信息
            self.f.close()
            # return True #处理异常,保证异常所处子代码块以外的代码正常进行
    
    obj = Open('b.txt','w')#没有文件的话会自动创建
    print(obj) #<__main__.Open object at 0x002559F0>
    
    with Open('a.txt') as f: #with Open('a.txt')操作的结果就是触发__enter__返回self.f, 之后as f相当于f=self.f
        print(f) #<_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'>
        # 1/0
        f.write('333
    ')

    5. __call__方法

    class Foo:
    
        def __call__(self, *args, **kwargs):
            print('===>')
    
    obj=Foo()
    obj() #如果类内部没有定义__call__方法,对象是不能以加括号的方式调用的。

    网络编程

    1. socket是什么?
      socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,socket其实就是一个门面模式,
      它把复杂的TCP/IP协议族隐藏在socket接口后面,对用户来说,一组简单的接口就是全部,让socket去组织数据,以符合
      指定的协议。我们无需深入理解TCP/UDP协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出
      的程序自然就是遵循TCP/UDP标准的。

    2. 基于TCP协议的socket的简单实现

    Server端实现
    
    import socket
    
    #socket.AF_INET 指定套接字地址家族
    #socket.SOCK_STREAM 指TCP流式协议
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8080)) #绑定IP地址端口号
    
    phone.listen(5) #bind连接池
    
    #conn为三次握手成功后建立的连接
    #addr为客户端的地址
    conn,addr = phone.accept() #等待连接
    print('conn',conn)
    print('client addr',addr)
    
    client_msg = conn.recv(1024) #收消息
    print('clent msg: %s' % client_msg)
    
    conn.send(client_msg.upper()) #发送消息
    
    conn.close() #关闭连接
    phone.close() #关闭通信
    
    
    Client端实现
    
    import socket
    
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1', 8080)) #客户端发起连接
    
    phone.send('Hello'.encode('utf-8')) #发消息
    
    back_msg = phone.recv(1024)
    
    print(back_msg)
    
    phone.close()

    3. 通信循环和连接循环

    Server端实现
    
    import socket
    
    #socket.AF_INET 指定套接字地址家族
    #socket.SOCK_STREAM 指TCP流式协议
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(('127.0.0.1',8080)) #绑定IP地址端口号
    
    phone.listen(5) #bind连接池
    
    while True:#连接循环
        # conn为三次握手成功后建立的连接
        # addr为客户端的地址
        conn, addr = phone.accept()  # 等待连接
        print('conn', conn)
        print('client addr', addr)
    
        while True:  # 与conn的通信循环
            try:
                client_msg = conn.recv(1024)  # 收消息
                if not client_msg: break #针对Linux平台,收空内容后断开客户端连接,windows平台下可不写
                print('clent msg: %s' % client_msg)
                conn.send(client_msg.upper())  # 发送消息
            except Exception:  # 解决服务端的异常终止
                break
    
        conn.close()  # 关闭连接
    phone.close() #关闭通信
    
    
    Client端实现
    
    import socket
    
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1', 8080)) #客户端发起连接
    
    while True:#通信循环
        msg = input('>>>:').strip()
        if not msg: continue #解决客服端发送空内容的问题
        phone.send(msg.encode('utf-8')) #发消息
        back_msg = phone.recv(1024)
        print(back_msg)
    
    phone.close()

    4. 基于socket实现远程执行shell命令

    Server端实现
    
    import socket
    import subprocess
    
    #socket.AF_INET 指定套接字地址家族
    #socket.SOCK_STREAM 指TCP流式协议
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(('127.0.0.1',8080)) #绑定IP地址端口号
    
    phone.listen(5) #bind连接池
    
    while True:#连接循环
        # conn为三次握手成功后建立的连接
        # addr为客户端的地址
        conn, addr = phone.accept()  # 等待连接
        print('conn', conn)
        print('client addr', addr)
    
        while True:  # 与conn的通信循环
            try:
                cmd = conn.recv(1024)  # 收消息
                if not cmd: break
                res = subprocess.Popen(cmd.decode('utf-8'),shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
                err = res.stderr.read()
                if err:
                    cmd_res = err
                else:
                    cmd_res = res.stdout.read()
                conn.send(cmd_res)
            except Exception:  # 解决服务端的异常终止
                break
    
        conn.close()  # 关闭连接
    phone.close() #关闭通信
    
    
    Client端实现
    
    import socket
    
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1', 8080)) #客户端发起连接
    
    while True:#通信循环
        cmd = input('>>>:').strip()
        if not cmd: continue #解决客服端发送空内容的问题
        phone.send(cmd.encode('utf-8')) #发消息
        cmd_res = phone.recv(1024)
        print(cmd_res.decode('gbk'))
    
    phone.close()

    自定义包头解决粘包问题

    P.S.只有TCP有粘包现象,UDP永远不会粘包

    什么是粘包?
    所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。这取决于TCP的工作原理。

    粘包发生在客户端:受限于网络传送介质的速度,未来得及每条都及时发送给服务端,导致发送的数据在客户端的缓存堆积并一块送到服务端
    粘包发生在服务端:服务端的接收的数据在缓存中未及时被取完,导致接下来从客户端发送过来的数据堆积在服务端的缓存,下一次可能被一并取出

    CPU工作的两种状态
    内核态:运行操作系统,可以操作硬件
    用户态:运行用户的应用程序

    如何解决粘包的问题:
    需要自己定制报头让发送端在发送数据之前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有的数据。

    Server端实现
    
    import socket
    import subprocess
    import struct
    import json
    
    #socket.AF_INET 指定套接字地址家族
    #socket.SOCK_STREAM 指TCP流式协议
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(('127.0.0.1',8080)) #绑定IP地址端口号
    
    phone.listen(5) #bind连接池
    
    while True:#连接循环
        # conn为三次握手成功后建立的连接
        # addr为客户端的地址
        conn, addr = phone.accept()  # 等待连接
        print('conn=', conn)
        print('client addr=', addr)
    
        while True:  # 与conn的通信循环
            try:
                cmd = conn.recv(1024)  # 收消息
                if not cmd: break
                res = subprocess.Popen(cmd.decode('utf-8'),shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
                err = res.stderr.read()
                if err:
                    cmd_res = err
                else:
                    cmd_res = res.stdout.read()
                # conn.send(struct.pack('i',len(cmd_res))) #先发报头
                head_dict={'filename':None,'hash':None,'total_size':len(cmd_res)}
                head_json = json.dumps(head_dict) #报头信息序列化
                head_bytes = head_json.encode('utf-8')#将报头信息转化为字节形式传输
                conn.send(struct.pack('i',len(head_bytes))) #发送报头长度
                conn.send(head_bytes) #再发送报头数据
                conn.send(cmd_res) #再发真实的数据
            except Exception:  # 解决服务端的异常终止
                break
    
        conn.close()  # 关闭连接
    phone.close() #关闭通信
    
    
    Client端实现
    
    import socket
    import struct
    import json
    
    phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.connect(('127.0.0.1', 8080)) #客户端发起连接
    
    while True:#通信循环
        cmd = input('>>>:').strip()
        if not cmd: continue #解决客服端发送空内容的问题
        phone.send(cmd.encode('utf-8')) #发消息
        head_len_info = phone.recv(4) #收到报头的长度信息
        head_len = struct.unpack('i', head_len_info)[0] #得到报头的长度
        head_bytes = phone.recv(head_len) #获取报头信息
        head_json = head_bytes.decode('utf-8') #报头信息反序列化
        head_dict = json.loads(head_json) #获取报头字典格式
        total_size = head_dict['total_size'] #从字典中取出真实的数据
        # total_size = struct.unpack('i',head)[0]
        recv_size = 0
        data = b''
        while recv_size < total_size:
            recv_data = phone.recv(1024)
            data += recv_data
            recv_size += len(recv_data)
        print(data.decode('gbk'))
    phone.close()
  • 相关阅读:
    Android ActionBar应用实战,高仿微信主界面的设计
    Android ActionBar完全解析,使用官方推荐的最佳导航栏(下) .
    Android ActionBar完全解析,使用官方推荐的最佳导航栏(上)
    actionBar兼容2.1及以上版本的做法 .
    Android UI开发详解之ActionBar .
    Android ActionBar详解(三):ActionBar实现切换Tabs标签
    Android ActionBar详解(二):ActionBar实现Tabs标签以及下拉导航 .
    完毕port(CompletionPort)具体解释
    Java的递归算法
    shell语法简单介绍
  • 原文地址:https://www.cnblogs.com/paodanke/p/7053734.html
Copyright © 2011-2022 走看看