Python面试基础题-2018-12-26详解编程语言

1).可变与不可变类型

答:

不可变类型:数字、字符串、元组

1、对不可变类型的变量重新赋值,实际上是重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象(如果没有其他变量引用原有对象的话(即引用计数为0),原有对象就会被回收)。

>>> s1=11 
>>> id(s1) 
9323456 
>>> s1=12 
>>> id(s1) 
9323488

2、对于不可变类型int,无论创建多少个不可变类型,只要值相同,都指向同个内存地址。同样情况的还有比较短的字符串。

>>> s1=11 
>>> id(s1) 
9323456 
>>> s1=11 
>>> id(s1) 
9323456 
>>> s2=11 
>>> id(s2) 
9323456 
>>> id(11) 
9323456 
>>> 

3、声明两个相同值的浮点型变量,查看它们的id,发现它们并不是指向同个内存地址,这点和int类型不同(这方面涉及Python内存管理机制,Python对int类型和较短的字符串进行了缓存,无论声明多少个值相同的变量,实际上都指向同个内存地址。)。

>>> f1=3.14 
>>> f2=3.14 
>>> id(f1) 
140426609213560 
>>> id(f2) 
140426609213632

可变类型:列表、字典

1、以list为例。list在append之后,还是指向同个内存地址,因为list是可变类型,可以在原处修改。

2、当存在多个值相同的可变类型变量时,看看它们是不是跟可变类型一样指向同个内存地址,答案:不是指向同个内存地址。

>>> a=[12,3] 
>>> b=[12,3] 
>>> c=[12,3] 
>>> id(a),id(b),id(c) 
(140426485310152, 140426485384200, 140426485384072)

2)浅拷贝与深拷贝的实现方式、区别;deepcopy如果你来设计,如何实现

 

深拷贝:复制并创建一个一摸一样的对象(拷贝的值是一样的,但是内存地址不一样,) 

  • 深浅拷贝都是对源对象的复制,占用不同的内存空间
  • 如果源对象只有一级目录的话,源做任何改动,不影响深浅拷贝对象
  • 如果源对象不止一级目录的话,源做任何改动,都要影响浅拷贝,但不影响深拷贝
  • 序列对象的切片其实是浅拷贝,即只拷贝顶级的对象
>>> a = [1, 2, 3, [4, 5]] 
>>> import copy 
>>> a1 = copy.copy(a) # 浅拷贝 
>>> b1 = copy.deepcopy(a) # 深拷贝 
>>> id(a),id(a1),id(b1) 
(140426485476488, 140426485476104, 140426485476168) 
>>>  
>>>  
>>> a.append(6) 
>>> a 
[1, 2, 3, [4, 5], 6] 
>>> a1 
[1, 2, 3, [4, 5]] 
>>> b1 
[1, 2, 3, [4, 5]] 
>>> a[3].append(7) 
>>> a 
[1, 2, 3, [4, 5, 7], 6] 
>>> a1 
[1, 2, 3, [4, 5, 7]] 
>>> b1 
[1, 2, 3, [4, 5]] 
>>> 

3).__new__() 与 __init__()的区别

???todo

4).你知道几种设计模式

???todo

4).编码和解码你了解过么

5).列表推导list comprehension和生成器的优劣

6).什么是装饰器;如果想在函数之后进行装饰,应该怎么做

7).手写个使用装饰器实现的单例模式

8).使用装饰器的单例和使用其他方法的单例,在后续使用中,有何区别

9).手写:正则邮箱地址

答:(r’^[a-zA-Z0-9.]+@[a-zA-Z0-9]+.[a-zA-Z]{2,3}$’)

10).介绍下垃圾回收:引用计数/分代回收/孤立引用环

 答:引用计数/分代回收, 孤立引用环???

11).多进程与多线程的区别,CPU密集型适合用什么

答:CPU密集型适合用多进程,因为可以充分利用CPU核心数,且多线程因为GIL全局锁的影响不适合CPU密集型,适合IO密集型。

12).进程通信的方式有几种

答:进程间通讯有多种方式,包括信号,管道,消息队列,信号量,共享内存,socket

13).介绍下协程,为何比线程还快

答:

为什么有全局锁GIL?

答:

  问题在于,这个引用计数变量需要在两个线程同时增加或减少时从竞争条件中得到保护。如果发生了这种情况,可能会导致泄露的内存永远不会被释放,抑或更严重的是当一个对象的引用仍然存在的情况下错误地释放内存。这可能会导致Python程序崩溃或带来各种诡异的bug。

      通过对跨线程分享的数据结构添加锁定以至于数据不会不一致地被修改,这样做可以很好的保证引用计数变量的安全。

      但是对每一个对象或者对象组添加锁意味着会存在多个锁这也就导致了另外一个问题——死锁(只有当存在多个锁时才会发生)。而另一个副作用是由于重复获取和释放锁而导致的性能下降。

      GIL是解释器本身的一个单一锁,它增加的一条规则表明任何Python字节码的执行都需要获取解释锁。这有效地防止了死锁(因为只存在一个锁)并且不会带来太多的性能开销。但是这的确使每一个计算密集型任务变成了单线程。

     GIL对I/O密集型任务多线程程序的性能没有太大的影响,因为在等待I/O时锁可以在多线程之间共享。

      但是对于一个线程是完全计算密集型的任务来说(例如,利用线程进行部分图像处理)不仅会由于锁而变成单线程任务而且还会明显的增加执行时间。正如上例中多线程与完全单线程相比的结果。

      这种执行时间的增加是由于锁带来的获取和释放开销。

python中有全局解释器锁,由于全局解释器锁的存在,所以在同一时间内,python解释器只能运行一个线程的代码,这大大影响了python多线程的性能。

协程的好处:

跨平台
跨体系架构
无需线程上下文切换的开销
无需原子操作锁定及同步的开销
方便切换控制流,简化编程模型
高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:

无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序:这一点和事件驱动一样,可以使用异步IO操作来解决

14).range和xrange的区别

答:

range([start,] stop[, step]),生成一个序列,数据量大时,占用内存大

xrange([start,] stop[, step]),是作为一个生成器,即他的数据生成一个取一个。

总结:xrange做循环的性能比range好,尤其是返回很大的时候。尽量用xrange吧,除非你是要返回一个列表

参考链接 :

https://blog.csdn.net/cH3RUF0tErmB3yH/article/details/79223846

https://www.cnblogs.com/clement-jiao/p/8719323.html

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/18179.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论