Python面试常见问题及解答(Basics )

1、Python的语言类型
Python是动态类型、强类型、解释型语言
区分动态类型/静态类型 - 编译阶段还是运行阶段检查类型
区分强类型/弱类型 – 运行时是否自动转换类型
“鸭子模型”

2、Python中的可变对象和不可变对象

不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。
可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变。
Python 中,数值类型(int 和float)、字符串str、元组tuple 都是不可变类型。而列表list、字典dict、集合set 是可变类型。

3、赋值、深浅拷贝的区别

1> 赋值
在Python 中,对象的赋值就是简单的对象引用。
赋值操作(包括对象作为参数、返回值)不会开辟新的内存空间,它只是复制了对象的引用。
2> 浅拷贝
浅拷贝会创建新对象,其内容非原对象本身的引用,而是原对象内第一层对象的引用。
浅拷贝有三种形式:切片操作、工厂函数、copy 模块中的copy 函数。
3> 深拷贝
深拷贝只有一种形式,copy 模块中的deepcopy()函数。
深拷贝和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。因此,它的时间和空间开销要高。
4> 拷贝的注意点?
①、深浅拷贝都是对源对象的复制,占用不同的内存空间。
②、不可变类型的对象,对于深浅拷贝毫无影响,最终的地址值和值都是相等的。
③、可变类型:
= 浅拷贝: 值相等,地址相等
copy浅拷贝:值相等,地址不相等
deepcopy深拷贝:值相等,地址不相等

4、List 和 Tuple 的区别和联系

都是可迭代集合对象,list、tuple可以互相转换
list、set可变,tuple不可变
tuple一旦初始化就不能修改,而且没有append() insert()这些方法,可以获取元素但不能赋值变成另外的元素
list是一种有序的集合,可以随时添加和删除其中的元素

5、Python中的进程、线程、协程的理解

进程:系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享,开销大。
多进程实现:multiprocessing
线程:调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。
多线程实现:threading
协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
协程实现:yield,greenlet,gevent

6、常用的Python标准库以及第三方库

标准库:os 操作系统,time 时间,random 随机,pyMySQL 连接数据库,threading 线程,multiprocessing进程,queue 队列。
第三方库:Django 和flask 也是第三方库,requests,virtualenv,selenium,scrapy,xadmin,celery,re,hashlib,md5。
关联:
python模块 import导入顺序风格
①、Python 标准库模块 ②、Python 第三方模 ③、应用程序自定义模块

7、Python中类方法、类实例方法和静态方法的区别

类方法:是类对象的方法,在定义时需要在上方使用“@classmethod”进行装饰,形参为cls,表示类对象,类对象和实例对象都可调用;
类实例方法:是类实例化对象的方法,只有实例对象可以调用,形参为self,指代对象本身;
静态方法:是一个任意函数,在其上方使用“@staticmethod”进行装饰,可以用对象直接调用,静态方法实际上跟该类没有太大关系。

8、Python的内存管理机制及调优手段

内存管理机制:引用计数、垃圾回收、内存池机制

一、对象的引用计数机制

Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数。
1、引用计数+1的情况
• 对象被创建,例如:a = 1
• 对象被引用,例如:b = a
• 对象被作为参数,传入到一个函数中,例如:func(a)
• 对象作为一个元素,存储在容器中,例如:list1 = [a,a]
2、引用计数-1的情况
• 对象的别名被显式销毁,例如:del a
• 对象的别名被赋予新的对象,例如:a = 2
• 一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
• 对象所在的容器被销毁,或从容器中删除对象
3、标记 - 清除
如果两个对象的引用计数都为1,但是仅仅存在他们之间的循环引用,那么这两个对象都是需要被回收的,也就是说,它们的引用计数虽然表现为非0,但实际上有效的引用计数为0。所以先将循环引用摘掉,就会得出这两个对象的有效计数。

二、垃圾回收

1,当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。

2,当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含一个对其他对象的应用,

因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。

三、内存池机制

Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。

1,Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。

2,Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc。

3,对于Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。

调优手段
1.手动垃圾回收 gc.collect()
2.调高垃圾回收阈值
3.避免循环引用(手动解循环引用和使用弱引用)

9、什么是lambda函数?有什么好处?

lambda 函数是一个可以接收任意多个参数(包括可选参数)并且返回单个表达式值的函数
1>lambda 函数比较轻便,即用即仍,很适合需要完成一项功能,但是此功能只在此一处使用,连名字都很随意的情况下;
2>匿名函数,一般用来给filter, map 这样的函数式编程服务;
3>作为回调函数,传递给某些应用,比如消息处理

10、生成器、迭代器、可迭代对象的区别

可迭代对象,实现__iter__方法(可以直接作用于for循环的对象统称为可迭代对象);
迭代器是一个抽象的概念,任何对象,如果它的类有next 方法和iter 方法返回自己本身,对于string、list、dict、tuple 等这类容器对象,使用for 循环遍历是很方便的。在后台for 语句对容器对象调用iter()函数,iter()是python 的内置函数。iter()会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是python 的内置函数。在没有后续元素时,next()会抛出一个StopIteration 异常。
生成器(Generator) 是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数,只是在需要返回数据的时候使用yield 语句。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)
区别:生成器能做到迭代器能做的所有事,而且因为自动创建了iter()和next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出StopIteration 异常。

11、装饰器

python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象(函数的指针)
装饰器最大的作用就是对于我们已经写好的程序,我们可以抽离出一些雷同的代码组建多个特定功能的装饰器,这样我们就可以针对不同的需求去使用特定的装饰器,这时因为源码去除了大量泛化的内容而使得源码具有更加清晰的逻辑。

#为函数添加计时功能的装饰器
import time

def decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        func()
        end_time = time.time()
        print(end_time - start_time)

    return wrapper

@decorator 
def func():
    time.sleep(0.8)

func() # 函数调用
# 输出:0.800644397735595

12、Python中的魔法方法
魔法方法就是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被Python 所调用,你可以定义自己想要的行为,而这一切都是自动发生的。它们经常是两个下划线包围来命名的(比如_init__lt_),Python 的魔法方法是非常强大的,所以了解其使用方法也变得尤为重要!
_init_ 构造器,当一个实例被创建的时候初始化的方法。但是它并不是实例化调用的第一个方法。
_new__才是实例化对象调用的第一个方法,它只取下cls 参数,并把其他参数传给__init_。__new__很少使用,但是也有它适合的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。
__call__允许一个类的实例像函数一样被调用。
__getitem__定义获取容器中指定元素的行为,相当于self[key] 。
__getattr__定义当用户试图访问一个不存在属性的时候的行为。
__setattr__定义当一个属性被设置的时候的行为。
__getattribute__定义当一个属性被访问的时候的行为。
更多魔法方法

你可能感兴趣的:(Python面试常见问题及解答(Basics ))