python近期遇到的一些面试问题(二)

1. 解释什么是栈溢出,在什么情况下可能出现。

栈溢出是由于C语言系列没有内置检查机制来确保复制到缓冲区的数据不得大于缓冲区的大小,因此当这个数据足够大的时候,将会溢出缓冲区的范围。
在Python中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
以上内容来自百度百科。https://baike.baidu.com/item/%E6%A0%88%E6%BA%A2%E5%87%BA/8538051?fr=aladdin

栈溢出的几种情况:

  1. 局部数组过大,当函数内部的数组过大时,有可能导致堆栈溢出。
  2. 递归调用层次太多。递归函数在运行时会执行压栈操作,当压栈次数太多时,也会导致堆栈溢出。
  3. 指针或数组越界。这种情况最常见,例如进行字符串拷贝,或处理用户输入等等。
    以上内容来自,https://blog.csdn.net/u010590166/article/details/22294291/

2.简述Cpython的内存管理机制

Python和其他高级编程语言,如Java、Ruby或JavaScript等一样,有自动内存管理机制。所以许多程序开发人员没有过多地关注内存管理,但是这可能会导致更多的内存开销和内存泄漏。这篇文章是关于CPython(Python解释器)是如何管理对象的生命周期的深度剖析,文章是我在GitHub上的 vprof 工程中记录来的,希望对大家有帮助。

引用计数

每一个Python对象都有一个引用计数器----用于记录有多少其他对象指向(引用)这个对象。它存储在变量 refcnt 中,并通过调用C宏Py_INCREF实现引用计数增加和Py_DECREF实现引用计数减少的操作。 Py_DECREF更复杂点,当引用计数器到零时,它会运行该对象的释放函数,回收该类型的对象。

通常以下两种情况你需要考虑这个宏定义:实现自己创建数据结构,或者修改已经存在的Python C API。如果你使用Python内置的数据结构,那么不需要任何操作。

如果想不增加引用计数,可以使用弱引用或 weakrefs 引用对象。 Weakrefs对于实现缓存和代理非常有用。

垃圾回收(GC)

引用计数是在Python 2.0之前管理对象生命周期的唯一方法。它有一个弱点,它不能删除循环引用的对象。 循环引用的最简单的例子是对象引用自身。


通常情况下,可以避免使用循环引用对象,但是有时是不可避免的(例如:长时间运行的程序)。

 

为了解决这个问题,Python 2.0引入了新的垃圾回收机制。 新GC与其他语言运行时(如JVM和CLR)的GC的主要区别在于,它仅用于寻找存在引用计数的循环引用。

循环引用只能由容器对象创建,因此Python GC不会跟踪整数,字符串等类型。

GC将对象分为3代,每一代对象都有一个计数器和一个阈值。当对象被创建时,阈值会被自动地指派为0,也就是第0代对象。当计数器大于某个阀值,GC就会运行在当前对象代上,回收该对象。没被回收的对象会被移至下一代,并且将相应的计数器复位。下一代的对象保留在下一代。

在Python 3.4之前,GC有一个致命缺点----每个对象重载了del__()方法,因为每个对象都可以相互引用,所以GC不知道该调用那个对象的__del()方法,这会导致GC直接跳过这些对象。具体详细信息可以参考 gc.garbage并且循环引用需要编程人员手动打破。

Python3.4介绍了一种最终的解决方法finalization approach ,现在的GC可以打破对象的循环引用,而不在使用gc.garbage介绍的方法去回收对象。

此外,值得一提的是,如果你确定你的代码没有创建循环引用(或者你不关心内存管理),那么你可以只依赖引用计数器自动管理内存,而不使用GC去管理内存。

以上内容来自:,https://python.freelycode.com/contribution/detail/511
英文原文:https://medium.com/@nvdv/cpython-memory-management-479e6cd86c9#.sbvb0py87

3.请列举你知道的Python的魔法方法及用途。

__init__

构造器,当一个实例被创建的时候调用的初始化方法

__new__

  1. __new__ 是在一个对象实例化的时候所调用的第一个方法
  2. 它的第一个参数是这个类,其他的参数是用来直接传递给 __init__ 方法
  3. __new__ 决定是否要使用该 __init__ 方法,因为 __new__ 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 __new__ 没有返回实例对象,则 __init__ 不会被调用
  4. __new__ 主要是用于继承一个不可变的类型比如一个 tuple 或者 string

__call__

允许一个类的实例像函数一样被调用

__del__

析构器,当一个实例被销毁的时候调用的方法

__len__(self): 定义当被 len() 调用时的行为
__repr__(self): 定义当被 repr() 调用时的行为
__str__(self): 定义当被 str() 调用时的行为
__bytes__(self):定义当被 bytes() 调用时的行为
__hash__(self): 定义当被 hash() 调用时的行为
__bool__(self): 定义当被 bool() 调用时的行为,应该返回 True 或 False

4. 已知以下list:

list1 = [
{
"mm": 2,
},{
"mm": 1,
},{
"mm": 4,
},{
"mm": 3,
},{
"mm": 3,
}
]

4.1 把list1中的元素按mm的值排序。

首先说函数sorted的具体用法:

(1).Python2.x:sorted(iterable, cmp=None, key=None, reverse=False)
,Python3.x:sorted(iterable, /, *, key=None, reverse=False),Python3.x和Python2.x的sorted函数有点不太一样,少了cmp参数。
key接受一个函数,这个函数只接受一个元素,默认为None
reverse是一个布尔值。如果设置为True,列表元素将被倒序排列,默认为False
着重介绍key的作用原理:
key指定一个接收一个参数的函数,这个函数用于从每个元素中提取一个用于比较的关键字。默认值为None 。

(2).sorted() 函数对所有可迭代的对象进行排序操作。
(3).对于 元组类型的列表比如

list2=[('b',4),('a',0),('c',2),('d',3)]

排序的方法是使用lambda然后获取需要排的元素的下标即可。

print(sorted(list2,key=lambda x:x[0]))

而本题有点麻烦 里面是字典类型所以我们需要通过以下方式获取

答案 :

sorted(list1,key=lambda x:x.items()[0][1])

或者

sorted(list1,key=lambda x:x['mm']))

或者用 operator 函数来加快速度, 上面排序等价于

from operator import itemgetter
print(sorted(list1,key=itemgetter('mm')))

4.2 获取list1中第一个mm值等于x的元素。
4.3 删除list1中所有mm等于x的元素,且不对list重新赋值。
4.4 取出list1中mm最大的元素,不能排序。

 

后续

猜你喜欢

转载自www.cnblogs.com/c-x-a/p/9226739.html
今日推荐