python面试题(二)

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> python面试题(二)

点击上方”python宝典”,关注获取python全套视频,

技术文章第一时间送达!

1 Python3和Python2中 int 和 long的区别?

Python 2有为非浮点数准备的int和long类型。int类型的最大值不能超过sys.maxint,而且这个最大值是平台相关的。可以通过在数字的末尾附上一个L来定义长整型,显然,它比int类型表示的数字范围更大。在Python 3里,只有一种整数类型int,大多数情况下,它很像Python 2里的长整型。由于已经不存在两种类型的整数,所以就没有必要使用特殊的语法去区别他们。

2 文件操作时:xreadlines和readlines的区别?

read(size) 

读入指定大小的内容,以byte为单位,size为读入的字符数,返回str类型

readline()

readline()读取一行内容,放到一个字符串变量,返回str类型。

readlines()

readlines() 读取文件所有内容,按行为单位放到一个列表中,返回list类型。

xreadlines() 

返回一个生成器,来循环操作文件的每一行。循环使用时和readlines基本一样,但是直接打印就不同

3列举布尔值为False的常见值?

下面的值在作为布尔表达式的时候,会被解释器看作假(false)

False    None    0   “”    ()    []   {}

换句话说,也就是标准值False和None,所有类型的数字0(包括浮点型,长整型和其他类型),空序列(比如空字符串、元组和列表)以及空的字典都为假。其他的一切都被解释为真,包括特殊值True.

也就是说Python中的所有值都能被解释为真值。”标准的“布尔值为True和False。事实上,True和False只不过是1和0的一种“华丽”的说法而已—-看起来不同,但是作用相同。

4 lambda表达式格式以及应用场景?

对于简单的函数,也存在一种简便的表示方式,即:lambda表达式


#普通函数
def func(a):
    return a+1
print 'test1_func0:',func(1000)
#lambda表达式 
func0 = lambda a:a+1
print 'test2_func0:',func0(1000)

上面这种方法,都实现了将1000+1的结果打印出来这个功能,但是用下面

lambda存在意义就是对简单函数的简洁表示。

说道lambda,这里再赠送一些可以给lambda加buff小伙伴:

map函数

我们使用map函数将会对列表中的所有元素进行操作。map有两个参数(函数,列表),它会在内部遍历列表中的每一个元素,执行传递过来的函数参数。在输出到新列表中。


li = [11, 22, 33]
new_list = map(lambda a: a + 100, li)
输出:[111, 122, 133]

reduce函数

对于序列内所有元素进行累计操作:


lst = [11,22,33]
func2 = reduce(lambda arg1,arg2:arg1+arg2,lst)
print 'func2:',func2
输出:func2: 66

filter函数

他可以根据条件对数据进行过滤:


li = [11, 22, 33]
new_list = filter(lambda arg: arg  22, li)
print new_list
输出:[33]

divmod()

 函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)。


divmod(7, 2)
(3, 1)
 divmod(8, 2)
(4, 0)

zip() 函数

用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。


a = [1,2,3]
 b = [4,5,6]
 c = [4,5,6,7,8]
 zipped = zip(a,b)     # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
 zip(a,c)              # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
 zip(*zipped)          # 与 zip 相反,*zipped 可理解为解压,返回二维矩阵式
[(1, 2, 3), (4, 5, 6)]

*5 *arg和**kwarg作用*

首先我们可以定一个简单的函数, 函数内部只考虑required_arg这一个形参(位置参数)


def exmaple(required_arg):
    print required_arg
exmaple("Hello, World!")
 Hello, World!

那么,如果我们调用函数式传入了不止一个位置参数会出现什么情况?当然是会报错!

*arg和**kwarg 可以帮助我们处理上面这种情况,允许我们在调用函数的时候传入多个实参


def exmaple2(required_arg, *arg, **kwarg):
    if arg:
        print "arg: ", arg
    if kwarg:
        print "kwarg: ", kwarg
exmaple2("Hi", 1, 2, 3, keyword1 = "bar", keyword2 = "foo")
 arg:  (1, 2, 3)
 kwarg:  {'keyword2': 'foo', 'keyword1': 'bar'}

