Python 八股
解释型语言和编译型语言
编译型语言
通过专门的编译器,把所有源代码一次转换为特定平台的可执行文件,然后再运行。一次编译,多次执行。比如C,C++。
优点 :
- 一次编译,多次运行。
- 脱离编译环境,并且运行效率高。
缺点:
- 依靠编译器、跨平台性差,可移植性差。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。
- 编译之后如果需要修改就需要整个模块重新编译。
解释型语言
由专门的解释器,根据需要将部分源代码临时转换成特定平台的机器码,然后执行。边解释边执行。比如Python, PHP.
优点:
- 跨平台性好,通过不同的解释器,将相同的源代码解释成不同平台下的机器码。
缺点:
- 一边执行一边转换,效率不高。
混合型语言
如Java, C#。兼具执行效率和跨平台性,不直接编译成机器码,需要先编译成中间码(java是编译成字节码),需要中间语言运行库类似java虚拟机(JVM),边解释边运行。
is和==的区别
==
是比较操作符,只是判断对象的值(value)是否一致,而 is
则判断的是对象之间的身份(内存地址)是否一致。对象的身份,可以通过 id()
方法来查看。
os和sys的区别
os模块是Python标准库中提供的与操作系统交互的模块,提供了访问操作系统底层的接口,里面有很多操作系统的函数。
sys模块负责程序与Python解释器的交互。
可变对象和不可变对象
在python中,一切皆对象,对象必有的三个属性:地址、类型、值
可变对象
- 当对象的值发生变化,但内存地址没有改变时,则说明是可变类型
- python里的可变对象有:列表、字典、集合
- 引用可变对象时,会创建新的内存地址,当可变对象值发生改变时,原内存地址不会改变
- 引用传递 主函数向调用函数传递的参数是可变类型时,实际上是将实参的引用传入了调用函数,对引用的操作等于对其指定的对象进行操作。
不可变对象
- 当对象的值发生变化,但内存地址也发生改变时,则说明是不可变类型
- python里的不可变对象有:元组、字符串、数值
- python在引用不可变对象时,会寻找该对象是否被创建过,若该对象已创建,则变量会直接引用该对象,不会再申请新的内存空间。
- 值传递 主函数向调用函数传递的参数是不可变类型时,实际上只是将实参的拷贝(即临时副本)传递给了被调用函数,并不是实参本身,这样被调函数不能直接修改主调函数中变量的值。
注意:如果直接将list2 = list1,那么list1和list2的地址会是相同的。只是换了不同的名称而已。
深拷贝和浅拷贝
- 浅拷贝,指的是重新分配一块内存,创建一个新的对象,但里面的元素是原对象中各个子对象的引用。
list1 = [1, 2, 3]
list2 = list(list1)
list3 = list1[:]
print("list1==list2 ?",list1==list2) True
print("list1 is list2 ?",list1 is list2) False 浅拷贝
print("list1 is list3 ?",list1 is list3) False 浅拷贝
- 深拷贝,是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联。
import copy
list1 = [[1, 2], (30, 40)]
list2 = copy.deepcopy(list1)
*arg
和 **kwarg
作用
允许我们在调用函数的时候传入多个实参
*arg
会把位置参数转化为 tuple
,**kwarg
会把关键字参数转化为 dict
with如何使用
with所求值的对象必须有一个enter()方法,一个exit()方法。
with**后面的语句被求值后,返回对象的**__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的exit()方法。
with open("/tmp/foo.txt") as file:
data = file.read()
__new__和__init__
__init__为初始化方法,__new__方法是真正的构造函数。 __new__是实例创建时被调用,它的任务是创建并返回该实例,是静态方法 __init__是实例创建之后被调用的,然后设置对象属性的一些初始值。
__new__方法在__init__方法之前被调用,并且__new__方法的返回值将传递给__init__方法作为第一个参数,最后__init__给这个实例设置一些参数。
装饰器
Python的装饰器本质上是一个嵌套函数,它接受被装饰的函数(func
)作为参数,并返回一个包装过的函数。
作用:在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加新功能。
Python的装饰器广泛应用于缓存、权限校验(如django中的@login_required和@permission_required装饰器)、性能测试比如统计一段程序的运行时间)和插入日志等应用场景。
示例代码:
from decorator import decorator
@decorator
def hint(
func
, *args, **kwargs):
print('{} is running'.format(func.__name__))
return func(*args, **kwargs)
迭代器和生成器
可迭代对象
python中一个非常强大的功能,它可以访问容器(字符串、列表、元祖、集合、字典、range)。
迭代是通过for循环遍历对象中的每一个元素,将元素取出来的过程。所以:容器都是可迭代对象。
可迭代对象除了包含常见的序列,还包括一个叫做迭代器的东西。
迭代器
- 迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
- 迭代器有两个基本的方法:iter() 和 next()。
- 字符串,列表或元组对象都可用于创建迭代器
>>> list=[1,2,3,4]
>>> it =
iter
(list) # 创建迭代器对象
>>> print (next(it))
>>> print (next(it))
生成器
- 在 Python 中,使用了 yield 的函数被称为生成器(generator)。
- 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作。
- 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
- 在调用生成器函数时,函数体并不执行,只返回一个生成器对象。
- 在对生成器对象使用next方法或者遍历的时候,生成器函数体才真正执行。
def func2():
print('######')
yield 100
print('------')
yield 200
print('======')
return 300
fliter map reduce
filter 函数用于过滤序列,它接收一个函数和一个序列,把函数作用在序列的每个元素上,然后根据返回值是True
还是False
决定保留还是丢弃该元素。
>>> mylist = list(range(10))
>>> list(filter(lambda x: x % 2 == 1, mylist))
[1, 3, 5, 7, 9]
map 函数传入一个函数和一个序列,并把函数作用到序列的每个元素上,返回一个可迭代对象。
>>> list(map(lambda x: x % 2, mylist))
[1, 0, 1, 0, 1, 0, 1, 0, 1]
reduce 函数用于递归计算,同样需要传入一个函数和一个序列,并把函数和序列元素的计算结果与下一个元素进行计算。
>>> from functools import reduce
>>> reduce(lambda x, y: x + y, range(101))
5050
全局变量 局部变量 闭包
在函数外定义的变量为全局变量。全局变量可以在函数中直接进行访问,但是在修改全局变量时,为了避免与局部变量产生混淆,需要先加上global声明,然后再修改。
闭包是Python编程一个非常重要的概念。如果一个外函数中定义了一个内函数,且内函数体内引用到了体外的变量,这时外函数通过return
返回内函数的引用时,会把定义时涉及到的外部引用变量和内函数打包成一个整体(闭包)返回。
作用:
- 即使外函数已经结束了,内函数仍然能够使用外函数的临时变量。
闭包经典问题:
[lambda x: x*i for i in range(4)]
res = [m(2) for m in lst]
print(res)
- 预计结果为:
[0, 2, 4, 6]
- 实际输出为:
[6, 6, 6, 6]
[lambda x: x*i for i in range(4)] --> [lambda x, i = i: x*i for i in range(4)]
等价于
def func():
fun_list = []
for i in range(4):
def foo(x):
return x*i
fun_list.append(foo)
return fun_list //是一个链表【foo(x), foo(x), foo(x), foo(x)】
i在外层作用域lambda x: x*i 为内层(嵌)函数,他的命名空间中只有 {'x': 1} 没有i,所以运行时会向外层函数(这里是列表解析式函数[])的命名空间中请求i,而当列表解析式运行时,列表解析式命名空间中的i经过循环依次变化为0-->1-->2-->3最后固定为3,所以当 lambda x: x*i内层函数运行时,去外层函数取i每次都只能取到 3。
变闭包作用域为局部作用域, 在foo()
中添加i=i
def func():
fun_list = []
for i in range(4):
def foo(x,
i=i
):
return x * i
fun_list.append(foo)
return fun_list
for m in func():
print(m(2))
匿名函数
匿名函数的关键字为lambda
,表现形式为:lambda 参数 : 返回值
,lambda
后面的参数就是函数的形参,冒号后面的表达式就是返回值。
lambda
表达式的意义两点:
- 对于只有一行的函数,使用此方式可以省去定义函数的过程,使代码简洁明朗;
- 对于不需要重复使用之函数,此方式可以在用完之后,立即释放,提高程序执行的性能。
垃圾回收机制
引用计数(主要手段)+标记清除(辅助)+分代回收(辅助)
1.引用计数
在Python中,使用了引用计数这一技术实现内存管理。一个对象被创建完成后就有一个变量指向这个对象,那么就这个对象的引用计数为1,以后如果有其他变量指向这个对象,其引用计数也会相应增加,如果将一个变量不再执行这个对象,那么这个对象的引用计数减1。如果一个对象没有任何变量指向这个对象,也即引用计数为0,那么这个对象会被Python回收。
优点:
- 高效。
- 运行期没有停顿。可以类比一下Ruby的垃圾回收机制,也就是实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。
- 对象有确定的生命周期。
- 易于实现。
缺点:
- 维护引用计数消耗资源,维护引用计数的次数和引用赋值成正比。
- 无法解决循环引用的问题。比如现在有两个对象分别为
a
和b
,a
指向了b
,b
又指向了a
,那么他们两的引用计数永远都不会为0。也即永远得不到回收。
2.标记清除
针对循环引用的情况,Python引入标记清除算法。
标记清除算法是一种基于追踪回收技术实现的垃圾回收算法。它分为两个阶段:
- 第一阶段是标记阶段,GC会把所有的活动对象打上标记
- 第二阶段是把那些没有标记的对象非活动对象进行回收。
3.分代回收
分代回收是建立在标记清除技术基础之上的,是一种以空间换时间的操作方式。
Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一“代”, Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾回收机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。
参考链接:
解释型语言
https://blog.csdn.net/jackcong_/article/details/116173078
https://www.cnblogs.com/superwuchaofan/p/16550375.html
全局变量
https://blog.csdn.net/weixin_45043349/article/details/120494883
八股
https://blog.csdn.net/qq_37621623/article/details/123353485
垃圾回收
https://blog.csdn.net/qq_37085158/article/details/126213782
https://wenku.baidu.com/view/e2b43fd4fa0f76c66137ee06eff9aef8941e48d9.html?_wkts_=1669344734124
闭包
https://blog.csdn.net/qq_37085158/article/details/124734978
装饰器
https://blog.csdn.net/qq_37085158/article/details/124933649
os和sys
https://www.jianshu.com/p/31cee25fc428
可变对象和不可变对象
https://www.jb51.net/article/223840.htm
浅拷贝和深拷贝
https://blog.csdn.net/qq_40630902/article/details/119278072
with的用法
https://blog.csdn.net/qq_35985044/article/details/125408938
迭代器和生成器
https://blog.csdn.net/qq_54259749/article/details/125685621
https://www.runoob.com/python3/python3-iterator-generator.html
python八股