模块三 面向对象编程 考核

1.什么是面向对象,回答准确

# 看了旧视频, 有进一步理解

面向对象: 核心是对象,是特征和技能的结合体
    
相似特征可以放在 __init__方法里面,方便调用
相似的技能可以定义方法,然后实例化对象直接调用方法即可

扩展性强

2.面向对象和面向过程的区别,扩展aop

面向对象:核心是对象,特征和技能的结合体,扩展性强
面向过程:核心是过程,按照步骤一步一步进行,类似流水线,简单好理解,但是扩展性差
# 扩展AOP, https://www.jianshu.com/p/5b9a0d77f95f  参考
Aspect Oriented Programming,面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,是Spring框架中的一个重要内容,是函数式编程的一种衍生范型

# AOP的相关概念
 - Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
 - Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
 - Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
 - Advice(通知):AOP在特定的切入点上执行的增强处理,有before(前置),after(后置),afterReturning(最终),afterThrowing(异常),around(环绕)
 - Target(目标对象):织入 Advice 的目标对象.。
 - Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
 - introduction(引入):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
 - AOP代理(AOP Proxy):AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
 - 目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。POJO
# Advice通知类型介绍
(1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可
(2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值
(3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名来访问目标方法中所抛出的异常对象
(4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式
(5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint

# AOP使用场景
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

# 关于jar包依赖
首先,使用aop依赖包除了Spring提供给开发者的jar包外,还需额外上网下载两个jar包:
1、aopalliance.jar
2、aspectjweaver.jar

# 使用AOP的几种方式
1.经典的基于代理的AOP
2.@AspectJ注解驱动的切面
3.纯POJO切面(纯粹通过<aop:fonfig>标签配置)
4.注入式AspectJ切面

# 主要功能
日志记录,性能统计,安全控制,事务处理,异常处理等等。

# 主要意图
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持

3.简述类、对象、实例化、类方法是什么

# 类
一系列相似对象的特征和技能的结合体

# 对象
特征和技能的结合体

# 实例化
类() 是实例化也即调用类,产生对象

# 类方法
绑定方法(绑定到对象的方法):默认参数self,主要是对象调用
类方法(绑定到类的方法):默认参数cls, @classmethod表示,主要是类调用
静态方法(非绑定方法):无参数,对象和类均可调用

4. __ init __ 和__ new __的作用区别是什么

# __init__ 
类实例创建之后才能调用,才会产生对象

# __new__
用于创建对象(空对象),并返回,在__init__方法执行之前
 - 通常用于控制生成一个新实例的过程。它是类级别的方法
 - 当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径
 - 实现自定义的@metaclass
 - 用__new__来实现单例,每次实例化类的对象时,都是最开始创建的那个对象,不再重复创建对象

# 简单的单例模式
class Singleton:
    instance = None

    def __init__(self, name):
        self.name = name
        
    def __new__(cls, *args, **kwargs):
        # 返回空对象
        if cls.instance:
            return cls.instance
        cls.instance = object.__new__(cls)
        return cls.instance

obj1 = Singleton('alex')
obj2 = Singleton('SB')

print(obj1,obj2) # 内存地址一样

# 多线程执行单例模式
import threading
import time
class Singleton:
    instance = None
    lock = threading.RLock() # 加锁在类里面

    def __init__(self, name):
        self.name = name
        
    def __new__(cls, *args, **kwargs):
        with cls.lock: # 用锁的时候 cls.lock
            if cls.instance:
                return cls.instance
            time.sleep(0.1)
            cls.instance = object.__new__(cls)
        return cls.instance

def task():
    obj = Singleton('x')
    print(obj)

for i in range(10):
    t = threading.Thread(target=task)
    t.start()
    
# 多线程执行单例模式(加判断) 需要背会
import threading
import time
class Singleton:
    instance = None
    lock = threading.RLock()

    def __init__(self, name):
        self.name = name
        
    def __new__(cls, *args, **kwargs):

        if cls.instance:
            return cls.instance  # 加此处判断
        with cls.lock:
            if cls.instance:
                return cls.instance
            time.sleep(0.1)
            cls.instance = object.__new__(cls)
        return cls.instance

def task():
    obj = Singleton('x')
    print(obj)

for i in range(10):
    t = threading.Thread(target=task)
    t.start()

# 执行1000行代码

data = Singleton('asdfasdf')
print(data)

5.静态方法、类方法、普通方法有什么区别

# 参考问题3
绑定方法(绑定到对象的方法):默认参数self,主要是对象调用
类方法(绑定到类的方法):默认参数cls, @classmethod表示,主要是类调用
静态方法(非绑定方法):无参数,对象和类均可调用


6.类的三大特性 这块需要加深理解多态 子类实现父类的方法

# 三大特性
继承,将多个子类中相同的方法放在父类中,子类可以继承父类中的方法(提升重用性)
封装,将多个数据封装到一个对象; 将同类的方法编写(封装)在一个类型中。
多态,天然支持多态,崇尚鸭子模型,不会对类型进行限制,只要具备相应的属性即可,例如:
	def func(arg):
		arg.send()
	不管arg是什么类型,只要具备send方法即可。
    
# 子类实现父类的方法
继承,单继承,多继承
super() (依赖继承) 是一个特殊的对象
指名道姓(不依赖继承) Hero.attack(self,enemy)
mro()
C3算法 从左到右,深度优先,大小钻石,留住顶端

7.什么是三次握手四次挥手 tcp/udp

# UDP
User Data Protocol 用户数据报协议
是一个无连接的简单的面向数据报的传输层协议
不可靠,它是一直发数据,不需要对方回应
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
常见的有:语音通话、视频通话、实时游戏画面 等。

# TCP
Transmission Control Protocol,传输控制协议
是面向连接的协议
可靠,因为只要对方回了确认收到信息,才发下一个,如果没收到确认信息就重发
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
常见有:网站、手机APP数据获取等。

# TCP协议有三次握手和四次挥手

# 三次握手
第一次握手:客户端先向服务端发起一次询问建立连接的请求,并随机生成一个值作为标识
第二次握手:服务端向客户端先回应第一个标识,再重新发一个确认标识
第三次握手:客户端确认标识,建立连接,开始传输数据

# 四次挥手
第一次挥手:客户端向服务端发起请求断开连接的请求
第二次挥手:服务端向客户端确认请求
第三次挥手:服务端向客户端发起断开连接请求
第四次挥手:客户端向服务端确认断开请求

8.osi️层模型及作用。arp

1.应用层:规定应用程序的数据格式
2.表示层:对来自应用层的命令和数据进行解释,按照一定格式传给会话层。如编码、数据格式转换、加密解密、压缩解压
3.会话层:负责与目标建立、中断连接
4.传输层:建立端口到端口的通信,其实就确定双方的端口信息(TCP/UCD协议)
5.网路层:标记目标IP信息(IP协议层)
6.数据链路层:对数据进行分组并设置源和目标mac地址(以太网&MAC地址)
7.物理层:将二进制数据在物理媒体上传输
# ARP (Address Resolution Protocol) 地址解析协议 扩展
目的是实现IP地址到MAC地址的转换

OSI模型把网络工作分为七层,IP地址在OSI模型的第三层,MAC地址在第二层,彼此不直接打交道。在通过以太网发送IP数据包时,需要先封装第三层(32位IP地址)、第二层(48位MAC地址)的包头,但由于发送时只知道目标IP地址,不知道其MAC地址,又不能跨第二、三层,所以需要使用地址解析协议。使用地址解析协议,可根据网络层IP数据包包头中的IP地址信息解析出目标硬件地址(MAC地址)信息,以保证通信的顺利进行

在计算机间通信的时候,计算机要知道目的计算机是谁(就像我们人交流一样,要知道对方是谁),这中间需要涉及到MAC地址,而MAC是真正的电脑的唯一标识符。

为什么需要ARP协议呢?因为在OSI七层模型中,对数据从上到下进行封装发送出去,然后对数据从下到上解包接收,但是上层(网络层)关心的IP地址,下层关心的是MAC地址,这个时候就需要映射IP和MAC

9.黏包是如何产生的

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

# 定义
在获取数据时,出现数据的内容不是本应该接收的数据,如:对方第一次发送hello,第二次发送world,我方接收时,应该收两次,一次是hello,一次是world,但事实上是一次收到helloworld,一次收到空,这种现象叫粘包

# 发生原因
粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
    
# 发生条件
1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
2.接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

# 解决办法
每次发送的消息时,都将消息划分为 头部(固定字节长度) 和 数据 两部分。例如:头部,用4个字节表示后面数据的长度。
发送数据,先发送数据的长度,再发送数据(或拼接起来再发送)。
接收数据,先读4个字节就可以知道自己这个数据包中的数据长度,再根据长度读取到数据。
对于头部需要一个数字并固定为4个字节,这个功能可以借助python的struct包来实现:
import struct
# ### 数值转换为固定4个字节,四个字节的范围 -2147483648 <= number <= 2147483647 ######
v1 = struct.pack('i', 199)
print(v1) # b'\xc7\x00\x00\x00'
for item in v1:
    print(item, bin(item)) 
 
# ########### 4个字节转换为数字 ###########
v2 = struct.unpack('i', v1) # v1= b'\xc7\x00\x00\x00'
 print(v2) # (199,) 

10.非阻塞和io多路复用

# 非阻塞

 - socket服务端(接收者)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setblocking(False) # 加上就变为了非阻塞
    sock.bind(('127.0.0.1', 8001))
    sock.listen(5)
  - socket客户端(发送者)
    client = socket.socket()
    client.setblocking(False) # 加上就变为了非阻塞
    client.connect(('127.0.0.1', 8001))
    
用户进程其实是需要不断的主动查看是否有数据发来,在这个时间段可以干其他事情

实现原因:
    直接查询是否有数据,如果没有数据,直接返回结果,然后在期间不断查询是否有数据发送且可以在此期间干一些其他事情

# IO多路复用
通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作

IO多路复用 + 非阻塞,可以实现让TCP的服务端同时处理多个客户端的请求

# ################### socket服务端 ###################
import select
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False)  # 加上就变为了非阻塞
server.bind(('127.0.0.1', 8001))
server.listen(5)

inputs = [server, ] # socket对象列表 -> [server, 第一个客户端连接conn ]

while True:
    # 当 参数1 序列中的socket对象发生可读时(accetp和read),则获取发生变化的对象并添加到 r列表中。
    # r = [] 无人连接
    # r = [server,]
    # r = [第一个客户端连接conn,]
    # r = [server,]
    # r = [第一个客户端连接conn,第二个客户端连接conn] 啷个同时发消息
    # r = [第二个客户端连接conn,]
    r, w, e = select.select(inputs, [], [], 0.05) # 0.05时间去检测是否有人来连接
    for sock in r:
        # server
        if sock == server:
            conn, addr = sock.accept() # 接收新连接。
            print("有新连接")
            # conn.sendall() #给客户端发消息
            # conn.recv("xx") # 接受客户端信息
            inputs.append(conn)
        else:
            data = sock.recv(1024)
            if data:
                print("收到消息:", data)
            else:
                print("关闭连接")
                inputs.remove(sock)
	# 干点其他事 20s 干其他事情的代码可以放在这里
"""
优点:
	1. 干点那其他的事。如果没有新客户来连接
	2. 让服务端支持多个客户端同时来连接。
"""


# IO多路复用+非阻塞
基于 IO多路复用 + 非阻塞的特性,无论编写socket的服务端和客户端都可以提升性能。其中
- IO多路复用,监测socket对象是否有变化(是否连接成功?是否有数据到来等)。
- 非阻塞,socket的connect、recv过程不再等待。

注意:IO多路复用只能用来监听 IO对象 是否发生变化,常见的有:文件是否可读写、电脑终端设备输入和输出、网络请求(常见)。


11. 一个圆形类,属性是半径,提供两个方法 计算圆的周长和面积

# 方式一
"""
r = float(input("请输入圆的半径:"))
π = 3.14
s = π*r**2
l = 2*π*r
print("圆的面积为:{}".format(s))
print("圆的周长为:{}".format(l))
"""


# 方式二

class Circle:
    def __init__(self, r):
        self.π = 3.14
        self.r = r

    def s(self):
        data = self.π * self.r ** 2
        print("圆的面积为:{}".format(data))

    def l(self):
        data = 2 * self.π * self.r
        print("圆的周长为:{}".format(data))


obj = Circle(3)
obj.s()
obj.l()
posted @ 2021-07-19 00:20  Max-BP23  阅读(98)  评论(0)    收藏  举报