【Python面试题】【基础篇】持续更新中...

  1. 云栖社区>
  2. 博客>
  3. 正文

【Python面试题】【基础篇】持续更新中...

awesome@qa 2018-02-26 17:08:23 浏览4701
展开阅读全文
1、不错的面试题网站

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. 浅拷贝:创建一个新的对象,但它包含的是对原始对象中包含项的引用(如果用引用的方式修改其中一个对象,另外一个也会修改改变){1,完全切片方法;2,工厂函数,如list();3,copy模块的copy()函数}
  3. 深拷贝:创建一个新的对象,并且递归的复制它所包含的对象(修改其中一个,另外一个不会改变){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、



网友评论

登录后评论
0/500
评论
awesome@qa
+ 关注