美文网首页
python面试题硬知识,你懂多少?

python面试题硬知识,你懂多少?

作者: 谢小磊 | 来源:发表于2018-10-28 17:04 被阅读0次
1、大数据的文件的读取

① 读取大几G的大文件,可以利用生成器 generator
② 对可迭代对象 file,进行迭代遍历:for line in file,会自动地使用缓冲IO(buffered IO)以及内存管理,而不必担心任何大文件的问题。

with open('filename') as file:
    for line in file:
        do_things(line)
2、迭代器和生成器区别 ?

(1)迭代器是一个更抽象的概念,任何对如果它类有 next方法和方法和 iter方法返回自己本身 。对于 strings、list、dict、tuple等这类容器对象,使用 for循环遍历是很方便的。在后台for语句对容器象调用iter()函数,iter()是python的内置函数。iter()会返回一个定义了会返回一个定义了next()方法的迭代器对象,它在容中逐个方法的迭代器对象,它在容中逐个方法的迭代器对象,它在容中逐个 访问容器内元素,next()也是python的内置函数。在没有后续元素时,next()会抛出一个StopIter异常

(2)生成器(Generator)是创建迭代器的简单而强大工具。它们写起来就像是正规的函数,只在需要返回据时候使用 yield语句。每次 next()被调用被调用 时,生成器会返回它脱离的位置(记忆语句最后一次执行和所有数据 时,生成器会返回它脱离的位置(记忆语句最后一次执行和所有数据 时,生成器会返回它脱离的位置(记忆语句最后一次执行和所有数据值)

区别:生成器能做到迭代的所有事区别:生成器能做到迭代的所有事,而且因为自动创建了 __iter__()和 next()方法 ,生成器显得特别简洁,而且生成器也是高效的 而且生成器也是高效的 而且生成器也是高效的 ,使用生成器表达式取代列,使用生成器表达式取代列,使用生成器表达式取代列解析可以同时节省内存。除了创建和保程序状态的自动方法内存。除了创建和保程序状态的自动方法 内存。除了创建和保程序状态的自动方法内存。除了创建和保程序状态的自动方法,当发生器终结时 当发生器终结时当发生器终结时,还会自动抛出StopIteration异常。Yield的用法有点像return,但是它返回的是一个生成器。

3、线程、进程、协程

进程 是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

线程 是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈,但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

协程 是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

4、什么是装饰器?

装饰器 是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

装饰器(decorator)里引入通用功能处理:引入日志、函数执行时间统计、执行函数前预备处理、执行函数后清理功能、权限校验等场景、缓存。

举个例子:

from time import ctime, sleep
def timefun(func):
    def wrappedfunc():
        print("%s called at %s"%(func.__name__, ctime()))
        return func()
    return wrappedfunc
@timefun
def foo():
    print("I am foo")
foo()
sleep(2)
foo()
5、谈谈你对同步异步阻塞非阻塞理解

所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。最常见的例子就是 SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的 LRESULT值返回给调用者。

异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。以CAsycSocket类为例(注意,CSocket从CAsyncSocket派生,但是起功能已经由异步转化为同步),当一个客户端通过调用 Connect函数发出一个连接请求后,调用者线程立刻可以朝下运行。当连接真正建立起来以后,socket底层会发送一个消息通知该对象。这里提到执行部件和调用者通过三种途径返回结果:状态、通知和回调。可以使用哪一种依赖于执行部件的实现,除非执行部件提供多种选择,否则不受调用者控制。如果执行部件用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低(有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一种很严重的错误)。如果是使用通知的方式,效率则很高,因为执行部件几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。

阻塞调用:指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。例如,我们在CSocket中调用Receive函数,如果缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时,当前线程还会继续处理各种各样的消息。如果主窗口和调用函数在同一个线程中,除非你在特殊的界面操作函数中调用,其实主界面还是应该可以刷新。socket接收数据的另外一个函数recv则是一个阻塞调用的例子。当socket工作在阻塞模式的时候,如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。

非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

对象的阻塞模式和阻塞函数调用。对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但是并不是一一对应的。阻塞对象上可以有非阻塞的调用方式,我们可以通过一定的API去轮询状态,在适当的时候调用阻塞函数,就可以避免阻塞。而对于非阻塞对象,调用特殊的函数也可以进入阻塞调用。函数select就是这样的一个例子。

6、GIL对多线程的影响?

GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。每个CPU在同一时间只能执行一个线程(在单核CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。)

在Python多线程下,每个线程的执行方式:

  • 获取GIL
  • 执行代码直到sleep或者是python虚拟机将其挂起。
  • 释放GIL

可见,某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。

在Python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是Python自身的一个计数器,专门做用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。并且由于GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行)。

IO密集型代码(文件处理、网络爬虫等),多线程能够有效提升效率(单线程下有IO操作会进行IO等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序执行效率),所以多线程对IO密集型代码比较友好。

7、python中的反射?

python中反射的核心本质其实就是利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动。


希望能对你有所帮助。
哈哈哈哈,点个赞,关个注?

相关文章

网友评论

      本文标题:python面试题硬知识,你懂多少?

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