python 面试题及数据结构

1、python的数据结构有哪些?

列表(list):可变类型

元组(tuple):可以理解为一个固定的列表,是不可变类型的

字典(dict):字典是key-value存储的,一个key对应一个value值,key值是唯一的

集合(set):是一种无序且不重复的列表

2、python中的列表和元组的区别是什么?元组是不是真的不可变?

列表(list):可变类型

元组(tuple):初始化后不可变

元组中的元素如果拥有可变类型的数据,那这个可变类型的数据,是可以改变的

3、什么是生成器和迭代器?它们之间有什么区别?

迭代器是一个抽象的概念,任何对象,如果它的类有next方法和iter方法返回自己本身,那么他就是一个迭代器

生成器是一种特殊的迭代器,生成器是用yield语句

区别:生成器能做到迭代器能做的所有事,而且应为自动创建iter和next方法,生成器就显得相对简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以节省内存

4、什么是闭包?装饰器又是什么?

闭包:声明在一个函数中的函数,叫做闭包函数。就是函数内嵌套函数,这个内嵌函数就是闭包函数

装饰器:装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。

它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

5、什么是匿名函数,用匿名函数有什么好处?

匿名函数就是lamaba表达式,匿名函数一般用于实现简单的逻辑,函数代码书写简洁,

匿名函数顾名思义就是函数没有名字,因此不用担心函数名冲突。不过Python对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。

6、如何提高python的运行的效率

(1)、减少使用局部变量

(2)、减少函数的调用次数

(3)、采用映射代替条件查找

(4)、采用生成器表达式替代列表解析

(5)、模块编程习惯

7、什么是类、继承

类:实现了属性的封装

继承:实现了代码的复用

8、说一下深拷贝和浅拷贝

浅拷贝:没有拷贝子对象,所以子对象发生改变,原始对象会发生改变

深拷贝:包含对象里面的子对象的拷贝,所以原始对象的改变不会改变拷贝后的对象

9、如何捕获异常,常用的异常机制有哪些?

如果我们没有对异常进行任何预防,那么在程序执行的过程中发生异常,就会中断程序,调用python默认的异常处理器,并在终端输出异常信息

try except finally语句:当在try语句体中间的语句执行出错,回到try语句层,寻找后面except语句,找到except语句后,会调用这个自定义的异常处理器。except将异常处理完毕后,程序继续往下执行,finally语句表示,无论是否发生异常,finally中的语句都要执行

assert语句(断言):判断assert后边紧跟的语句是True还是False,如果是True则继续执行,如果是False则中断程序,调用默认的异常处理器,同时输出assert语句后面的提示信息

with语句:如果with语句或语句块中发生异常,会调用默认的异常处理器,但文件还是回正常关闭。

10、copy()和deepcopy()的区别

copy()是浅拷贝,只拷贝可变元素的地址,如果可变元素的值发生改变,copy后的对象也做出相应的改变

deepcopy()是深拷贝,拷贝可变对象的所有元素,deepcopy后的可变元素发生改变,与原对象没有任何的关系

11、简述pyhton的作用域以及python搜索变量的顺序

python的作用域简单说就是一个变量的命名空间。代码中变量被赋值的位置,就决定了哪些范围的对象可以访问这个变量,这个范围就是变量的作用域。在python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域。python的变量名解析机制也称为LEGB法则:本地作用域(local)-->当前作用域被嵌入的本地作用域(Enclosing locals)-->全局/模块作用域(Global)-->内置作用域(Built-in)

、、

12、简述__new__和__init__的区别

创建一个新实例时调用__new__,初始化一个实例时用__init__,这是它们最本质的区别。

new方法回返回所构造的对象,init则不会

new函数必须以cls作为第一个参数,而init则以self作为其第一个参数

13、python垃圾回收机制

Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。

(a)、引用计数:PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。

优点:简单 实时性

缺点:维护引用计数消耗资源 循环引用

(b)、标记-清楚机制:基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。

(c)、分代回收:分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。

Python默认定义了三代对象集合,索引数越大,对象存活时间越长。

14、python中@property有什么作用?如何实现成员变量只读属性?

@property装饰器就是负责把一个方法变成属性调用,通常用在属性的get方法和set方法,通过设置@property可以实现实例成员变量的直接访问,又保留了参数的检查。另外通过设置get方法而不定义set方法可以实现成员变量的只读属性。

15、*args和**kwargs

*args:代表位置参数,它会接收任意多个参数并把这些参数作为元组传递给函数

**kwargs:代表关键字参数,允许你使用没有事先定义的参数名,位置参数一定要放在关键字参数的前面

16、with的作用?

with语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。

17、在python中如何实现多线程