从上面的例子可以看到,当我传入了更多实参的时候

  • *arg会把多出来的位置参数转化为tuple
  • **kwarg会把关键字参数转化为dict
  • **kwarg会把关键字参数转化为dict

    6 is和==的区别

    在讲is和==这两种运算符区别之前,首先要知道Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)。

    is和==都是对对象进行比较判断作用的,但对对象比较判断的内容并不相同。下面来看看具体区别在哪。

    ==比较操作符和is同一性运算符区别

    ==是python标准操作符中的比较操作符,用来比较判断两个对象的value(值)是否相等,例如下面两个字符串间的比较:

    
     a = 'cheesezh'
     b = 'cheesezh'
     a == b
    True
    

    is也被叫做同一性运算符,这个运算符比较判断的是对象间的唯一身份标识,也就是id是否相同。通过对下面几个list间的比较,你就会明白is同一性运算符的工作原理:

    
     x = y = [4,5,6]
     z = [4,5,6]
     x == y
    True
     x == z
    True
     x is y
    True
     x is z
    False
    
     print id(x)
     print id(y)
     print id(z)
    

    7 简述Python的深浅拷贝以及应用场景?

    可变对象-不可变对象

    在Python中不可变对象指:一旦创建就不可修改的对象,包括字符串,元祖,数字

    在Python中可变对象是指:可以修改的对象,包括:列表、字典

    
     L1 = [2,3,4]      #L1变量指向的是一个可变对象:列表  
     L2 = L1           #将L1值赋给L2后,两者共享引用同一个列表对象[1,2,3,4]  
     L1[0] = 200       #因为列表可变,改变L1中第一个元素的值  
     L1; L2            #改变后,L1,L2同时改变,因为对象本身值变了  
    [200, 3, 4]  
    [200, 3, 4]
    

    如果不想改变列表L2的值,有两种方法:切片 和 copy模块

    
     L1 = [2,3,4]   
     L2 = L1  
     id(L1);id(L2)     #共享引用一个可变对象  
    45811784L  
    45811784L  
     L2 = L1[:]        #切片操作  
     id(L1);id(L2)     #切片后,对象就不一样了  
    45811784L  
    45806920L  
     L1[0] = 200  
     L1;L2             #L1发生改变,L2没有变化  
    [200, 3, 4]  
    [2,   3, 4]
    

    拷贝

    切片技术应用于所有的序列,包括:列表、字符串、元祖 

    但切片不能应用于字典。对字典只能使用D.copy()方法或D.deepcopy()方法.

    python面试题(二)

    深浅拷贝,即可用于序列,也可用于字典

    
    import copy
    X = copy.copy(Y)      #浅拷贝:只拷贝顶级的对象,或者说:父级对象
    X = copy.deepcopy(Y)  #深拷贝:拷贝所有对象,顶级对象及其嵌套对象。或者说:父级对象及其子对象
    

    如果字典只有顶级对象:

    python面试题(二)

    如果字典中嵌套对象:

    python面试题(二)

    【结论】

  • 深浅拷贝都是对源对象的复制,占用不同的内存空间
  • 如果源对象只有一级目录的话,源做任何改动,不影响深浅拷贝对象
  • 如果源对象不止一级目录的话,源做任何改动,都要影响浅拷贝,但不影响深拷贝
  • 序列对象的切片其实是浅拷贝,即只拷贝顶级的对象
  • 如果源对象只有一级目录的话,源做任何改动,不影响深浅拷贝对象

    序列对象的切片其实是浅拷贝,即只拷贝顶级的对象

    8 Python垃圾回收机制?

    import sys  sys.getrefcount()查看引用计数
    字符串中间有空格!?等会重新创建新的字符串
     总结

    1. 小整数[-5,257)共用对象,常驻内存,不会被释放。
    2. 单个字符共用对象,常驻内存。
    3. 单个单词,不可修改,默认开启intern机制,共用对象,引用计数为0,则销毁 。
    4. 大整数不共用内存,引用计数为0,销毁 .
    5. 数值类型和字符串类型在 Python 中都是不可变的,这意味着你无法修改这个对象的值,每次对变量的修改,实际上是创建一个新的对象 .
      Garbage collection(GC垃圾回收)
      python采用的是引用计数机制为主,标记-清除和分代收集(隔代回收、分代回收)两种机制为辅的策略

    引用计数机制的优点:

    1、简单

    2、实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。
    引用计数机制的缺点:
    维护引用计数消耗资源
    循环引用,解决不了

    gc模块

    3.1. 垃圾回收机制
     导致引用计数+1的情况
    1.对象被创建,例如a = “hello”
    2.对象被引用,例如b=a
    3.对象被作为参数,传入到一个函数中,例如func(a)
    4.对象作为一个元素,存储在容器中,例如list1=[a,a]

    1. 常用函数
      1、gc.set_debug(flags) 设置gc的debug日志,一般设置为gc.DEBUG_LEAK
      2、gc.collect([generation]) 显式进行垃圾回收,可以输入参数,0代表只检查零代的对象,1代表检查零,一代的对象,2代表检查零,一,二代的对象,如果不传参数,执行一个full collection,也就是等于传2。 在python2中返回不可达(unreachable objects)对象的数目
      3、gc.get_threshold() 获取的gc模块中自动执行垃圾回收的频率。
      4、gc.set_threshold(threshold0[, threshold1[, threshold2]) 设置自动执行垃圾回收的频率。
      5、gc.get_count() 获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表
      Python的GC模块主要运用了引用计数来跟踪和回收垃圾。在引用计数的基础上,还可以通过“标记-清除”解决容器对象可能产生的循环引用的问题。通过分代回收以空间换取时间进一步提高垃圾回收的效率。

    标记-清除

    标记-清除的出现打破了循环引用,也就是它只关注那些可能会产生循环引用的对象
    缺点:该机制所带来的额外操作和需要回收的内存块成正比。
    一旦这个差异累计超过某个阈值(700,10,10),则Python的收集机制就启动了,并且触发上边所说到的零代算法释放“浮动的垃圾”,并且将剩下的对象移动到一代列表。随着时间的推移,程序所使用的对象逐渐从零代列表移动到一代列表。通过这种方法,你的代码所长期使用的对象,那些你的代码持续访问的活跃对象,会从零代链表转移到一代再转移到二代。通过不同的阈值设置,Python可以在不同的时间间隔处理这些对象。Python处理零代最为频繁,其次是一代然后才是二代。

    隔代回收

    原理:将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。
    dir(builtins)查看内建属性
    __getattribute__内建属性。属性访问拦截器(方法和属性都可以被拦截),可以返回一个值:以后不要在__getattribute__方法中调用self.xxxx会引起递归时程序死掉
    map函数会根据提供的函数对指定序列做映射返回值是列表
    map(function, sequence[, sequence, …]) - list
    • function:是一个函数
    • sequence:是一个或多个序列,取决于function需要几个参数
    • 返回值是一个list
    filter函数python3返回的是生产器filter函数会对指定序列执行过滤操作
    filter(function or None, sequence) - list, tuple, or string
    • function:接受一个参数,返回布尔值True或False
    • sequence:序列可以是str,tuple,list
    list(filter(lambda x x%2==0,[1,2,3,4,5,6])—-[2,4,6]
    sorted函数-排序
    sorted(iterable, reverse=False) – new sorted list
     functools模块import functools
    partial函数(偏函数)把一个函数的某些参数设置默认值,返回一个新的函数,调用这个新函数会更简单。
    wraps函数 使用装饰器时,让外界看被装饰的函数时内容一致。
    例如,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变)。
    functools.wraps(func)

    9 求结果:

    
    v = dict.fromkeys(['k1','k2'],[])
    v['k1'].append('000')
    print(v)
    v['k1']=666
    print(v)
    
    {'k1': ['000'], 'k2': ['000']}
    {'k1': 666, 'k2': ['000']}
    

    10 求结果

    
    def num():
        return [lambda x:i*x for i in range(4)]
    print([m(2) for m in num()])
    
    [6, 6, 6, 6]
    

    以上代码的输出是 [6, 6, 6, 6] (而不是 [0, 2, 4, 6])。

    这个的原因是 Python 的闭包的后期绑定导致的 late binding,这意味着在闭包中的变量是在内部函数被调用的时候被查找。所以结果是,当任何 multipliers() 返回的函数被调用,在那时,i 的值是在它被调用时的周围作用域中查找,到那时,无论哪个返回的函数被调用,for 循环都已经完成了,i 最后的值是 3,因此,每个返回的函数 multiplies 的值都是 3。因此一个等于 2 的值被传递进以上代码,它们将返回一个值 6 (比如: 3 x 2)。

    (顺便说下,正如在 The Hitchhiker’s Guide to Python 中指出的,这里有一点普遍的误解,是关于 lambda 表达式的一些东西。一个 lambda 表达式创建的函数不是特殊的,和使用一个普通的 def 创建的函数展示的表现是一样的。)

    这里有两种方法解决这个问题。

    最普遍的解决方案是创建一个闭包,通过使用默认参数立即绑定它的参数。例如:

    
    def num():    
        return [lambda x, i=i : i * x for i in range(4)]
    

    另外一个选择是,你可以使用 functools.partial 函数:

    
    from functools import partial
    from operator import mul
    def num():
        return [partial(mul, i) for i in range(4)]
    
    python面试题(二)

    识别图中二维码,欢迎关注python宝典

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> python面试题(二)


     上一篇
    python面试题(一) python面试题(一)
    点击上方”python宝典”,关注获取python全套视频, 技术文章第一时间送达! 1 简述解释型和编译型编程语言? 解释型语言编写的程序不需要编译,在执行的时候,专门有一个解释器能够将VB语言翻译成机器语言,每个语句都是执行的时候才翻译
    2021-04-05
    下一篇 
    python面试题(三) python面试题(三)
    点击上方”python宝典”,关注获取python全套视频, 技术文章第一时间送达! 1 一行代码实现9*9乘法表 print ("n".join("t".join(["%s*%s=%s" %(x,y,x*y) for y in rang
    2021-04-05