Python知识点——面试常考题
Posted sutcoderhang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python知识点——面试常考题相关的知识,希望对你有一定的参考价值。
1.python中在使用while时,可以与else一起使用。
即当条件不满足时不是跳出循环,而是执行else后面的语句后再跳出循环
2.python中不用声名变量,可以直接使用
3.python中的数据类型有
1)数字:int double 等
2)字符串:用“” ‘’ 表示。单个字符也默认为字符串。字符串可索引和单个提取。
3)元组:特殊的字符串,用()
4)列表:用[]
5)集合:用{}或set()
6)字典:字典和以上差别较大,多了一个关键字的定义:用d = {key1 : value1, key2 : value2 }表示
(1. 有序集合:list,tuple,str和unicode;
(2. 无序集合:set
(3. 无序集合并且具有 key-value 对:dict
4.python中break与continue的区别
当循环遇到break时,条件不满足不往下执行底下任何代码块,直接跳出循环;
当循环遇到continue时,条件不满足时只是跳出下面剩余语句,然后接着执行循环
5.在循环条件语句中,循环for或while下面接的条件if语句,就相当于C里的条件,条件满足时执行循环,条件不满足跳出循环。
而else与谁并列要思考,如果是整个循环结束后执行语句,则else就要与while并列,如果是当前判断条件不满足执行语句,要与if并列
6.如果if可以看作是二分法执行的话,那么循环可以看作是数据变更然后重复去判断一件事
7.可更改(mutable)与不可更改(immutable)对象
跟C的指针修改有些类似。若在函数里定义的变量值 形参a = 10 ,在外部定义实参 b = 5,在调用函数,把b发送给a时。
a的值本身是不会变的,变的是按传值的方式复制了变量 b,a 和 b 都指向了同一个 Int 对象,在 a=10 时,
则新生成一个 int 值对象 10,并让 a 指向它。
而这种情况换成元组就可以,直接将原始参数改变。
8.Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块
(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问,
9.正则表达式RE模块
10.range(start, stop[, step])
参数说明:
start: 计数从 start 开始。默认是从 0 开始。例如range(5)等价于range(0, 5);
stop: 计数到 stop 结束,但不包括 stop。例如:range(0, 5) 是[0, 1, 2, 3, 4]没有5
step:步长,默认为1。例如:range(0, 5) 等价于 range(0, 5, 1)
11.可以用如下格式对输出格式进行进一步控制
%[(name)][flags][width].[precision]typecode
(name)为命名
flags可以有+,-‘ ‘ 0表示。+表示左对齐。-表示右对齐。‘ ‘为一个空格,表示在正数的左侧
填充一个空格,从而和附属对其。0表示0填充。
width表示显示宽度
precision表示小数点后精度
12.循环是让计算机做重复任务的有效的方法。
break语句可以在循环过程中直接退出循环,而continue语句可以提前结束本轮循环,
并直接开始下一轮循环。这两个语句通常都必须配合if语句使用。
要特别注意,不要滥用break和continue语句。break和continue会造成代码执行逻辑分叉过多,容易出错。大多数循环并不需要用到break和continue语句,上面的两个例子,
都可以通过改写循环条件或者修改循环逻辑,去掉break和continue语句
13.要注意定义可变参数和关键字参数的语法:
*args是可变参数,args接收的是一个tuple;
**kw是关键字参数,kw接收的是一个dict。
14.python胶水语言 想用C / C++ 拿过来直接一起运行
框架(Framework):Python没有官方框架,框架很多。
框架是前人经过不断尝试和探索而设计出的一种适合于当下应用场景的整个或部分系统的可重用设计。
我们可以再次基础上进一步开创新的东西
16.random模块,随机产生数字模块
randint随机产生指数的方法。
17.异常处理
1).当程序执行到某个位置时一旦出现问题,整个程序会直接挂掉,为了防止因几行代码出现问题导致整个程序不往下执行,
我们引入了异常处理,也就是把可能出现异常的程序放到try里,如果出现异常,程序会接着往下走,最后把异常结果输出。
try:
可能出现异常的语句
except xxxError:
print()
如果出现上述xxxError异常,返回什么
2).但是这样的话就需要我们提前判断可能会出现的异常,因为不符合xxx并不会返回。所以
我们可以改写,except exception: 这样改写之后,只要出现异常,就会返回,不论该异常是什么类型。
经典写法
try:
可能出现异常的语句
except exception as ret:
print(ret)
这样写完之后,一旦出现异常可以自动输出异常,不用定义。
3).引入else,finally:
try:
(可能出现异常的语句)
except exception as ret:
print(ret)------------捕获异常,如果出现异常最后返回错误。
else:
(没有异常才会执行的功能)
finally:
(不论有没有异常,最终都必须执行的功能)
4).异常是可以传递的,意思就是说你捕获的异常中如果调用了函数,如果被调用的函数出现了异常,
即使该函数中没有用try捕获异常,但它会把异常的消息传递给接口,而接口会被try捕获。综上,在调用的函数中
出现了异常,也会被捕获。
5).我们也可以人为的抛出异常---用作判断用户输入
具体方法是可以定义一个类,让他继承exception,然捕获异常中添加if判断,当条件满足时用raise+刚刚定义的类返回刚刚定义异常,
这时候该异常会被捕获到,当条件不满足时,直接没有异常往下执行。
6).而使用raise 后面不接?任何异常,会将此时的异常重新抛回给系统,让系统该怎么处理怎么处理
18.if用做判断时,空字符串 空列表 空元组 空字典 0 None 都表示 假!
19.pip python管理py模块的工具
20.字符串逆序:str(::-1)
21.print(‘站点列表 {0}, {1}, 和 {other}。‘.format(‘Google‘, ‘Runoob‘,other=‘Taobao‘))
站点列表 Google, Runoob, 和 Taobao。
22.对一个列表增删该查
增:expend 追加;insert(位置,元素);extend(列表)将一个列表追加到另一个列表后面
删:pop(),默认删最后一个,括号里可以加下标;remove(内容),根据内容删;del name[下标] 根据下标删
改:name[]根据下标赋新值
查:用in
23.对一个字典增删改查
增/改:直接dict[key]=
删:del dict[key]
查:get(key) 直接获取value
24.合并两个字典的四种方式
1)创建一个空字典,分别遍历添加
2)借助字典的dict(d1, **d2)方法
3)创建一个空字典,借助字典的update()方法
4)借助dict(d1.items() + d2.items())的方法
25.静态方法既可以用类调用,也可以 用对象调用
26.列表倒置用reverse
27.map与lambda配合使用,求一组数据的平方
map(lambda x: x ** 2, [1, 2, 3, 4, 5])
28.py中可变类型与不可变类型
python不可变数据类型:int 、flotat 、str、tuple
python可变数据类型:list、dict、set
不可变数据类型:相当于单例,之创建一个对象,之后所有引用指向这个对象,内存中有引用计数来记录都少个变量指向这个对象。不允许改变值,如果改变值,相当于创建一个新的对象
可变数据类型:允许值发生变化,值变化也不会生成一个新对象,并且对象的地址也不会发生改变,而相同值创建多个对象,每个对象有自己的地址
29.for循环原理
之所以能用for遍历是因为 所遍历的是一个可迭代对象如list touple dict 但可迭代对象不一定是迭代器,有__iter__方法的都是迭代器对象
for循环的原理,调用for后面的__iter__方法,使之变成迭代器对象.然后不断调用__next__遍历,然后捕捉StopIteration 异常,结束迭代
需要用iter将可迭代对象形成一个迭代器对象。而这个对象通常只保存对象本身内存,并不会保存整个数据表里的数据。所以会很节省内存。
我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行iter方法得到的,迭代器有的好处是可以节省内存。
30.列表生成式与生成器
列表生成式 a = [i for i in range(1,18)]
for只确定循环次数,而列表里放什么值, 由i决定
列表生成式嵌套 a = [(i,j),for i in range(3) for j in range(2)]
相当于for i in range(3):
for j in range(2):
print((i,j))
列表生成式:将a = [i for i in range(1,18)]中括号改成()就变成一个生成器
31.range有什么风险
python2有风险,range返回一个列表。(会占很大内存 )python3没有
32.py生成器与迭代器的区别,生成器的优势
生成器自动创建了_iter_()和_next_()内置方法仅仅保存了一套生成数值的算法,调用时,才去计算然后返回一个值。
生成器一定是迭代器,而迭代器不一定是生成器,一边循环一边计算的机制称为生成器,含有yield语句的函数,可以减少内存空间。
如果在某些情况下,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的东西就叫生成器。
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
-------------------py核心编程-----------------------------------------------
20.模块
1)定义:模块就是py文件,可以是你自己写的,也可以是python当中自带的工具,当你在某个py文件下想要引用其他模块的功能,
就需要你把你把该py文件导入。
2)导入方法:使用import XXXX 直接将模块导入,用这种方法导入时在使用模块里的函数需要用 模块name.函数名/类名
另外一种导入模块的方法可以用 from 模块 import 函数/类,这种导入方式仅仅将模块里的函数或者类导入进来,
在调用时就可以直接使用该函数,若想要将所有函数导入可以用 * 代替。
3)注意在定义py文件名时尽量不要和py本身自带模块名相同。同时尽量不要用*来导入,如果多个模块的函数名相同,
后导入的会覆盖先导入的。
4)调用模块时,会自动将此模块的程序执行一遍,所以在多人协作时,在写完很多方法或类时,一般会在底下调用一下,
进行测试。所以这些测试不希望在别人导入自己模块时被调用,引入了__name__变量。
__name__变量当你在自己模块下打印时会显示---__main__,在别人调用模块执行时会打印该模块名。
所以可以在自己测试代码前 用 if __name__=="__main__":也就是单独执行该模块时会调用if下面的代码,
而别人调用时不会。
5)导入不再当前文件夹下的模块,先导入sys,修改sys.path.append(),添加一个新的路径即可,重新加载模块用imp下的reload
import xxx as xxx 导入模块起个别名
6)模块中:__all__的作用
在模块前面加上__all__(“函数名/类名/变量名”)之后,当你导入你一个模块,只能引用括号里面的,all之外的无法引用
7)包
实际开发中,会将很多个相同功能的py文件(模块),存放到一个文件夹里。如果在这个文件夹里构建一个__init__.py文件,那么这个文件夹就可以称之为包
8)包中__init__
__init__文件的作用就是让python知道,当前文件夹是一个包,可以被导入。并且在导入时会先执行一遍__init__.py。
但如果__init__文件里什么都不写,那么将包导入进去之后,也用不了里面的模块。想要用包里模块,应该在__init__.py中写一个__all__(),并在括号里写上导入模块的名字
此时就可以用 from 包 import 模块 导入。
另外一种方式是在 __init__中写上 from . import 模块 从当前路径下导入模块,就可以用包.模块.类/方法了
21.Py中 ‘== ‘ 与 ‘is‘
==是判断两个变量内容是否相同,相同返回ture,不相同返回false。
is 判断两个变量是否指向同一个对象,如果is返回Ture 那么 两个对象id一定相同
22.深拷贝,浅拷贝。
通过 a=b 进行赋值的过程就是浅拷贝,即只是复制了一份引用,让a指向b指向的对象。
而深拷贝需要调用copy模块中的deepcopy(),让通过这种方式拷贝的话是让a重新开辟了一块内存空间,存放着b存放的内容。
copy模块中除了deepcoy之外还有一个copy方法,使用copy的特点是当拷贝数据类型是嵌套形式的比如列表嵌套列表,
使用copy只拷贝当前的最外层的列表,而里面包含的,并不会生成新的空间。同时,对那些不可变类型,copy是不会拷贝的。
23.变量
xxx:共有的,谁都可以用
_xxx:只能在模块里面用,from import *导不进去,也就是别的模块用不了。
__xxx:只能在类里面使用,私有熟悉,私有方法,只要出了类之外就用不了。
__xxx__:python文件里定义的,具有特殊含义,不要自己定义这样的变量。----原因是python将变量名改了_object__变量
xxx_:为了防止和关键字冲突的定义。
24.property的使用
我们在类中定义私有属性时,外部是不能调用的。但有时我们需要在外部去获取、更改这个变量值。
所以常规方法是在类中定义两个方法,getter,setter专门为了获取、更改这两个变量,当需要上述操作,直接调用方法实现。
但这样以来每一次都去调用方法会很麻烦,所以就引入了property。来实现,用调用对象.XX的操作。
具体实现方法有两种:1)变量名 = property(getter,setter),此时你使用变量名.方法时会自动进行获取、更改操作。
2)通过property装饰器来实现上述功能,在获取变量方法前[email protected]
3)在设置变量前+变量名.setter
最后,通过property可以实现一个封装效果,即你没法直接随意的去使用私有属性,只能间接的去使用。
25.在函数里面修改全局变量需要先加,global声名
26.Python支持动态添加属性方法。动态特性的好处就在于不用修改整个程序的前提下,可以添加修改部分功能。
具体实现方式可以在类的外面添加实例属性、类属性、实例方法、类方法、静态方法。
添加实例属性只需要通过 exampleName.的方式添加即可,同理添加类属性通过ClassName.的方式。
添加类方法、静态方法时同样的类外面写一个类方法、静态方法,然后按照添加属性的方式一样,将方法传给某个变量,
下次可通过改变量调用其方法。
而添加实例方法则需要借助types模块。原因是如果采取和属性同样的方式, 在类外定一个函数,里面self。
都没问题,但在实际添加的过程中不会去传参数。所以要使用exampleName.MethodName = types.MethodType(MethodName,exampleName)
将所添加的方法与该类绑定即可。
如果想要限定不允许额外添加实例属性,可以用__slots__=("name")来进行控制,这样以来,在类的外面不能添加、使用
name以外的属性。
27.当继承多个类时,默认先继承第一个
--------------------------生成器------------------------------------------------------------------------
1.生成器------demo列表生成器:a = [x for x in range(10) ]
生成器的引入,节省当前用不到的内存空间,也就是说你创建一个包含100000个元素的列表,但并不需要马上全部使用,
而这100000个元素当前所占用的内存空间就是一种浪费资源。如果将这100000个元素变成一个列表生成器,它在存储的
时候就不会占用太多内存,而且能保证每次你在使用的时候迭代输出。
2.生成器的具体实现方法有两种
一种是简单的用a = (x for x in range(10)),a保存的就是列表生成器,接下来在调用next(a)或.__next__时,会一直迭代输出。
第二种方法可以将一个函数通过yield改写成一个列表生成器。让一个变量保存该函数,即创建了一个生成器对象。
yield的功能有两点,一是函数的程序执行的yield时会停止向下执行,等待你下一次的next时继续。
二是可以将当前的变量值返回即输出,即yield后面一般都会跟一个正在变化的变量如 yield i。
3. next 与 send
相同点:在调用以上两种方法时都会进行下一次迭代。
不同点:使用send时可以传一个值,并将这个值附给yield i整体,即可以找一个变量接受。
demo:A.send("哈哈哈") temp = yield i 相当与把temp = 哈哈哈,同时i迭代一次
send(None) = __next__
4.生成器、多任务、协程
使用生成器可以实现多任务,具体实现方法时将两个生成器对象扔到两个while 1里面,然后将两个生成器对象在扔到一个
while 1 里面,这样以来,两个生成器会无限交替往下执行,如果速度足够快,可以看做是同一时间三个while1同时执行,
实现多任务。
5.线程、进程、协程
1)进程是资源分配的单位,使用多进程新开辟一段空间来保存,多进程相互之间没有任何练习,一个挂了也不会影响其他。
2)线程是CPU调度的单位,线程执行在进程里面,没有自己的独立空间,Python中的GIL解释器锁,不论CPU有几核,也只能
占用一个核。如果一个子线程挂的时候对共享区造成破坏,那么其余子线程以及主线程(进程)会受到影响.
3)协程执行在线程里面,相比线程、进程不占用资源。最大的优势可以避免使用多进程、多线程时导致多任务在CPU的切换成本,
即每一次切换都要保存上一个任务的数据。所以,使用协成可以实现多任务,并且相比来说,效率会更高。
6.计算密集型--->需要大量CPU资源,用多进程
io密集型----->需要网络功能,大量的时间都在等待网络数据的到来.
---------------------迭代器-----------------------------------
1.首先区分一个概念,那就是什么是可迭代的(Iterable),什么是迭代器(Iterator)。
list、dict、str都是Iterable但不是Iterator。可以通过iter()函数将可迭代的变量创建一个Iterator对象。
判断可以用isinstance。
2.那什么是可迭代的呢?凡是可以用for去遍历的都是可迭代的。
3.但是迭代去必须能够通过next()取值,生成器一定是迭代器。
---------------------闭包-------------------------------
1.什么是闭包
在一个函数里面在定义一个函数,同时被定义的函数用到了它外层函数的变量,并且外层函数返回的值是内层函数。
这样的结构叫做闭包。
def test1(num):
print(---1---)
def test2(num1)
print(num+num1)
print(---2----)
return test2
ret = test1(100)
ret(100)
测试输出:200 ret指向test2,在调用ret时相当于调用test2,里面用到了test1函数的变量会到里面去找。
2.闭包注意的地方,ret指向test1会生成内存空间,并不会被释放,如果接下来用ret1去指向test1(100)会额外开辟一段空间
------------------装饰器---------------------------------------
1.基于闭包,让一个函数再执行之前,先去到另外一个函数执行,而并不改变函数本身,执行完之后回来执行自己函数内部。
并且通常情况下,会对函数本身产生一定的影响,可以的使输出值的变化,可能是条件判断等。
2.具体实现的方式是找一个变量在装饰器外层函数接收被装饰的函数。
def w1(func):
def d1():
print(‘正在装饰‘)
func()
return d1
def d2()
print(‘ha‘)
d2 = w1(d2)
d2()
相当于把 d2函数扔到 w1函数里面执行,只不过在执行之前先让其先print(‘正在装饰‘)
最好把再把执行以后的结果返回给d2,而过程中讲一个函数作为一个参数传入传出是通过闭包的原理实现的。
而d2 = w1(d2)这样的过程可以用一种py特殊的方法替代,用@w1,将其放到d2函数的上面,即可达到上面的效果。
而@w1即为装饰器~等价于 d2 = w1(d2)
3.如果同时使用两个或多个装饰器,如:
@w2
@w1
def d2()
print(‘ha‘)
这种情况即便是先装饰w1,在装饰w2,原因是,装饰器下面有函数的情况下才能被装饰,所以w1挨着d2,所以装饰之后返回函数
在被w2装饰.
4.Python解释器会在调用函数之前就已经进行装饰了,调用是装饰以后的结果
5.装饰器是从近往远装的,但是在调用是从外往里调用的。就是先装里函数最近的装饰器依此往外,调用时是相反的方向。
6.如果装饰的是一个有参数的函数,那么在装饰器里的子函数,以及接受函数的参数也应该设置有参数,并且与
被装饰函数的参数一致。否则就会报错,有上述demo,如果d2有两个参数,那么d1,func也需要接受两个参数。
7.但如果函数的参数是不定长的,或者经常需要变换的话,可以将d1,func放不定长的参数,也是我们之前所学的
(*args,**kwargs),用来接受不定长的参数。
8.如果被装饰的函数如果有返回值的话,那么在func处一定得找一个值来接收他的返回值,也就是说用一个变量
a = func(),然后return a 将该值返回。
9. 那么,一个被装饰的函数可能有返回值,可能有参数,但这些都是不确定的啊,那该如何写一个通用的装饰器呢。
def w1(func):
def d1(*args,**kwargs):
ret = func(*args,**kwargs)
return ret
return d1
这样的一个w1装饰器就能够达到通用的效果,利用*args,**kwargs保证传参的不定长,用ret接收返回值如果没有返回值,
那么ret接收的就是一个None!
10.带有参数的装饰器
实现方式:在装饰外面在再来一层,把装饰器包在里面。而外面的函数用来接收装饰器里面的参数,然后返回装饰器函数引用。
def w2(arg):
def w1(func):
def d1(*args,**kwargs):
ret = func(*args,**kwargs)
return ret
return d1
retunr w1
@w2(haha)
用途:一个装饰器在装饰不同函数的时候有不同的功能,原理就是通过在装饰时传入不同的参数,然后在装饰器里面用if判断。
11.可以用类实现装饰器,具体通过改写 __call__方法实现
12.range的范围是半闭半开的区间[0,x)
---------------------------------元类--------------------------
1.py中一切皆对象,class也是对象,linux一切皆文件
以上是关于Python知识点——面试常考题的主要内容,如果未能解决你的问题,请参考以下文章