CPU进行资源分配和调度的基本单位,线程是进程的一部分,是比进程更小的能独立运行的基本单位,一个进程下的多个线程可以共享该进程的所有资源

如果IO操作密集,则可以多线程运行效率高,缺点是如果一个线程崩溃,都会造成进程的崩溃

一个线程就是一个轻量级进程,多线程能让我们一次执行多个线程,

因为python中有GIL(全局解释器锁)的存在,确保一次执行单个线程。一个线程保存GIL并在将其传递给下个线程之前执行一些操作,这回让我们产生并行运行的错觉。但实际上,只是线程在 CPU上轮流运行。当然,所有的传递回增加程序执行的内存压力。

18、python中的pass语句是什么?

本质上pass语句没有实际的意义,pass语句是一个占位符,也可以当作一个标记。

19、什么是进程、线程、协程:

进程:是资源分配的最小单位,是系统进行资源分配和调度的基本单元(是计算机中的程序关于某数据集上的一次运行活动)真正意义上的并发,充分利用计算机的多核机制,多进程的运行是独立的,互不干扰的,若是实现数据的传递,需要用通道,队列

线程: 是程序执行流的最小单元(有时被称为轻量级进程),依附于进程存在,在运行中共享内存,共享资源,主要是解决I/O操作

协程:在一个线程内,没有线程切换的开销大

20、守护线程:

SetDaemon 将线程声明为守护线程,必须在start()方法调用之前设置,如果不设置为守护线程程序会被无限挂起。当我们在程序运行中,执行一个主线程,如果主线程有创建一个子线程,主线程和子线程就兵分两路,分别运行,当主线程完成,想退出是,会检验子线程是否完成,如果子线程未完成,则主线程等待子线程完成后在退出,但是有时候我们需要的是只要主线程完成了,不管子线程是否完成,都要和主线成一起退出,这是就可以用setDaemon方法

21、线程分为哪几种使用方式

守护线程,主线程,子线程

22、线程中join方法的作用:

join工作完成的就是线程的同步,即主线程任务结束之后,进入阻塞状态,一直等待其他子线程执行结束之后,主线程再终止

join有一个timeout参数

1、当设置守护线程是,含义是主线程对于子线程等待timeout的时间将会杀死该子线程,简单的来说,就是给每一个子线程一个timeout的时间,让他去执行,时间一到不管任务有没有完成,直接杀死

2、没有设置守护线程,主线程将会等待timeout的累加和这样的一段时间,时间一到,主线程结束,但是并没有杀死子线程,程序依然可以继续执行,直到子线程全部结束,程序结束

23、线程互斥锁的概念?

用于锁住临界资源,当一个线程需要访问临界资源这个资源没有锁住,那么访问这个资源的同时给这个资源加上锁,这样别的线程

就无法访问这个临界资源了,直到这个线程访问完这个临界资源。这个就是互斥锁的概念

24、什么死锁和递归锁:

线程上锁后可能会出现死锁的情况

所谓死锁:就是指两个或两个以上的进程或线程在执行过程中,因争夺资源造成的一种互相等待的现象,若无外力作用,他们都将无法推进下去,此时称系统处于死锁状态,或系统产生了死锁

25、开启线程的两种方式?

函数式和继承式(继承是继承threading.Thread)

26、全局解释器锁GIL介绍

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

可以肯定的一点是,保护不同的数据的安全,就应该加不同的锁。

为了支持多线程机制,一个基本的要求就是需要实现不同线程对共享资源的互斥,所以引入了GIL

GIL并不是python的特性,它是在实现python解释器(cpython)时引入的一个概念。补充:因为python默认的执行环境是cpython,所以想当然的把GIL归结为python语言的缺陷。所以这里要先声明明确一点:GIL并不是python的特性,pyhton完全可以不依赖于GIL

GIL缺点:多处理器退化为单处理器;优点:避免大量的加锁解锁的操作

27、协程和生成器

协程一定是生成器,生成器不一定是协程

协程是数据的消费者,生成器是数据的生产者

协程的概念

协程是在一个线程执行过程中可以在一个子程序的预定或者随机位置中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。他本身是一种特殊的子程序或者称作函数。

协程,又称微线程,纤程,英文名Coroutine。协程的作用,是在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。但这一过程并不是函数调用(没有调用语句),这一整个过程看似像多线程,然而协程只有一个线程执行。

简单点说协程是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态。

28、进程、线程和协程

(1、进程

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

(2、线程

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

(3、协程

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

(1、进程多与线程比较

线程是指进程内的一个执行单元,也是进程内的可调度实体。线程与进程的区别:

1) 地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间

2) 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源

3) 线程是处理器调度的基本单位,但进程不是

4) 二者均可并发执行

