python面试题
1.到底什么是Python?你可以在回答中与其他技术进行对比
下面是一些关键点:
Python是一种解释型语言。这就是说,与C语言和C的衍生语言不同,Python代码在运行之前不需要编译。其他解释型语言还包括PHP和Ruby。Python是动态类型语言,指的是你在声明变量时,不需要说明变量的类型。你可以直接编写类似x=111和x="I'm a string"这样的代码,程序不会报错。Python非常适合面向对象的编程(OOP),因为它支持通过组合(composition)与继承(inheritance)的方式定义类(class)。
Python中没有访问修饰符(access specifier,类似C++中的public和private),但以__XX(双横线)结尾的函数或者变量相当于private。这么设计的依据是“大家都是成年人了”,Python本身没有任何机制阻止你干坏事,一切全靠自觉。
在Python语言中,函数是第一类对象(first-class objects)。这指的是它们可以被指定给变量,函数既能返回函数类型,也可以接受函数作为输入。类(class)也是第一类对象。Python代码编写快,但是运行速度比编译语言通常要慢。好在Python允许加入基于C语言编写的扩展,因此我们能够优化代码,消除瓶颈,这点通常是可以实现的。Numpy就是一个很好的例子,它的运行速度真的非常快,因为很多算术运算其实并不是通过Python实现的。
Python用途非常广泛——网络应用,自动化,科学建模,大数据应用,等等。它也常被用作“胶水语言”,帮助其他语言和组件改善运行状况。Python让困难的事情变得容易,因此程序员可以专注于算法和数据结构的设计,而不用处理底层的细节。
如果你应聘的是一个Python开发岗位,你就应该知道这是门什么样的语言,以及它为什么这么酷。以及它哪里不好。
2.什么是PEP8?
PEP8是一个编程规范,内容是一些关于如何让你的程序更具可读性的建议。
其主要内容包括代码编排、文档编排、空格的使用、注释、文档描述、命名规范、编码建议等。
3.什么是pickling和unpickling?
Pickle模块读入任何Python对象,将它们转换成字符串,然后使用dump函数将其转储到一个文件中,这个过程叫做pickling。反之从存储的字符串文件中提取原始Python对象的过程,叫做unpickling。
4.说说python程序运行过程?
迄今为止,我们大多数将 python 作为一门编程语言来介绍,但从实现来看,python 也是一个名为解释器的软件包。解释器是一个让其他程序运行起来的程序。当你编写了一段python程序后,python解释器将读取程序,并按照其中的命令执行,得出结果。 实际上,解释器是代码与机器硬件之间的软件逻辑层。
按照惯例,python是以.py结尾的。从技术上讲,这种命名方案在导入时才是必须的,你可以用任何自己喜欢的文本编辑器创建以其它扩展名结尾的文件,但大多数python文件为了统一都是以.py结尾命名的。你在文本文件中输入代码,之后在解释器中运行这些代码。
执行程序时,python内部(对大多数用户完全隐藏)会先将源代码编译成字节码。编译是一个简单的翻译步骤,字节码是一个低级的与平台无关的表现形式。这些字节码比文本文件中的源代码语句的运行速度快的多。如果python进程在机器上具有写入权限,那么它会将字节码保存在一个以.pyc为扩展名的文件中。python保存字节码是对启动速度的一个优化。如果下次启动时,源代码没有修改,并且使用的python版本也没有改变,那么python将会直接加载.pyc文件,并跳过编译这个步骤。如果python无法在机器上写入字节码,程序仍然可以工作:字节码会在内存中生成,并在程序结束时被丢弃。
一旦程序被编译成字节码,之后的字节码发送到python虚拟机(Python Virtual Machine,PVM)上来执行。PVM就是迭代运行字节码指令的一个大循环,一个接一个地完成操作。PVM是Python运行时的引擎,它时常表现为Python系统的一部分,并且是实际运行脚本的组件。从技术上来说,它只是所谓Python解释器的最后一步。
整理自python学习手册第五版,阅读书籍以了解更多细节
5.Python是怎样管理内存的?
Python的内存管理是由私有heap空间管理的。所有的Python对象和数据结构都在一个私有heap中。程序员没有访问该heap的权限,只有解释器才能对它进行操作。
Python的heap空间分配内存是由Python的内存管理模块进行的,其核心API会提供一些访问该模块的方法供程序员使用。
Python有自带的垃圾回收系统,它回收并释放没有被使用的内存,让它们能够被其他程序使用。
6.流重定向
stream_redirection.py
import sys
import os
use command:python stream_redirection.py > saveit.txt
print(sys.platform)
print(os.getcwd())
print('-' * 30)
print('hello world')
在终端使用命令行:
python stream_redirection.py > saveit.txt
就会把print()里的内容打印到文本saveit.txt
Linux 允许将命令执行结果重定向到一个文件,将本应显示在终端上的内容输出/追加到指定文件中:
表示输出,会覆盖文件原有的内容
表示追加,会将内容追加到已有文件的末尾
7.python为什么不能像C++一样快?如果要追求速度该怎么做?
与C/C++这类完全编译语言不同的是,python工作中通常没有构建或“make”的步骤: 代码写好后立即运行,另外一个就是 python 字节码不是机器的二进制代码,字节码是特定于 python 的一种表现形式。
这就是python代码无法像C/C++一样快的原因。 PVM循环仍然需要解释字节码。
大多数使用的python都是Cython,是标准的实现,Cython具有调用C函数以及使用变量、参数和类属性的C类型声明的能力。追求速度可以使用C与python混合编程。
(Shed Skin是python 到 C++ 的转换器。尝试将python代码翻译成C++代码,然后使用机器中的C++编译器将得到的C++代码编译为机器代码)
8.数组和元组之间的区别是什么?
数组和元组之间的区别:数组内容是可以被修改的,而元组内容是只读的。另外,元组可以被哈希,比如作为字典的关键字。
9.谈谈你对python中变量、对象和引用的理解
Python中的一切都是类,类型属于对象而不是变量,变量名没有类型,所谓赋值只是让不同的变量名引用不同类型的对象。
变量永远不会拥有和它关联的类型信息或约束。类型的概念存在于对象而不是变量中。变量原本是通用的,他只是在一个特定的时间点,简单地引用了一个特定的对象而已。
当变量出现在表达式中时,它会马上被当前引用的对象所替换,无论这个对象是什么类型。此外,所有的变量必须在使用前明确地被赋值,使用未赋值的变量会产生错误。
简而言之,变量在赋值的时候才会被创建,它可以引用任何类型的对象,而且必须在引用之前赋值。
语句 a=3,变量a实际是到对象内存空间(通过运行常量表达式3而创建)的一个指针。
python学习手册P185,186,187
10.python中的冻结二进制文件你了解多少?
可以通过第三方工具将python程序转为可执行程序,他们在python世界中被称为冻结二进制文件(frozen binary)。这些程序可以不安装python环境而独立运行。冻结二进制文件,能够将程序的字节码,PVM,以及任何程序所需要的python支持文件捆绑在一起形成一个单独的文件包。这个过程存在一些不同的变体,但最终的结果都会产生一个独立的可执行二进制程序,类似Windows系统中的.exe文件。
11.Python都有哪些自带的数据类型?
以python3.5为例,Python自带的数据类型分为可变的和不可变的。可变的有:列表、集合(可变集合)、字典、bytearray(字节数组);不可变的有:字符串、元组、数字、frozenset(不可变集合)。
12.什么是Python的命名空间?
在Python中,所有的名字都存在于一个空间中,它们在该空间中存在和被操作————这就是命名空间。
它就好像一个盒子,每一个变量名字都对应着一个对象。当查询变量的时候,会从该盒子里面寻找相应的对象。 命名空间是一个字典的实现,键为变量名,值是变量对应的值。各个命名空间是独立没有关系的,一个命名空间中不能有重名,但是不同的命名空间可以重名而没有任何影响。
python程序执行期间会有2个或3个活动的命名空间(函数调用时有3个,函数调用结束后2个)。按照变量定义的位置,可以划分为以下3类:
Local 局部命名空间,每个函数所拥有的命名空间,记录了函数中定义的所有变量,包括函数的入参、内部定义的局部变量。
Global 全局命名空间,每个模块加载执行时创建的,记录了模块中定义的变量,包括模块中定义的函数、类、其他导入的模块、模块级的变量与常量。
Built-in python自带的内建命名空间,任何模块均可以访问,放着内置的函数和异常。
https://www.cnblogs.com/apollo1616/articles/9785371.html
13.Python中的pass是什么?
Pass是一个在Python中不会被执行的语句。在复杂语句中,如果一个地方需要暂时被留白,它常常被用于占位符。
14.在Python中什么是slicing?
Slicing是一种在有序的对象类型中(数组,元组,字符串)节选某一段的语法。
15.Python中的docstring是什么?
Python中文档字符串被称为docstring,它在Python中的作用是为函数、模块和类注释生成文档。
16.如何在Python中拷贝一个对象?这些拷贝之间有什么区别?
赋值(=),就是创建了对象的一个新的引用,不会开辟新的内存空间。
浅拷贝:创建一个新的对象,其内容是原对象的引用。 浅拷贝有三种形式:切片操作,工厂函数,copy模块中的copy函数。 如: lst = [1,2,3,[4,5]]
切片操作:lst1 = lst[:] 或者 lst1 = [each for each in lst]
工厂函数:lst1 = list(lst)
copy函数:lst1 = copy.copy(lst) 浅拷贝之所以称为浅拷贝,因为它仅仅只拷贝了一层,在lst中有一个嵌套的list[4,5],如果我们修改了它,情况就不一样了。
深拷贝:创建一个新的对象,并且递归的复制多层嵌套里的对象,深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。(copy模块的deep.deepcopy()函数)
“=”赋值可变数据类型与不可变数据类型
a = 3
b = a # 指针 b 与 a 指向同一片内存地址:常量 3 所在的内存地址
a = 7 # 指针 a 现在改变指向,指向 7 所在的内存地址
b
3
a = [1, 2]
b = a # 指针 b 与 a 指向同一片内存地址:可变类型列表 [1, 2] 所在的内存地址
id(a)
140461052988872
id(b)
140461052988872
a = 1 # 指针 a 现在改变指向,指向常量 1 所在的内存地址
b
[1, 2]
a = [1, 2]
b = a # 指针 b 与 a 指向同一片内存地址:可变类型列表 [1, 2] 所在的内存地址
a.append(3)
b # 虽然指针 b 指向的地址没有改变,但指向的地址里的数据改变了,
[1, 2, 3]
copy.copy() 与 copy.deepcopy()
import copy # copy 浅拷贝,没有拷贝子对象,所以原始数据改变,子对象会改变
a=[1,2,3,4,['a','b']]
c=copy.copy(a)
a.append(5)
a
[1, 2, 3, 4, ['a', 'b'], 5]
c
[1, 2, 3, 4, ['a', 'b']]
a[4].append('c')
a
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
c
[1, 2, 3, 4, ['a', 'b', 'c']]
综上所述,浅copy和深copy:
copy浅拷贝(copy.copy()),没有拷贝子对象,所以原始数据改变,子对象会改变;
深copy(copy.deepcopy()),递归的复制它所包含的对象,所以原始对象的改变不会造成深拷贝里任何子元素的改变;
它们的相同之处是,父对象都没有改变.
Python里面如何拷贝一个对象·时间带着假象流淌
Python中的赋值(复制)、浅拷贝、深拷贝之间的区别·碧水幽幽泉
17.Python中的负索引是什么?
Python中的序列索引可以是正也可以是负。如果是正索引,0是序列中的第一个索引,1是第二个索引。
如果是负索引,(-1)是最后一个索引而(-2)是倒数第二个索引。
18.如何将一个数字转换成一个字符串?
你可以使用自带函数str()将一个数字转换为字符串。如果你想要八进制或者十六进制数,可以用oct()或hex()。
19.Python中的模块和包是什么?
在Python中,模块是搭建程序的一种方式。每一个Python代码文件都是一个模块,并可以引用其他的模块,比如对象和属性。
一个包含许多Python代码的文件夹是一个包。一个包可以包含模块和子文件夹。
20.函数中*args,**kwargs这两个参数是什么意思?我们为什么要使用它们?
如果不确定往一个函数中传入多少参数,或者我们希望以元组(tuple)或者列表(list)的形式传参数的时候,我们可以使用*args(单星号)。如果我们不知道往函数中传递多少个关键词参数或者想传入字典的值作为关键词参数的时候我们可以使用**kwargs(双星号),args、kwargs两个标识符是约定俗成的用法。
另一种答法:当函数的参数前面有一个星号号的时候表示这是一个可变的位置参数,两个星号**表示这个是一个可变的关键词参数。星号把序列或者集合解包(unpack)成位置参数,两个星号**把字典解包成关键词参数。
21.谈一谈Python的装饰器(decorator)
装饰器本质上是一个Python函数,它可以让其它函数在不作任何变动的情况下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景。比如:插入日志、性能测试、事务处理、缓存、权限校验等。有了装饰器我们就可以抽离出大量的与函数功能无关的雷同代码进行重用。
详细笔记点击这里
BAT面试题39:Python中的装饰器用过吗?
22.简要描述Python的垃圾回收机制(garbage collection)
Python中的垃圾回收是以引用计数为主,标记-清除和分代收集为辅。
引用计数:Python在内存中存储每个对象的引用计数,如果计数变成0,该对象就会消失,分配给该对象的内存就会释放出来。
标记-清除:一些容器对象,比如list、dict、tuple,instance等可能会出现引用循环,对于这些循环,垃圾回收器会定时回收这些循环(对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边)。
分代收集:Python把内存根据对象存活时间划分为三代,对象创建之后,垃圾回收器会分配它们所属的代。每个对象都会被分配一个代,而被分配更年轻的代是被优先处理的,因此越晚创建的对象越容易被回收。
Python垃圾回收机制--完美讲解!
23.说明os,sys模块不同,并列举常用的模块方法?
os模块是Python标准库中的一个用于访问操作系统功能的模块,使用os模块中提供的接口,可以实现跨平台访问
sys模块负责程序与Python解释器的交互,提供对python解释器相关的操作。
os和sys模块一些常用的方法:
24.什么是lambda表达式?它有什么好处?
lambda 是为编写简单函数而设计的,而 def 能处理更大的任务。lambda 主体结构中只能封装有限的逻辑,连 if 这样的语句都不能使用[P565]。
lambda表达式一般的形式是:关键词lambda后面紧接一个或多个参数,紧接一个冒号“:”,紧接一个表达式。lambda表达式是一个表达式不是一个语句。
L = {'add':(lambda x,y:x+y),
'pow':(lambda x,y:x**y)}
L'add'
5
L'pow'
8
lambda 也支持默认参数
f = lambda x, y = 2, z = 3:x + y + z
f(1)
6
f(1,5)
9
25.表达式与语句有什么区别?
Python代码由表达式和语句组成,并由Python解释器负责执行。
它们的主要区别是“表达式”是一个值,它的结果一定是一个Python对象。当Python解释器计算它时结果可以是任何对象。例如42,1+2,int(‘123’),range(10)等。
结果不是对象的代码则成为‘语句’。它们表示的是一个动作而不是生成或者返回一个值。常见的Python语句有print,if/else,def等
26.__new__和__init__的区别。
__init__为初始化方法,创建对象后,就立刻被默认调用了,可接收参数。
__new__方法是真正的构造函数。__new__是实例创建之前被调用,它的任务是创建并返回该实例。
__init__有一个参数self,就是__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值,__init__是实例创建之后被调用的,然后设置对象属性的初始值。
总结:__new__方法在__init__方法之前被调用,并且__new__方法的返回值将传递给__init__方法作为第一个参数,最后__init__给这个实例设置一些参数。
Python中的__new__及其用法
27.Python中单下划线和双下划线分别是什么?
name:一种约定,Python内部的名字,用来与用户自定义的名字区分开,防止冲突
_name:一种约定,用来指定变量私有
__name:解释器用_classname__name来代替这个名字用以区别和其他类相同的命名
python中的下划线
28.说一说Python自省。
自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型。简单一句话就是运行时能够获得对象的类型。比如:type()、dir()、getattr()、hasattr()、isinstance()
Python自省(反射)指南
29.如何在一个函数内部修改全局变量
比如:
a = 5
def foo():
a = 3
foo()
print(a) # 5
输出 5
a = 5
def foo():
"""
利用global在函数内部声明该变量,修改其为全局变量
"""
global a
a = 3
foo()
print(a) # 3
输出 3
30.列出你知道的python标准库
os, sys, re, math, datetime, string, random等
31.如何删除字典的键以及如何合并两个字典
d = {'name':'Lux','age':18}
d
{'age': 18, 'name': 'Lux'}
del d['age']
d
{'name': 'Lux'}
d2 = {'game':'LOL'}
d.update(d2)
d2
{'game': 'LOL'}
d
32.python2和python3的range(100)的区别
python2返回列表,python3返回迭代器,节约内存.
33.简述with方法打开处理文件帮我我们做了什么?
f = open("./t.txt", "w+")
try:
f.write("hello world")
except:
pass
finally:
f.close()
打开文件在进行读写的时候可能会出现一些异常状况,如果按照常规的f.open()写法,我们需要try,except,finally,做异常判断,并且文件最终不管遇到什么情况,都要执行 finally 后的语句f.close()关闭文件,with方法帮我们实现了finally中f.close()。
34.列表[1,2,3,4,5],请使用map()函数输出[1,4,9,16,25],并使用列表推导式提取出大于10的数,最终输出[16,25]
arr = [1, 2, 3, 4, 5]
def f(x):
return x**2
res = list(map(f, arr))
print(res) # [1, 4, 9, 16, 25]
res = [x2 for x in arr if x2 > 10]
print(res) # [16, 25]
35.python中生成随机整数、随机小数、0-1之间小数方法
import random
import numpy as np
a = random.randint(10,20)
res = np.random.randn(5)
ret = random.random()
print("正整数:"+str(a))
print("5个随机小数:"+str(res))
print("0-1之间的随机小数:"+str(ret))
"""
正整数:14
5个随机小数:[ 2.28309587 -0.17086874 -1.51438665 1.13570107 -0.09369251]
0-1之间的随机小数:0.6803547742221879
"""
python random
python--随机函数(random,uniform,randint,randrange,shuffle,sample)
36.python中的断言
assert()方法,断言成功,则程序继续执行,断言失败,则程序报错。
a = 3
assert(a > 1)
print("successfully")
b = 4
assert(b > 5)
print("failed")
"""
successfully
Traceback (most recent call last):
File "t.py", line 5, in
assert(b > 5)
AssertionError
""""
37.python2和python3区别?至少5个
1、Python3 使用 print 必须要以小括号包裹打印内容,比如 print('hi'),Python2 既可以使用带小括号的方式,也可以使用一个空格来分隔打印内容,比 如 print 'hi'
2、python2 range(1,10)返回列表,python3中返回迭代器,节约内存
3、python2 中使用 ascii 编码,python中使用utf-8编码
4、python2 中 unicode表示字符串序列,str表示字节序列,python3中str表示字符串序列,byte表示字节序列
5、python2中为正常显示中文,引入coding声明,python3中不需要
6、python2中是raw_input()函数,python3中是input()函数
38.列出python中可变数据类型和不可变数据类型,并简述原理
不可变数据类型:字符串、元组、数值、frozenset。
不可变变量的值发生变化,如果改变了变量的值,相当于新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象(一个地址),如下面代码中用id()方法可以打印对象的id
a = 3
b = 3
id(3)
94437307314304
id(b)
94437307314304
可变数据类型:列表、集合、字典、bytearray。
可变变量的值发生变化,即如果对变量进行append、+=等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化。对于相同的值的不同对象,在内存中地址不同,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。
a = [1,2]
b = [1,2]
a == b
True
a is b
False
id(a)
139733341882056
id(b)
139733368805448
39.字典根据键从小到大排序
y = {1:3, 2:2, 3:1}
by_key = sorted(y.items(),key = lambda item:item[0])
by_value = sorted(y.items(),key = lambda item:item[1])
print by_key # 结果为[(1, 3), (2, 2), (3, 1)],即按照键名排列
print by_value # 结果为[(3, 1), (2, 2), (1, 3)],即按照键值排列
python之对字典按照键的大小进行排序!
40.过滤掉列表 a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]中的所有偶数
a = [1,2,3,4,5,6,7,8,9,10]
print([x for x in a if x & 1])
def foo(a):
return a & 1
new_a = list(filter(foo, a))
print(new_a)
41.正则表达式匹配中,(.)和(.?)匹配区别?
(.)是贪婪匹配,会把满足正则的尽可能多的往后匹配
(.?)是非贪婪匹配,会把满足正则的尽可能少匹配
42.[[1,2],[3,4],[5,6]],一行代码展开该列表,得出[1,2,3,4,5,6]
a = [[1,2],[3,4],[5,6]]
new_a = [i for x in a for i in x]
new_a
[1, 2, 3, 4, 5, 6]
43.举例说明zip()函数用法
zip()函数在运算时,会以一个或多个序列(可迭代对象)做为参数,返回一个元组的列表。同时将这些序列中并排的元素配对。
zip()参数可以接受任何类型的序列,同时也可以有两个以上的参数;当传入参数的长度不同时,zip能自动以最短序列长度为准进行截取,获得元组。
a = [1,2]
b = ['a','b']
list(zip(a, b)) # a,b长度相同,构建元素是元组的列表
[(1, 'a'), (2, 'b')]
x = "abcdef"
y = "12345"
dict(zip(x, y)) # x,y长度不同,构建字典
44.提高python运行效率的方法
1、使用生成器,因为可以节约大量内存
2、循环代码优化,避免过多重复代码的执行
3、核心模块用Cython PyPy等,提高效率
4、多进程、多线程、协程
5、多个if elif条件判断,可以把最有可能先发生的条件放到前面写,这样可以减少程序判断的次数,提高效率
6、while 循环比基于迭代器的 for 循环慢的多,因为迭代器在 Python 内部是以 C 语言的速度运行的,而 while 循环则是通过 Python 虚拟机运行 Python 字节码的。[P419]
列表推导比手动 for 循环语句运行得更快(往往速度快一倍),这是因为它们的迭代在解释器内部以 C 语言速度执行的,而不是手动以 Python 代码执行的。尤其对于较大的数据集合,使用列表推导能带来极大的性能优势。[P426]
45.如何四舍五入?
a = 123.45678
round(a, 2)
123.46
round(a, 1)
123.5
round(a, 0)
123.0
round(a, -1)
120.0
round(a, -2)
100.0
round(a, -3)
0.0
a = 987.876
round(a, -3)
1000.0
round(a, -2)
1000.0
round(a, -1)
990.0
round(a, 0)
988.0
round(a, 1)
987.9
round(a, 2)
987.88
46.简述多线程、多进程
进程:
1、操作系统进行资源分配和调度的基本单位,多个进程之间相互独立
2、稳定性好,如果一个进程崩溃,不影响其他进程,但是进程消耗资源大,开启的进程数量有限制 线程:
1、CPU进行资源分配和调度的基本单位,线程是进程的一部分,是比进程更小的能独立运行的基本单位,一个进程下的多个线程可以共享该进程的所有资源
2、如果IO操作密集,则可以多线程运行效率高,缺点是如果一个线程崩溃,都会造成进程的崩溃 应用: IO密集的用多线程,在用户输入,sleep 时候,可以切换到其他线程执行,减少等待的时间 CPU密集的用多进程,因为假如IO操作少,用多线程的话,因为线程共享一个全局解释器锁,当前运行的线程会霸占GIL,其他线程没有GIL,就不能充分利用多核CPU的优势
47.简述any()和all()方法
都返回 bool 值。
any():只要迭代器中有一个元素为真就为真
all():迭代器中所有的判断项返回都是真,结果才为真
48.python中什么元素为假?
0,空字符串'',空列表[]、空字典{}、空元组()、空集合set()、None, False
49.IOError、AttributeError、ImportError、IndentationError、IndexError、KeyError、SyntaxError、NameError分别代表什么异常?
IOError:输入输出异常
AttributeError:试图访问一个对象没有的属性
ImportError:无法引入模块或包,基本是路径问题
IndentationError:语法错误,代码没有正确的对齐
IndexError:下标索引超出序列边界
KeyError:试图访问你字典里不存在的键
SyntaxError:Python代码逻辑语法出错,不能执行
NameError:使用一个还未赋予对象的变量
50.sort()和sorted()有什么区别?
sort() 只是 list 的方法,而 sorted() 可以接受任何的可迭代对象
若 L 为列表,L.sort() 无返回值,就地更改列表
sorted(L) 返回一个新的排好序的列表,原列表无变化
51.lambda函数配合sorted()进行复杂排序
对元素是元组的集合排序
a = {('a',3),('c',2),('b',1)}
sorted(a, key=lambda x:x[0])
[('a', 3), ('b', 1), ('c', 2)]
sorted(a, key=lambda :[1])
[('b', 1), ('c', 2), ('a', 3)]
多条件排序
a = [-5,8,0,5,4,-4,20,-2,-8,2,-20]
sorted(a, key=lambda x:(x<0,abs(x))) # 按绝对值排序,并且保证负数在正面后面
[0, 2, 4, 5, 8, 20, -2, -4, -5, -8, -20]
sorted(a, key=lambda x:(x<5,abs(x)))
[5, 8, 20, 0, -2, 2, 4, -4, -5, -8, -20]
sorted(a, key=lambda x:(abs(x),x<5))
[0, -2, 2, 4, -4, 5, -5, 8, -8, 20, -20]
首先,要知道sorted 内部实现使用了归并排序,而归并排序是稳定的排序,就是说当元素比不出大小时,其相对位置是不变的。
那么,利用稳定排序的特性,key函数有几个返回值就排序几次,先排序次要条件,后排序主要条件(条件从前到后重要程度依次降低),用主要条件覆盖次要条件,主要条件相同时,又不会改变之前排好的次要条件的相对位置,就实现了多条件排序。
给我们的直观效果是,主要条件排不出先后顺序的元素,就按照次要条件排序。
1<6>4>0 # 而这是按照链式法则比较大小,6大于4大于1,4大于0小于6,整个表达式为 True
True
(1<6)>(4>0) # True > True? 返回False
False
(1<6)>(4>9)
True
python 中其它类型的比较大小,比如比较两个列表的大小,其实是比较两个列表的布尔值,
但==就不一样了,比较的是内容,
>= 可以理解为 > or ==
True > False
True
a = [1,2,3]
b = [4,5,6]
a > b
False
a == b
False
c = [1,2,3]
a == c
True
a >= c
True
a >= b
False
a >= []
True
python sorted函数多条件排序是怎么回事
52.用两种方法去空格
s = "hello world ha ha"
res = s.replace(" ","")
res
'helloworldhaha'
s
'hello world ha ha'
res = s.split(" ")
res = "".join(res)
res
'helloworldhaha'
53.简述python引用计数机制
python垃圾回收主要以引用计数为主,标记-清除和分代清除为辅的机制,其中标记-清除和分代回收主要是为了处理循环引用的难题。
引用计数算法
当有1个变量保存了对象的引用时,此对象的引用计数就会加1,当使用del删除变量指向的对象时,如果对象的引用计数不为1,比如3,那么此时只会让这个引用计数减1,即变为2,当再次调用del时,变为1,如果再调用1次del,此时会真的把对象进行删除。
54.Python传参数是传值还是传址?
python中函数参数是引用传递(注意不是值传递)。
对于不可变类型(数值型、字符串、元组),因变量不能修改,所以运算不会影响到变量自身;
而对于可变类型(列表字典)来说,函数体运算可能会更改传入的参数变量。
def foo(L):
L.append([1,2])
arr = [3,4]
print(arr) # [3, 4]
foo(arr)
print(arr) # [3, 4, [1, 2]]
55.Python多线程(multi-threading)。这是个好主意吗?
Python并不支持真正意义上的多线程,Python提供了多线程包。Python中有一个叫Global Interpreter Lock(GIL)的东西,它能确保你的代码中永远只有一个线程在执行。经过GIL的处理,会增加执行的开销。这就意味着如果你先要提高代码执行效率,使用threading不是一个明智的选择,当然如果你的代码是IO密集型,多线程可以明显提高效率,相反如果你的代码是CPU密集型的这种情况下多线程大部分是鸡肋。
56.异常模块中try except else finally的区别
try..except..else没有捕获到异常,执行else语句
try..except..finally不管是否捕获到异常,都执行finally语句
57.is与==有什么区别?
在Python中一切都是对象。Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)。对象之间比较是否相等可以用==,也可以用is。
is 和 == 都是对对象进行比较判断作用的,但对对象比较判断的内容并不相同。
is比较的是两个对象的id值是否相等,是对象同一性测试,比较地址。也就是比较两个对象是否为同一个实例对象,是否指向同一个内存地址。
== 比较的是两个对象的内容是否相等,默认会调用对象的__eq__()方法。
a = [1,2,3]
b = a
print(id(a),id(b))
print('a is b? ', a is b)
print('a == b? ', a == b)
b = a[:]
print('-' * 30)
print(id(a),id(b))
print('a is b? ', a is b)
print('a == b? ', a == b)
"""
输出:
139944745517896 139944745517896
a is b? True
a == b? True
139944745517896 139944677151688
a is b? False
a == b? True
"""
58.python除法‘/’ 与 ‘//’
print(5/2) # 2.5
print(5//2) # 2
print(5//2.0) # 2.0 向下整除法(地板除),只要有一个为小数结果也会是小数
print(6/2) # 3.0 经典除法在python3.5中,整除也会保留一位小数。
print(6/2.0) # 3.0
print(5//-2) # -3 注意不是-2,而是-3,因为取小于-2.5最接近的整数
59.集合有哪些运算?
集合的运算与数学上的集合类似
A = set([1,2,3,4,5])
B = set([4,5,6,7,8])
C = set([1,2,3])
D = set([5,6])
E = set([5,6])
print(A | B) # {1, 2, 3, 4, 5, 6, 7, 8} A 并 B
print(A - B) # {1, 2, 3} 差集。A 减 A 交 B,即 A 有但 B 没有。
print(B - A) # {8, 6, 7}
print(A & B) # {4, 5} A 交 B
print(A ^ B) # {1, 2, 3, 6, 7, 8} 对称差集:集合的并集减去集合的交集
print(C < A, C < B) # True False 是否包含关系
print(B > D) # True
print(D > E, D < E, D == E) # False False True 相等不是包含
空集合只能用set(),而不是{},因为{}代表空字典。
60.列出字符串常用操作
s + s1 字符串拼接
s * 3 字符串重复
s.lower() 将字符串转换为小写,大小写仅限于英文字符
s.upper() 将字符串转换为大写
s.islower() 判断字符串是否为小写,输出为布尔类型
s.isupper() 判断字符串是否为大写,输出为布尔类型
s.isalpha() 判断字符串是否由字母组成,输出为布尔类型
s.isdigit() 判断字符串是否由数字组成,输出为布尔类型
s.isalnum() 判断字符串是由字母或数字字符组成,输出为布尔类型
s.replace(s1, s2) 将字符串中的子字符串替换为其它的字符串,用 s2 替换掉s1
s.replace(" ", "") 用 replace 来删除中间的空白字符, 其实就是将空白字符替换为空字符。
s.strip() 删除字符串首尾的空白字符
s.lstrip() 删除字符串首部的空白字符,这里的'l'是英文单词left
s.rstrip() 删除字符串尾部的空白字符,这里的'r'是英文单词right
s.find() 从左到右查找某字符串第一次出现的位置,如果查不到,则返回值为-1
s.rfind() 从右到左查找某字符串第一次出现的位置,如果查不到,则返回值为-1
s.count() 统计字符串出现的次数, 若没有相应的字符串,则返回0
s.startswith()、s.endswith() 分别用来判断字符串是否以指定的子字符串开始或结束,返回值为布尔类型。
s.join(s2) 将 s 插入到 s2 的每一个字符中 如:
s = "@@"
s2 = "abc"
s.join(s2)
'a@@b@@c'
当 s 为空,可作为字符拼接
a = ['I', 'love','U']
''.join(a)
'IloveU'
61.列出列表的常用操作
常用操作无非增删改查
增
L.insert(index, object) 在指定索引位置插入值
L.append(object) 在列表尾部添加值
L.extend(iter) 在列表尾部添加另一个可迭代对象 extend方法传入的是一个可迭代的对象,在使用extend方法时会将可迭代对象中的元素逐个加入到列表中。
a = [1,2,3]
b = a[:]
a.append("abc")
b.extend("abc")
a
[1, 2, 3, 'abc']
b
[1, 2, 3, 'a', 'b', 'c']
再比如,含多层嵌套时
a = [1,2,3]
b = a[:]
c = [9,[8,[7]]]
a.append(c) # append 将 c 整体作为一个元素添加进来
b.extend(c) # extend 遍历 c (深度为 1)将每一个元素添加进来
a
[1, 2, 3, [9, [8, [7]]]]
b
[1, 2, 3, 9, [8, [7]]]
删
L.pop(index=-1) 删除指定索引位置的元素,默认删除最后一个
L.remove(object) 删除列表中的元素
L.clear() 清空列表,L 存在
del L[slicing] # 删除 L 中某一个索引处的元素或某一段切片
del L 删除 L 这个对象,L 被垃圾回收
改
L[index] = new_value
查
L.index(object) 查找列表元素的索引,元素不存在时执行index方法会抛出异常:使用语法:list.index(value, [start, [stop]]), start表示查找的起始位置,stop表示查找的结束位置(闭区间,不包括stop)。start的默认值为0,end的默认值为列表的长度。
L.count(object) 统计元素的数量,元素不存在时返回0
62.两个等长列表如何构造字典?
L1 = ['a', 'b', 'c']
L2 = [1, 2, 3]
{k: v for (k, v) in zip(L1, L2)} # 使用推导构建字典
{'b': 2, 'a': 1, 'c': 3}
dict(zip(L1, L2)) # 使用 zip 构建字典
63.只有一个列表如何用enumerate构造字典?
L = ['a', 'b', 'c']
64.迭代器和可迭代对象分别是什么,它们之间有什么区别?
如果一个对象定义了 iter 和 next 两个方法,它就是一个迭代器。对于迭代器来说,iter 返回的是它自身 self,next 则是返回迭代器中的下一个值,如果没有值了则抛出一个 StopIteration 的异常。(关于这点,你可以想象成一个只进不退的标记位,每次调用 next,就会将标记往后移一个元素并返回,直到结束。)
如果一个对象定义了 iter 方法,返回一个迭代器对象,那么它就是一个可迭代的对象。 如果一个对象可迭代,那么就可以被 for 循环使用。比如经常用到的 list、dict、str 等类型。 迭代器(Iterator)和可迭代(Iterable)区别:
一个迭代器一定是可迭代对象,因为它一定有 iter 方法。反过来则不成立。(事实上,Iterator 就是 Iterable 的子类)
迭代器的 iter 方法返回的是自身,并不产生新实例。而可迭代对象的 iter 方法通常会生成一个新的迭代器对象。iter、next 分别对应于 Python 的内置函数 iter() 和 next()。
可迭代对象和迭代器
65.单遍迭代器与多遍迭代器有什么区别?
与 range 类似,内置函数 map、zip 和 filter 在 Python3.X 中也可以转换成可迭代对象以节约内存空间,而不是一次性在内存中产生一个结果列表。与 range 不同,map、zip 和 filter它们本身都是迭代器——在遍历一次后,它们就用尽了(单遍迭代器)。换句话说,你不能在它们的结果上拥有多个位于不同位置的迭代器(多遍迭代器)。
[P437,439]
R = range(3)
next(R) # range可迭代,但自身不是迭代器
Traceback (most recent call last):
File "", line 1, in
TypeError: 'range' object is not an iterator
I1 = iter(R)
next(I1)
0
next(I1)
1
I2 = iter(R) # I2 是一个新的迭代器,从第一个元素开始迭代,多遍迭代器
next(I2)
0
next(I1)
2
M = map(abs,(-2,-1,3))
M1 = iter(M)
M2 = iter(M)
next(M1)
2
next(M2) # M2 从 M1的位置开始迭代,单遍迭代器
1
next(M1)
3
next(M1) # 迭代器用尽了
Traceback (most recent call last):
File "", line 1, in
StopIteration
M3 = map(abs,(-2,-1,3)) # 创建一个新的迭代器,从头开始迭代(用 iter() 方法办不到)
next(M3)
2
66.什么是多态?
def times(x, y):
return x * y
times 函数中表达式 x * y 的意义完全取决于 x 和 y 的对象类型,同样的函数,在一种对象下执行的是乘法(3 * 3),在另一种对象下执行的确是重复(‘a’ * 3)Python 把对某一对象在某种语法下的合理性交给那个对象自身来判断,* 作为一个分派机制,将执行的控制权移交给被处理的对象。这种依赖类型的行为称为多态。其含义就是一个操作的意义取决于被操作对象的类型。
67.如何理解闭包?
工厂函数(闭包)能够记忆外层作用域里的值,不管那些嵌套作用是否还在内存中存在。
def maker(N):
def action(X):
return X ** N
return action
定义一个外层函数,返回一个嵌套函数,却并不调用内嵌函数。maker创造出action,却只是简单地返回action而不执行它。若调用外部函数,我们得到的只是内嵌函数的一个引用。
f = maker(2)
f(3) # 9
f(4) # 16
内嵌函数记住了 N = 2,即maker内部的变量N。实际上,在外层嵌套局部作用域内的N被作为执行状态信息保留了下来,并附加到生成的 action 函数上。
如果再调用外部函数,可以得到一个新的不同状态信息的嵌套函数。
g = maker(3) # 返回的action函数用来求一个数的立方
g(4) # 64
f(4) # 16
每次对工厂函数的调用,都将得到属于调用自己的状态信息的集合。我们使 g 函数记住了 N = 3,使 f 函数记住了 N = 2。每个函数都有自己的状态值,这个状态信息由 maker 中的变量 N 决定。
[P501]
68.谈谈类中的访问限制
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,内部访问用 self.__x。
需要注意的是,在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量。
以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。 双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。
不能直接访问__x是因为Python解释器对外把__x变量改成了_ClassName__x,所以,仍然可以通过_ClassName__x来访问__x 变量。