Python编程类知识(面经)

1. Python的装饰器

Python中的装饰器,本质上就是一个函数。
可以让其他函数在不需要任何代码变动的情况下,增加函数对象。适用场景有 插入日志、性能测试、、
就是说 为了已经存在的函数或对象添加额外的功能
还有其他的带参数的装饰器 、类装饰器、、

2. Python的生成器怎么实现的?和普通的函数有什么区别(迭代器)

跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。 在调用生成器运行的过程中,每次遇到yield 时函数会暂停并保存当前所有的运行信息,返回yield 的值, 并在下一次执行next() 方法时从当前位置继续运行。调用一个生成器函数,返回的是一个迭代器对象。

3. 深拷贝、浅拷贝

浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象

深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响
list.copy()。原来的改变会改变
list.deepcopy() 独立起来,不受原本的改变而改变

4. 进程和线程的区别

进程:一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程

线程:进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。

同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多

根本区别:
(1)进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
(2)每个进程都有独立的代码和数据空间;同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
(3)一个进程内有多个线程

5. 多线程和多进程的区别

多进程和多线程的主要区别是:1、线程是进程的子集,一个进程可能由多个线程组成;2、多进程的数据是分开的,共享复杂,需要用IPC,但同步简单;3、多线程共享进程数据,共享简单,但同步复杂

6. 进程的阻塞 block

正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为

7. 某个线程奔溃了会影响所在进程么

线程崩溃不一定导致进程崩溃。 线程崩溃的本质就是内存出错

8. 跨进程通信有哪些方式

共享内存
信号(signal)
管道通信

9. 进程IPC有哪些?用过哪些

InterProcess Communication
匿名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道(FIFO)。

匿名通道
有名通道
信号(Signal)

11. 三次握手四次挥手了解吗?讲一讲为什么要有四次挥手,四次挥手的各个阶段?

三次握手:
第一次握手就是客户端给服务器端发送一个报文,第二次就是服务器收到报文之后,会应答一个报文给客户端,第三次握手就是客户端收到报文后再给服务器发送一个报文,三次握手就成功了
作用是为了确认双方的接收与发送能力是否正常。

四次挥手:
客户端先发起关闭请求
第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于FIN_WAIT1状态。

第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT状态。

第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +

第四次挥手:作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态
服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。

12. 死锁解决;了解线程的锁吗?怎么区分是死锁还是死循环

什么是全局变量 ?
在编写程序的时候,如果想为一个在函数外的变量重新赋值
就需要告诉python这个变量的作用域是全局变量。此时用global语句就可以变成这个任务,也就是说没有用global语句的情况下,是不能修改全局变量的。

当多个线程同时操作同一个共享全局变量的时候,就容易出现线程安全问题。
解决资源占用的问题;保证同一时间一个对象只有一个线程在访问;保证数据的安全性。

13. 数组和链表的区别 ,数组为什么查找比较快

数组由于是紧凑连续存储,可以随机访问,通过索引快速找到对应元素,而且相对节约存储空间。但正因为连续存储,内存空间必须一次性分配够,所以说数组如果要扩容,需要重新分配一块更大的空间,再把数据全部复制过去。
而且你如果想在数组中间进行插入和删除,每次必须搬移后面的所有数据以保持连续。

链表因为元素不连续,而是靠指针指向下一个元素的位置,所以不存在数组的扩容问题;如果知道某一元素的前驱和后驱,操作指针即可删除该元素或者插入新元素,时间复杂度O(1)。
但是正因为存储空间不连续,你无法根据一个索引算出对应元素的地址,所以不能随机访问;而且由于每个元素必须存储指向前后元素位置的指针,会消耗相对更多的储存空间。

14. 解释一下动态数组 以及动态数组的底层实现

解释一下动态数组 以及动态数组的底层实现。

在静态数组中,我们需要在分配时指定大小。在定义数组的时候,其实计算机已经帮我们分配好了内存来存储,实际上我们不能扩展数组,因为它的大小是固定的。比如:我们分配一个大小为10的数组,则不能插入超过10个项目。

但是动态数组会在需要的时候自动调整其大小。这一点有点像我们使用的Python列表,可以存储任意数量的项目,而无需在分配时指定大小。