5) 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制

(2、协程多与线程进行比较

1) 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。

2) 线程进程都是同步机制,而协程则是异步

3) 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态

29、设计模式的类别

创建型模式:(5)工厂方法,抽象工厂,单例模式,建造者模式,原型模式。

结构型模式:(7)适配器模式,装饰器模式,代理模式,外观模式,组合模式,亨元模式

行为型模式:(11)策略模式,模板方法模式,观察模式,迭代子模式,责任链模式,命令模式,备忘录模式,中介者模式,解释器模式。

30、for循环的原理

for循环遍历其实就是取出可迭代对象中的迭代器然后对迭代器不断的间隙next(),在处理掉最后一次对迭代器next()时抛出异常StopIteration为止。

(1、首先判断对象是否是一个可迭代对象,不是的话直接报错,抛出typeerror异常,调用__iter__方法,返回一个迭代器

(2、不断地调用迭代器的__next__方法,每次按序返回迭代器中的一个值

(3、迭代到最后,没有更多元素了,就抛出异常stopiteration,这个异常python自己处理,不会暴露给开发者

31、面向过程和面向对象

面向过程:是函数式编程,每一个函数代表一个功能的实现,程序按顺序的执行,可扩展性不强,但代码便于理解

面向对象:把某一类的对象封装成一个类,关注的是对象可以实现的行为,属性

32、python中的os和sys模块的作用

os模块负责程序与操作系统的交互,提供了访问操作系统底层的接口

sys模块负责程序与pyhton解释器的交互,提供操作python运行函数的接口

33、类函数的self参数代表什么意思

代表一个类的实例

34、类里的私有属性和方法如何定义

加上双下划线代表私有的

单下线表示受保护的的类型,即保护类型,即保护类型只能靠允许其本身与子类进行访问。若内部变量表示,如:当使用from m import进行引入时,不会将一个下划线开头的对象引入

35、面向对象编程的好处

面向对象的三大特性:继承,封装,多态

继承:实现代码的复用,

封装:结构清晰,可读性好,

多态:不同类型的使用同一个方法,得到不同的结果

1.维护简单,可读性好,2.效率高3.容易扩展

36、__slots__的作用

没有__slots__的限制,我们可以为一个实例添加额外的属性,当我们制定了__slots__时,就只能使用__slots__允许我们使用的,限制一个类的属性

37、判断类属性是否存在,设置类属性,获得类属性

类属性是否存在:hasattr

设置类属性:setattr

获取类属性:getattr

38、实例属性和类属性:

实例属性:是实例对象私有的

类属性:类属性是类本身所拥有的属性,他是类实例共有的属性,类属性就相当于全局变量,实例对象共有的属性

39、什么是模块,什么是包,包和模块的区别

模块:是对一些功能的封装

包:模块的集合

包包含模块,模块包含具体的功能

类是对数据进行封装的

40、from import 和 import导入的区别

import:导入一个模块,注:相当于导入的是一个文件夹,是个相对路径

from import:导入了一个模块中的一个函数,注:相当于导入的是一个文件夹中的文件,是个绝对路径

41、对同步和异步,阻塞和非阻塞的理解

同步:程序按顺序的执行,上一个任务没有执行完毕,就不执行下一个任务

异步:就是一个异步过程调用发出后,调用者不能立即得到结果。实际处理这个调用的部件在完成后,通过状态,通知回调来通知调用者。

阻塞:阻塞调用是指调用结果返回前,当前线程会被暂时挂起,(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,及线程暂停运行)。函数只有在得到结果之后才会返回

当前的线程不执行别的事情,一直在等待

非阻塞:就是指当前线程在受到阻塞时,可以去执行其他的任务

同步io和异步io的区别就在于:数据拷贝的时候进程是否阻塞

阻塞io和非阻塞io的区别在于:应用程序的调用是否立即返回

42、简述函数式编程

在函数式编程中,函数是基本单元,每一个函数代表一个功能的实现,代码简单,但逻辑上不够清晰,可扩展性不够好,

43、新式类和旧式类的区别

为了统一类和类型,python在2.2版本引进来新式类。在2.1版本中,类和类型是不同的

为了确保使用的是新式类,有以下方法:

放在类模块代码的最前面__metaclass__=type

从内建类object直接或者间接地继承

在pyhton3版本中,默认所有的类都是新式类

44、单例模式和工厂模式

单例模式指的是一个类每次新创建的对象指定的都是第一次创建这个对象的地址

工厂模式指的是把某一个功能,按照类别的不同,实现不同的方法,工厂类通过调用不同的类实现不同的组装,形成一个完整的功能

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值