面经

作者: 全村希望gone | 来源:发表于2020-09-13 15:15 被阅读0次

    Q:浅拷贝与深拷贝,你来设计deepcopy会如何实现?

    A:浅拷贝只复制对象的引用,新旧引用还是指向同一块内存,所以改变内存中的值,二者都会改变;而深拷贝则是新建了一个有相同值的对象,两个对象不共享内存,修改任一对象不影响其它对象。
    deepcopy:对于不可变数据类型(整型、字符串、元组)直接赋值;对于可变数据类型(列表、字典)用递归来实现
    参考:浅拷贝与深拷贝的实现方式、区别;deepcopy如果你来设计,如何实现

    Q:编码和解码

    A:编码是把unicode转为字节流,通过encode方法,解码是把字节流转为指定编码,通过decode方法
    参考:python中的编码与解码

    Q:推导式和生成式区别

    A:推导式会将数据一次性加载到内存中(平常见到的[ele for ele in nums]之类的都是推导式),而生成式则是将数据一个个地加载到内存中,在处理大量数据时很有用。推导式有两种方式,一是把推导式的[]改成(),二是用yield函数,使用yield的函数的生成器有保存状态的特性,即“在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行”

    def fibonacci(num):
        a,b,counter = 0,1,0
        while True:
            if (counter > num):
                return
            yield a
            print('这是第{}次'.format(counter))
            a,b = b, a+b
            counter += 1
    f = fibonacci(10)
    
    print(f)
    print(next(f))
    '''
    <generator object fibonacci at 0x1076e7830>
    0
    '''
    

    继续执行next函数

    print(next(f))
    '''
    这是第0次
    1
    '''
    

    补充:迭代器
    迭代器的创建通过iter函数,遍历通过next函数,或者普通的for循环,其实生成器也可以理解为迭代器,因为它返回的是迭代器对象。
    Python3 迭代器与生成器
    参考:
    列表推导式与生成器的区别
    python中生成器与列表推导式的说明差异
    列表推导式三种模式和生成器

    Q:python创建单例模式的几种方式

    A:单例模式是指一个类只有一个实例(无论这个类被调用多少次,它也只有一个实例)。

    • 使用模块。新建A文件,在文件A下新建B类,并实例化B类,B的实例化对象为b,然后使用from A import b来使用B类。(原理:python的模块就是天然的单例模式,因为模块在第一次导入的时候,会生成.pyc文件,当第二次导入的时候,就会直接加载.pyc文件,而不是再次执行模块代码.如果我们把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了)
    • 使用装饰器。
    def My_decorate(f):
        _dict = {}
    
        def fn(*args, **kwargs):
            if f not in _dict:
                _dict[f] = f(*args, **kwargs)
                print('decorate called')
            return _dict[f]
    
        return fn
    
    
    @My_decorate
    def fx():
        print('fx called')
    
    
    fx()
    
    • 使用类。
    • 使用new。
    class Singleton(object):
        _instance = None
        def __new__(cls, *args, **kwargs):
            if cls._instance is None:
                cls._instance = object.__new__(cls, *args, **kwargs)
    
            return cls._instance
    
    s1 = Singleton()
    s2 = Singleton()
    print(s1)
    print(s2) 
    # 输出
    <__main__.Singleton object at 0x7fdef58b1190>
    <__main__.Singleton object at 0x7fdef58b1190>
    

    Q:使用装饰器构建的单例和使用其它方法构建的单例在后续使用中有什么不同吗

    A:使用装饰器单例的属性不会被覆盖,因为装饰器返回的是之前生成的对象,而用new构建的单例,会调用init方法初始化实例的属性。

    Q:socket短连接、长连接是什么意思

    A:
    短连接:连接->传输数据->关闭连接
    短连接就是建立连接后,只传输一次数据就断开连接,http服务就是短连接的,因为web的访问量很大,如果对服务器端对每个客户端都保持长连接的话会消耗大量的资源。
    长连接:连接->传输数据->保持连接->传输数据->....->关闭连接
    长连接建立连接后不管是否使用都保持连接,适用于操作频繁,点对点的通信,而且连接数不能太多的情况(不然一直保持连接会消耗大量资源)。比如数据库的连接,因为如果数据库用短连接通信会造成socket错误(不知道为什么),而且频繁的socket连接创建会对资源造成浪费。

    image.png
    参考:
    [1] Socket的长连接和短连接(很详细)
    [2] 长连接/短连接应用场景
    [3] Socket长连接和短连接的区别

    Q:TIME_WAIT过多是因为什么?

    TIME_WAIT是主动关闭连接的一方所保持的状态,一般主动关闭连接的一方是server端,所以当存在多个高并发短连接时,会导致存在大量的TIME_WAIT(时长为2个max segment lifetime)。(短连接表示“业务处理+传输数据的时间 远远小于 TIMEWAIT超时的时间”的连接,那为什么是高并发短连接呢,我自己的理解是短连接消耗的资源少,所以有高并发的可能,而长连接本来就用于连接数不太多的操作频繁的点对点通信,所以不太可能出现高并发情况)。
    补充:TIME_WAIT存在的原因:
    (1)维持连接的状态:当出现第四次挥手时ACK丢失,而服务器端重新发送FIN的情况,因为有TIME_WAIT的存在,连接状态得以保持,客户端可以正常地应答服务器端;
    (2)允许老的重复分节在网络中消逝:TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个原来的迷途分节就称为lost duplicate。
    在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身(incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。
    为了避免这个情况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时候,来自连接先前化身的重复分组已经在网络中消逝。
    参考:
    [1] 解决TIME_WAIT过多造成的问题
    [2] TIME_WAIT是什么?

    Q:一次完整的http请求过程

    A:域名解析 --> 发起TCP三次握手 --> 建立连接后浏览器发起http请求 --> 服务器响应请求,浏览器获得html代码 --> 浏览器解析html代码,请求其中的资源 --> 浏览器将页面渲染后呈现给用户
    参考:一次完整的HTTP请求过程

    Q:select和epoll你了解么,区别在哪

    A:完全不了解,都没听说过,看了答案之后也不知道这是什么东西。
    参考:select、poll、epoll之间的区别(搜狗面试)

    Q:五种I/O模型

    A:

    Q:python中可变类型和不可变类型

    A:在python中变量存储的实际上是值的地址,可以用id函数看到
    不可变数据类型(整型,字符串,元组),不允许变量的值发生变化,如果改变了变量的值,相当于新建一个对象,变量存储的地址也随之改变,而具有相同值的不同变量,无论有多少个,这些变量中存储的都是同一个地址。
    可变数据类型(列表,字典),允许变量的值发生变化,如果改变了变量的值,变量存储的地址不会发生变化。具有相同值的不同变量,它们存储的地址也不同,即这些值虽然相同,但是在内存中有自己的地址。
    参考:Python 可变类型和不可变类型,以及其引用

    相关文章

      网友评论

          本文标题:面经

          本文链接:https://www.haomeiwen.com/subject/bxzaektx.html