所以实现一个动态数组的实现的关键是——如何扩展数组

  1. 当列表list1的大小已满时,而此时有新的元素要添加进列表,我们会执行一下步骤来克服其大小限制的缺点:
  2. 分配具有更大容量的新数组 list2
  3. 设置 list2[i] = list1[i] (i=0,1,2,…,n-1),其中n是该项目的当前编号
  4. 设置list1 = list2,也就是说,list2正在作为新的数组来引用我们的新列表
  5. 然后,只要将新的元素插入(添加)到我们的列表list1即可。

下来要思考的问题是,新数组应该多大?通常我们得做法是:新数组的大小是已满的旧数组的2倍。

15. 什么是 treemap,treemap的底层实现

TreeMap是Java内部实现比较复杂的集合类之一。与HashMap不一样,TreeMap的底层不是用哈希表实现的,而是用红黑树实现的。另外,HashMap存取元素的时间复杂度是O(1)的常量级,而TreeMap对元素的操作复杂度为O(log n)。虽然在操作性能方面,TreeMap不占优势,但是因为它使用红黑树(平衡二叉查找树)实现,所以它内部的元素都是排好序的。当需要查找的元素是排好序的,TreeMap的优势就体现出来了

16. 使用过http协议吗,介绍下post;post和get的区别

GET 用于获取信息,是无副作用的,是幂等的,且可缓存。
POST 用于修改服务器上的数据,有副作用,非幂等,不可缓存

GET是 “读取“一个资源。比如Get到一个html文件。反复读取不应该对访问的数据有副作用。比如”GET一下,用户就下单了,返回订单已受理“,这是不可接受的。没有副作用被称为“幂等“(Idempotent)。因为GET因为是读取,就可以对GET请求的数据做缓存。这个缓存可以做到浏览器本身上

POST是 在页面里 标签会定义一个表单。点击其中的submit元素会发出一个POST请求让服务器做一件事。这件事往往是有副作用的,不幂等的。
不幂等也就意味着不能随意多次执行。因此也就不能缓存。

因为POST可能有副作用,所以浏览器实现为不能把POST请求保存为书签。想想,如果点一下书签就下一个单,是不是很恐怖?

补充1:python和java在多线程的区别

任何Python程序,不管有多少核的处理器,任何时候都总是只有一个线程在执行(这点上Python是不同于Java的,对于多核的情况,Java可以同时开启多个线程进行处理

补充2. 什么是并行和并发?

并发:是指应用能够交替执行不同的任务,其实并发有带你类似于多线程的原理,多线程并非同时执行多个任务,如果你开多个线程执行,就是在你几乎不可察觉的速度不断的去切换这几个线程,以达到“同时执行的效果”

并行:时指应用能够“同时”执行不同的的任务,就像你吃饭的时候可以一边吃饭一边打电话
总结来说就是,并发时交替执行,并行时同时执行

补充3 互斥锁是什么?有什么好处和坏处?

Lock锁的作用是当多个进程需要访问共享资源的时候,避免访问的冲突。加锁保证了多个进程修改同一块数据时,同一时间只能有一个修改,即串行的修改,牺牲了速度但保证了数据安全。
GIL(全局解释器锁)

GIL本质就是一把互斥锁,既然是互斥锁,所有的互斥锁的本质都是一样的,都是会将并发运行变成串行,以此来控制同一时间内数据只能被一个任务修改,进而保证数据安全。保护不同的数据的安全,就应该加不同的锁。

也就是说,在Python中的多线程是假的多线程,Python解释器虽然可以开启多个线程,但在同一时间只有一个线程在解释器中运行,而做到这一点的正式由于GIL锁的存在,它的存在使得CPU的资源统一时间只会给一个线程使用,而由于开启线程的开销小,所以多线程才能有一篇用武之地。

补充4. 什么是僵尸进程和孤儿进程?

孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

补充5 什么是面对对象的编程

面对对象的三个基本特征是
封装、继承、多态

首先什么是面对对象的编程? 是指对对象进行编程
所有事情都可以抽象为对象。然后对这个对象定义一些属性和方法,放在一个类上。
这样 这个对象便可以完成一系列行为。

封装是隐藏对象的属性和实现细节,仅对外提供公共访问方式(接口)
继承是子类可以拥有父类的属性和方法
多态是建立一个类,给对象分配不同的属性和方法,这样就形成很多在属性和方法存在差异的对象,

继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法:

当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。

补充6. 协程的优势是什么

协程是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间, 而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态
协程的执行效率非常高。 因为子程序切换不是线程切换,而是由程序自身控制。 …
不需要多线程的锁机制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jianafeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值