【Python面试题】【基础篇】持续更新中...
1、不错的面试题网站 http://www.codingonway.com/ https://github.com/revotu/python-interviews https://github.com/taizilongxu/interview_python 2、Python是如何进行内存管理的? Python GC主要使用 引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过 “标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过 “分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。 3、垃圾回收机制 当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。 当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含一个对其他对象的应用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。 4、引用计数机制 PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除、引用超出作用域或被重新赋值,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。 优点: 1. 简单 2. 实时性 缺点: 1. 维护引用计数消耗资源 2. 循环引用 5、标记-清除 基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。 6、分代回收 分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。 Python默认定义了三代对象集合,索引数越大,对象存活时间越长。 举例: 当某些内存块M经过了3次垃圾收集的清洗之后还存活时,我们就将内存块M划到一个集合A中去,而新分配的内存都划分到集合B中去。当垃圾收集开始工作时,大多数情况都只对集合B进行垃圾回收,而对集合A进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合B中的某些内存块由于存活时间长而会被转移到集合A中,当然,集合A中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。 7、内存池机制 Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。 Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。 Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc。 对于Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。 8、函数参数 普通参数 即在调用函数时必须按照准确的顺序来进行参数传递。 def fun(name): print('Hello', name) fun('World')#Hello World 默认参数 即参数含有默认值,在调用函数时可以进行参数传递,若没有进行参数传递则使用默认值,要注意,默认参数必须在普通参数的右侧(否则解释器无法解析) def fun(name, age=1): print('Hello', name, age, '年') fun('World') # Hello World 1 年 元组参数,即 *args 参数格式化存储在一个元组中,长度没有限制, 必须位于普通参数和默认参数之后 def fun(name, age=1, *args): print('Hello', name, age, '年') # Hello World 1 年 print(args) # ('I', 'love', 'it') for i in args: print(i) fun('World', 1, 'I', 'love', 'it') 输出结果: I love it 字典参数,即 **kwargs 参数格式化存储在一个字典中, 必须位于参数列表的最后面 def fun(name, age=1, *args, **kwargs): print('Hello', name, age, '年') # Hello World 1 年 print(args) # ('I', 'love', 'it') for i in args: print(i) print(kwargs) # {'my': 'jack', 'like': 'girl'} for m in kwargs: print(m, ':', kwargs[m]) fun('World', 1, 'I', 'love', 'it', my='jack', like='girl') 输出结果: Hello World 1 年 ('I', 'love', 'it') I love it {'my': 'jack', 'like': 'girl'} my : jack like : girl 9、Python里面如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别) 赋值(=),就是创建了对象的一个新的引用,修改其中任意一个变量都会影响到另一个。 浅拷贝:创建一个新的对象,但它包含的是对原始对象中包含项的引用(如果用引用的方式修改其中一个对象,另外一个也会修改改变){1,完全切片方法;2,工厂函数,如list();3,copy模块的copy()函数} 深拷贝:创建一个新的对象,并且递归的复制它所包含的对象(修改其中一个,另外一个不会改变){copy模块的deep.deepcopy()函数} copy 仅拷贝对象本身,而不拷贝对象中引用的其它对象。 deepcopy 除拷贝对象本身,而且拷贝对象中引用的其它对象。代码实例 import copy a = [1, 2, 3, 4, ['a', 'b']] #原始对象 b = a #赋值,传对象的引用 c = copy.copy(a) #对象拷贝,浅拷贝 d = copy.deepcopy(a) #对象拷贝,深拷贝 a.append(5) #修改对象a a[4].append('c') #修改对象a中的['a', 'b']数组对象 print 'a = ', a print 'b = ', b print 'c = ', c print 'd = ', d 输出结果: a = [1, 2, 3, 4, ['a', 'b', 'c'], 5] b = [1, 2, 3, 4, ['a', 'b', 'c'], 5] c = [1, 2, 3, 4, ['a', 'b', 'c']] d = [1, 2, 3, 4, ['a', 'b']] 10、Python中重载 函数重载主要是为了解决两个问题: 1. 可变参数类型 2. 可变参数个数 解释一: 那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。 那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。 鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。 解释二: 简单来说,Python中为什么不需要重载,重载要解决的是参数类型和参数个数的问题,对于类型,python不像是c语言整型要写int,字符串要写str,,,这些python都不需要。 那么需要解决的就是传递参数个数问题,此时python可以传递列表呀,字典呀,可以使用*arg和**args呀,所以python根本不需要重载。 11、Python中单下划线和双下划线 __foo__:一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突. _foo:一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式. __foo:这个有真正的意义:解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名. 12、 __new__和__init__的区别 1. __new__是一个静态方法,而__init__是一个实例方法. 2. __new__方法会返回一个创建的实例,而__init__什么都不返回. 3. 只有在__new__返回一个cls的实例时,后面的__init__才能被调用. 4. 当创建一个新实例时调用__new__,初始化一个实例时用__init__. 13、单例模式 该模式的主要目的是确保某一个类只有一个实例存在 使用模块 其实,Python 的模块就是天然的单例模式, 因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。 # mysingleton.py class My_Singleton(object): def foo(self): pass my_singleton = My_Singleton() 将上面的代码保存在文件 mysingleton.py 中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象 # to use from mysingleton import my_singleton my_singleton.foo() 使用类class Singleton(object): def __init__(self): pass @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance 基于__new__方法实现(推荐使用,方便) 当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式 当我们实现单例时,为了保证线程安全需要在内部加入锁,未加锁部分并发执行,加锁部分串行执行,速度降低,但是保证了数据安全 import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): pass def __new__(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = object.__new__(cls) return Singleton._instance 14、创建字典的方法 直接创建 dict = {'name':'earth', 'port':'80'} 工厂方法 items=[('name','earth'),('port','80')] dict2=dict(items) fromkeys()方法 dict1={}.fromkeys(('x','y'),-1) dict={'x':-1,'y':-1} dict2={}.fromkeys(('x','y')) dict2={'x':None, 'y':None} 15、数组和元组之间的区别是什么? 相同点 首先,列表与元组都是容器,是一系列的对象; 其次,二者都可以包含任意类型的元素甚至可以是一个序列。 不同点 列表和元组的“技术差异”是,列表是可变的,而元组是不可变的。 16、Python都有那些自带的数据结构? Python自带的数据结构分为可变的和不可变的。 可变的有: 集合 字典 不可变的有: 字符串 元组 数字 数组 17、推导式 列表(list)推导式 功能:是提供一种方便的列表创建方法,所以,列表解析式返回的是一个列表 例子: >>> li=[i*2 for i in range(10) if i % 2 == 0] >>> print li [0, 4, 8, 12, 16] 列表解析式最擅长的方式就是对整个列表分别做相同的操作,并且返回得到一个新的列表 字典(dict)推导式 集合(set)推导式 功能:集合推导式跟列表推导式差不多,都是对一个列表的元素全部执行相同的操作,但集合是一种无重复无序的序列 区别:跟列表推到式的区别在于:1.不使用中括号,使用大括号;2.结果中无重复;3.结果是一个set()集合,集合里面是一个序列 >>> squared={i*2 for i in [1,1,2]} >>> print squared set([2, 4]) 18、Python是如何进行类型转换的? Python提供了将变量或值从一种类型转换成另一种类型的内置函数。比如int函数能够将符合数学格式数字型字符串转换成整数。否则,返回错误信息。 19、Python是如何被解释的? Python是一种解释性语言,Python解释器会将源代码转换成中间语言,之后再翻译成机器码再执行。 20、.Python中的负索引是什么? Python中的序列索引可以是正也可以是负。如果是正索引,0是序列中的第一个索引,1是第二个索引。如果是负索引,(-1)是最后一个索引而(-2)是倒数第二个索引。 21、Python的参数传递是值传递还是引用传递 1).Python的参数传递有: 位置参数 默认参数, 可变参数, 关键字参数 2).函数的传值到底是值传递还是引用传递,要分情况 a. 不可变参数用值传递: 像整数和字符串这样的不可变对象,是通过拷贝进行传递的,因为你无论如何都不可能在原处改变不可变对象 b. 可变参数是用引用传递的 比如像列表,字典这样的对象是通过引用传递,和C语言里面的用指针传递数组很相似,可变对象能在函数内部改变. 22、Xrange和range的区别是什么? xrange 函数说明:用法与range完全相同,所不同的是生成的不是一个数组,而是一个生成器。 >>> range(5) [0, 1, 2, 3, 4] >>> xrange(5) xrange(5) >>> list(xrange(5)) [0, 1, 2, 3, 4] 23、单引号,双引号,三引号的区别 1),单引号和双引号主要用来表示字符串 区别: 若你的字符串里面本身包含单引号,必须用双引号 比如:"can't find the log\n" 2).三引号 三单引号:'''python ''',也可以表示字符串一般用来输入多行文本,或者用于大段的注释 三双引号:"""python""",一般用在类里面,用来注释类 24、类和实例 https://www.cnblogs.com/crazyrunning/p/6945183.html 25、类变量和实例变量 实例变量是对于每个实例都独有的数据,而类变量是该类所有实例共享的属性和方法。 class Dog: kind = 'canine' # class variable shared by all instances def __init__(self, name): self.name = name # instance variable unique to each instance 类Dog中,类属性kind为所有实例所共享; 实例属性name为每个Dog的实例独有。 26、类对象和实例对象 类对象 类对象仅支持两个操作: 实例化;使用instance_name = class_name()的方式实例化,实例化操作创建该类的实例。 属性引用;使用class_name.attr_name的方式引用类属性。 实例对象 实例对象是类对象实例化的产物,实例对象仅支持一个操作: 属性引用;与类对象属性引用的方式相同,使用instance_name.attr_name的方式。 27、属性绑定 我们说的属性绑定,首先需要一个可变对象,才能执行绑定操作,使用objname.attr = attr_value的方式,为对象objname绑定属性attr。 这分两种情况: 若属性attr已经存在,绑定操作会将属性名指向新的对象; 若不存在,则为该对象添加新的属性,后面就可以引用新增属性。 类属性绑定 类属性的绑定发生在两个地方: 类定义时; 运行时任意阶段。 在类定义中,类属性的绑定并没有使用objname.attr = attr_value的方式,这是一个特例,其实是等同于后面使用类名绑定属性的方式。 因为是动态语言,所以可以在运行时增加属性,删除属性。 实例属性绑定 与类属性绑定相同,实例属性绑定也发生在两个地方: 类定义时; 运行时任意阶段。 类实例有两个特殊之处: __init__在实例化时执行 Python实例调用方法时,会将实例对象作为第一个参数传递 因此,__init__方法中的self就是实例对象本身 28、属性引用 类属性引用 类属性的引用,肯定是需要类对象的,属性分为两种: 数据属性 函数属性 实例属性引用 使用实例对象引用属性稍微复杂一些,因为实例对象可引用类属性以及实例属性。但是实例对象引用属性时遵循以下规则: 总是先到实例对象中查找属性,再到类属性中查找属性; 属性绑定语句总是为实例对象创建新属性,属性存在时,更新属性指向的对象。 29、 Python 中的 is 和 == is is the identity comparison. #比较引用是否相同 == is the equality comparison. #比较内容是否相同 python中新建变量时,并不需要指定类型,因为每个变量实际上存储的是一个引用,就是指向一个对象实体的指针。 is 判断的就是这个指针的值是否相同,如果相同则表示两个变量指向同一个对象实体。 而==则比较它们的内容是否相同 30、isinstance 和 type 的区别 class A : pass class B ( A ): pass isinstance ( A (), A ) # returns True type ( A ()) == A # returns True isinstance ( B (), A ) # returns True type ( B ()) == A # returns False 区别就是: type()不会认为子类是一种父类类型。 isinstance()会认为子类是一种父类类型。 31、