Python基础面试题
1.为什么学习Python?
Python是一门优秀的综合语言, Python的宗旨是简明、优雅、强大,在人工智能、云计算、金融分析、大数据开发、WEB开发、自动化运维、测试等方向应用广泛
2.通过什么途径学习的Python?
通过自学(博客、视频、github等)
3.Python和Java、PHP、C、C#、C++等其它语言的对比?
Python:轻量级、易学、自由/开放源码软件、可移植性、支持面向对象、丰 富的库、规范的代码。
Java:优点:开源性,功能强大,库多
缺点:编译速度 比较慢,不完全
PHP:优点:性能很强,配合简单,稳定,容易部署。
缺点:函数命名不规范,驼峰法和下划线,传参位置不一。
C: 优点:能操纵底层,能细粒度优化性能。
缺点:1、是面向过程的,2、运行时类型检查不可用,3、不提供命名 空间功能,4、构造函数和析构函数不可用。
C#:优点:强大的.NET Framework托管代码集合类,较简单的语言特性。 WEB应用程序开发速度快。
缺点:底层和高性能不合适,Windows平台以外支持有限。
C++: 优点:性能比较高,可进化型。
缺点:难学,门槛高
4.简述解释型和编译型编程语言?
编译型语言:C、C++、C#
使用专门的编译器,针对特定的平台,将高级语言源代码一次性的编译成可被该平台硬件执行的机器码,并包装成该平台所能识别的可执行性程序的格式。
特点:
在编译型语言写的程序执行之前,需要一个专门的编译过程,把源代码编译成机器语言的文件.
执行方式:
源代码 ———> 编译(一次编译) ———>目标代码———>执行(多次执行)———>输出
解释型语言:Python、PHP、ruby、java
使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行。
特点:
解释型语言不需要事先编译,其直接将源代码解释成机器码并立即执行,所以只要某一平台提供了相应的解释器即可运行该程序。
执行方式:
源代码 ———> 解释器(每次执行都需要解释)———>输出
5.Python解释器种类以及特点?
CPython
官方版本的解释器:CPython。这个解释器是用C语言开发的,所以叫CPython。在命令行下运行python就是启动CPython解释器。CPython是使用最广且被的Python解释器。
IPython
IPython是基于CPython之上的一个交互式解释器,也就是说,IPython只是在交互方式上有所增强,但是执行Python代码的功能和CPython是完全一样的。CPython用>>>作为提示符,而IPython用In [序号]:作为提示符。
PyPy
PyPy是另一个Python解释器,它的目标是执行速度。PyPy采用JIT技术,对Python代码进行动态编译(注意不是解释),所以可以显著提高Python代码的执行速度。绝大部分Python代码都可以在PyPy下运行,但是PyPy和CPython有一些是不同的,这就导致相同的Python代码在两种解释器下执行可能会有不同的结果。如果你的代码要放到PyPy下执行,就需要了解PyPy和CPython的不同点。
Jython
Jython是运行在Java平台上的Python解释器,可以直接把Python代码编译成Java字节码执行。
IronPython
IronPython和Jython类似,只不过IronPython是运行在微软.Net平台上的Python解释器,可以直接把Python代码编译成.Net的字节码。
6.位和字节的关系?
位:"位(bit)"是电子计算机中最小的数据单位。每一位的状态只能是0或1。
字节:8个二进制位构成1个"字节(Byte)",它是存储空间的基本计量单位。1 个字节可以储存1个英文字母或者半个汉字,换句话说,1个汉字占据2 个字节的存储空间。
7.b、B、KB、MB、GB的关系?
b 比特bit / 位
B——字节 1 B = 8b(8个bit/ 位)一个字节(byte)等于8位(bit)
KB——千比特 1 kB = 1024 B (kB - kilobajt)
MB——兆比特 1 MB = 1024 kB (MB - megabajt)
GB——吉比特 1 GB = 1024 MB (GB - gigabajt)
8.请至少列举5个PEP8规范?
一、代码编排
- 缩进。4个空格的缩进(编辑器都可以完成此功能),不使用Tap,更不能混合使用Tap和空格。
- 每行最大长度79,换行可以使用反斜杠,最好使用圆括号。换行点要在操作符的后边敲回车。
- 类和top-level函数定义之间空两行;类中的方法定义之间空一行;函数内逻辑无关段落之间空一行;其他地方尽量不要再空行。
二、文档编排
- 模块内容的顺序:模块说明和docstring—import—globals&constants — 其他定义。其中import部分,又按标准、三方和自己编写顺序依次 排放,之间空一行。
- 不要在一句import中多个库,比如import os, sys不推荐。
三、空行的使用:总体原则,避免不必要的空格
- 各种右括号前不要加空格。
- 逗号、冒号、分号前不要加空格。
- 函数的左括号前不要加空格。如Func(1)。
- 序列的左括号前不要加空格。如list[2]。
- 操作符左右各加一个空格,不要为了对齐增加空格。
- 函数默认参数使用的赋值符左右省略空格。
- 不要将多句语句写在同一行,尽管使用‘;’允许。
- if/for/while语句中,即使执行语句只有一句,也必须另起一行。
四、注释:总体原则,英文,简明
- 与代码自相矛盾的注释比没注释更差。修改代码时要优先更新注释!
- 注释是完整的句子。如果注释是断句,首字母应该大写,除非它是小写字母开头的标识符(永远不要修改标识符的大小写)。
- 如果注释很短,可以省略末尾的句号。注释块通常由一个或多个段落组成。段落由完整的句子构成且每个句子应该以点号(后面要有两个空格)结束,并注意断词和空格。
- 非英语国家的程序员请用英语书写你的注释,除非你120%确信代码永远不会被不懂你的语言的人阅读。
- 注释块通常应用在代码前,并和这些代码有同样的缩进。每行以 '# '(除非它是注释内的缩进文本,注意#后面有空格)。注释块内的段落用仅包含单个 ‘#’ 的行分割。
- 慎用行内注释(Inline Comments) 节俭使用行内注释。 行内注释是和语句在同一行,至少用两个空格和语句分开。行内注释不是必需的,重复罗嗦会使人分心。
五、命名:总体原则,新编代码必须按下面命名风格进行,现有库的编码尽量 保持风格
- b(单个小写字母)
- B(单个大写字母)
- lowercase(小写串)
- lower_case_with_underscores(带下划线的小写)
- UPPERCASE(大写串)
- UPPER_CASE_WITH_UNDERSCORES(带下划线的大写串)
- CapitalizedWords(首字母大写的单词串或驼峰缩写)
- 类的方法第一个参数必须是self,而静态方法第一个参数必须是cls。注意: 使用大写缩写时,缩写使用大写字母更好。故 HTTPServerError 比HttpServerError 更好
- mixedCase(混合大小写,第一个单词是小写)
- Capitalized_Words_With_Underscores(带下划线,首字母大写,丑陋)
六、编码建议
- 编码中考虑到其他python实现的效率等问题,比如运算符‘+’在CPython(Python)中效率很高,都是Jython中却非常低,所以应该采用.join()的方式。
- 尽可能使用‘is’‘is not’取代‘==’,比如if x is not None 要优于if x。
- 使用基于类的异常,每个模块或包都有自己的异常类,此异常类继承自Exception。
- 异常中不要使用裸露的except,except后跟具体的exceptions。
- 异常中try的代码尽可能少。
- 使用startswith() and endswith()代替切片进行序列前缀或后缀的检查。
- 使用isinstance()比较对象的类型。
- 字符串不要以空格收尾。
- 二进制数据判断使用 if boolvalue的方式。
9.通过代码实现如下转换:
二进制转换成十进制:v = “0b1111011”
十进制转换成二进制:v = 18
八进制转换成十进制:v = “011”
十进制转换成八进制:v = 30
十六进制转换成十进制:v = “0x12”
十进制转换成十六进制:v = 87
# hex()转16进制;oct()转8进制;bin()转二进制;int()转十进制
# 二进制转换成十进制
v = "0b1111011"
print(int(v, 2)) # 123
# 十进制转换成二进制
v = 18
print(bin(v)) # 0b10010
# 八进制转换成十进制
v = "011"
print(int(v, 8)) # 9
# 十进制转换成八进制:
v = 30
print(oct(v)) # 0o36
# 十六进制转换成十进制
v = "0x12"
print(int(v, 16)) # 18
# 十进制转换成十六进制:
v = 87
print(hex(v)) # 0x57
10.请编写一个函数实现将IP地址转换成一个整数。
如 10.3.9.12 转换规则为:
10 00001010
3 00000011
9 00001001
12 00001100
再将以上二进制拼接起来计算十进制结果:
00001010 00000011 00001001 00001100 = ?
def ipAddr_union(ip_addr):
li = ip_addr.split('.')
new_li = []
print(li) # ['10', '3', '9', '12']
for i in li:
numStr = str(bin(int(i)))
binStr = numStr.split('b')[1]
# print(binStr)
while len(binStr) < 8:
binStr = '0' + binStr
new_li.append(binStr)
print(new_li)
res_str = ''
for i in new_li:
res_str += i
print(res_str) # 00001010000000110000100100001100
res_int = int(res_str, 2)
print(res_int) # 167971084
ipAddr_union('10.3.9.12')
11.Python递归的最大层数?
import sys
sys.getrecursionlimit() # 获取最大递归层数 默认是1000(0-999)
sys.setrecursionlimit(1200) # 设置最大递归层数
12. 求结果:
v1 = 1 or 3
v2 = 1 and 3
v3 = 0 and 2 and 1
v4 = 0 and 2 or 1
v5 = 0 and 2 or 1 or 4
v6 = 0 or Flase and 1
print(1 or 3) # 1
print(1 and 3) # 3
print(0 and 2 and 1) # 0
print(0 and 2 or 1) # 1
print(0 and 2 or 1 or 4) # 1
print(0 or False and 1) # False
13.ascii、unicode、utf-8、gbk 区别?
编码 | 制定时间 | 作用 | 所占字节数 |
---|---|---|---|
ASCII | 1967年 | 表示英语及西欧语言 | 8bit/1bytes |
GB2312 | 1980年 | 国家简体中文字符集,兼容ASCII | 2bytes |
Unicode | 1991年 | 国际标准组织统一标准字符集 | 2bytes |
GBK | 1995年 | GB2312的扩展字符集,支持繁体字,兼容GB2312 | 2bytes |
UTF-8 | 1992年 | 不定长编码 | 1-3bytes |
14.字节码和机器码的区别?
机器码(machine code),学名机器语言指令,有时也被称为原生码(Native Code),是电脑的CPU可直接解读的数据。
通常意义上来理解的话,机器码就是计算机可以直接执行,并且执行速度最快的代码。
用机器语言编写程序,编程人员要首先熟记所用计算机的全部指令代码和代码的涵义。手编程序时,程序员得自己处理每条指令和每一数据的存储分配和输入输出,还得记住编程过程中每步所使用的工作单元处在何种状态。这是一件十分繁琐的工作,编写程序花费的时间往往是实际运行时间的几十倍或几百倍。而且,编出的程序全是些0和1的指令代码,直观性差,还容易出错。现在,除了计算机生产厂家的专业人员外,绝大多数的程序员已经不再去学习机器语言了。
机器语言是微处理器理解和使用的,用于控制它的操作二进制代码。8086到Pentium的机器语言指令长度可以从1字节到13字节。尽管机器语言好像是很复杂的,然而它是有规律的。存在着多至100000种机器语言的指令。这意味着不能把这些种类全部列出来。总结:机器码是电脑CPU直接读取运行的机器指令,运行速度最快,但是非常晦涩难懂,也比较难编写,一般从业人员接触不到。
字节码(Bytecode)是一种包含执行程序、由一序列 op 代码/数据对 组成的二进制文件。字节码是一种中间码,它比机器码更抽象,需要直译器转译后才能成为机器码的中间代码。
通常情况下它是已经经过编译,但与特定机器码无关。字节码通常不像源码一样可以让人阅读,而是编码后的数值常量、引用、指令等构成的序列。
字节码主要为了实现特定软件运行和软件环境、与硬件环境无关。字节码的实现方式是通过编译器和虚拟机器。编译器将源码编译成字节码,特定平台上的虚拟机器将字节码转译为可以直接执行的指令。字节码的典型应用为Java bytecode。
字节码在运行时通过JVM(JAVA虚拟机)做一次转换生成机器指令,因此能够更好的跨平台运行。
总结:字节码是一种中间状态(中间码)的二进制代码(文件)。需要直译器转译后才能成为机器码。
15.三元运算规则以及应用场景?
# 三元运算又称三目运算,是对简单的条件语句的简写:
# 为真时的结果 if 判断条件 else 为假时的结果(注意,没有冒号)
x = x + 1 if x % 2 == 1 else x
16.列举Python2和Python3的区别?
Python2默认的字符编码是ASCII,默认的文件编码也是ASCII ;
python3默认的字符编码是unicode,默认的文件编码是utf-8。
在python2里,将string处理为原生的bytes类型。
python3把字符串的编码改成了unicode, 还把str和bytes做了明确区分, str就是unicode格式的字符,bytes是单纯二进制。
print语句没有了,取而代之的是print()函数。
在python 2.x中/除法整数相除的结果是一个整数,把小数部分完全忽略掉,浮点数除法会保留小数点的部分得到一个浮点数的结果。
在python 3.x中/除法不再这么做了,对于整数之间的相除,结果也会是浮点数。
捕获异常的语法由 except exc, var 改为 except exc as var。
Python 2.x 中反引号相当于repr函数的作用 Python 3.x 中去掉了
这种写法,只允许使用repr函数
Py3.X去除了long类型,现在只有一种整型——int,但它的行为就像2.X版本的long
在 Python 2 中 有range()和xrange() ,一般使用xrange()创建迭代对象。
在 Python 3 中,range() 是像 xrange()那样实现,xrange()内置函数已经删除。
17.用一行代码实现数值交换:a = 1 b = 2
a = 1
b = 2
a, b = b, a
print(a) # 2
print(b) # 1
18.Python3和Python2中 int 和 long的区别?
python2有非浮点数准备的int和long类型。int类型最大值不能超sys.maxint,而且这个最大值是平台相关的。可以通过在数字的末尾附上一个L来定义长整型,显然,它比int类型表示的数字范围更大。
python3里,只有一种整数类型int,大多数情况下,和python2中的长整型类似。
19.xrange和range的区别?
python2里,有两种方法获得一定范围内的数字:range(),返回一个列表,还有xrange(),返回一个迭代器。
python3里,range()返回迭代器,xrange()不再存在。
20.文件操作时:xreadlines和readlines的区别?(python2.3)
返回类型不同:
readlines()读取所有行然后把它们作为一个字符串列表返回。
xreadlines返回的是生成器
readline()方法读取整行,包括行结束符,并作为字符串返回;
每次读取一行,返回的是一个字符串对象,保持当前行的内存
21.列举布尔值为False的常见值?
print(bool(0)) # False
print(bool(None)) # False
print(bool("")) # False
print(bool(())) # False
print(bool([])) # False
print(bool({})) # False
22.字符串、列表、元组、字典每个常用的5个方法?
字符串:索引、查找find(‘e’)、移除空白strip()、长度len()、替换replace(‘h’, ‘H’)
列表:切片[1:4]、追加append()、插入insert()、修改li[2]=“修改”、删除remove和pop
元组:索引ages[3]、切片name[0:2]、长度len()、创建tuple、删除del
字典:keys()返回包含字典所有key的列表;values()返回包含字典所有value列表;items()返回一个包含所有键值的列表;get()查看字典key对应的值;len()查看字典长度。
23.lambda表达式格式以及应用场景?
lambda函数就是可以接受任意多个参数(包括可选参数)并且返回单个表达式值得函数。
语法:lambda [arg1 [,arg2,…argn]]:expression
def calc(x,y):
return x*y
# 将上述一般函数改写为匿名函数:
lambda x,y:x*y
应用:
- lambda函数比较轻便,即用即仍,适合完成只在一处使用的简单功能。
- 匿名函数,一般用来给filter,map这样的函数式编程服务
- 作为回调函数,传递给某些应用,比如消息处理。
24.pass的作用?
- 空语句 do nothing
- 保证格式完整
- 保证语义完整
25.*arg和**kwarg作用?
*args代表位置参数,它会接收任意多个参数并把这些参数作为元组传递给函数。
**kwargs代表的关键字参数,允许你使用没有事先定义的参数名。
位置参数一定要放在关键字参数的前面。
作用:使用*args和**kwargs可以非常方便的定义函数,同时可以加强扩展性,以便日后的代码维护。
26.is和==的区别?
==是python标准操作符中的比较操作符,用来比较判断两个对象的value(值)是否相等;
is也被叫做同一性运算符,这个运算符比较判断的是对象间的唯一身份标识,也就是id是否相同。
27.简述Python的深浅拷贝以及应用场景?
导入模块:import copy
浅拷贝:copy.copy
深拷贝:copy.deepcopy
浅拷贝指仅仅拷贝数据集合的第一层数据,深拷贝指拷贝数据集合的所有层。
所以对于只有一层的数据集合来说深浅拷贝的意义是一样的,比如字符串,数字,还有仅仅一层的字典、列表、元祖等.
应用:
浅拷贝在拷贝大量数据且不需要改变内部元素的值的时候,能大量的减少内存的使用;
深拷贝在拷贝大量数据的时候,需要在前后内部元素的内容进行改变的时候,可以修改拷贝出来的模板
28.Python垃圾回收机制?
- 回收计数引用为0的对象,释放其占用空间
- 循环垃圾回收器。释放循环引用对象
29.Python的可变类型和不可变类型?
可变类型:list、dict、set、可变集合
不可变类型:string、int、float、tuple、不可变集合
30.求结果:
# Python 字典 fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值。
# dict.fromkeys(seq, value)
v = dict.fromkeys(['k1', 'k2'], [])
print(v) # {'k1': [], 'k2': []}
v['k1'].append(666)
print(v) # {'k1': [666], 'k2': [666]}
v['k1'] = 777
print(v) # {'k1': 777, 'k2': [666]}
31.求结果:
def num():
return [lambda x: i * x for i in range(4)]
# lambda表达式
print([m(2) for m in num()]) # 列表生成式 # [6, 6, 6, 6]
a = [i + 1 for i in range(10)]
print(a) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
32. 列举常见的内置函数?
abs() # 取绝对值
dict() # 把数据转为字典
help() # 帮助
min() # 找出最小值
max() # 找出最大值
setattr() # 设置属性值
bool() # 判断True or False(bool(0)、bool(Flase)、bool([]))
all() # 可循环的数据集合每个元素bool()均为True;或者空列表也是True
any() # 任意一个值是True即返回True
dir() # 打印当前程序里的所有变量
hex() # 转换为16进制数
slice() # 提前定义切片规则
divmod() # 传入两个变量a、b,得到a//b结果和余数a%b
sorted() # 列表排序sorted(li)等同于li.sort() 用法:sorted(iterable, key)
ascii(2) # 只能返回ascii码
enumerate([3,2,13,4]) # 返回列表的索引
input(‘dasd’)
oct(10) # 转八进制
staticmethod() #
bin(10) # 转二进制
open() # 文件打开
str() # 转字符串
isinstance()
ord(‘a’) # 返回97,ascii码中’a’位置
chr(97) # 返回’a’,输入97位置返回ascii码对应字符
sum([1,4,5,-1,3,0]) # 计算列表求和
pow(100,2) # 返回x的y次方,10000
callable() # 查看函数是否可以调用,还可用于判断变量是否是函数
format()
vars() # 打印变量名和对应的值
locals() # 打印函数的局部变量(一般在函数内运行)
globals() # 打印全局变量
repr() # 显示形式变为字符串
compile() # 编译代码
complex() # 将一个数变为复数
a = complex(3, 5) # (3+5j)
round(1.2344434,2) # 指定保留几位小数 输出1.23
delattr, hasattr, getattr, setattr # 面向对象中应用
hash() # 把一个字符串变为一个数字
memoryview() # 大数据复制时内存映射
set() # 把一个列表变为集合
set([12,5,1,7,9]) # {1, 5, 7, 9, 12}
33.fifilter、map、reduce的作用?
filter() # 将符合条件的值过滤出来
map(lambda x:x*x , [1,2,3,4,5]) # 根据提供的函数对指定序列做映射
reduce(function, iterable[, initializer]) # 对参数序列中元素进行累积
34.一行代码实现9*9乘法表
print("\n".join("\t".join(["%s*%s=%s" % (x, y, x * y) for y in range(1, x + 1)]) for x in range(1, 10)))
35.如何安装第三方模块?以及用过哪些第三方模块?
pip install 模块 或 npm install 模块
base64,requests,urllib3等
36.至少列举8个常用模块都有那些?
- sys:用于提供对解释器相关的访问以及维护,并有很强的交互功能
- time: 时间模块
- os:用于提供操作系统模块
- ashlib:用于加密相关的操作
- random:生成随机变量
- pickle:用于python特有的类和pthon的数据类型间进行转换
- datetime:date和time的结合体
- re:正则表达式模块
37.re的match和search区别?
re.match 从头开始匹配
re.search 匹配包含
match与search函数功能一样,match匹配字符串开始的第一个位置,search是在字符串全局匹配第一个符合规则的。
38.什么是正则的贪婪匹配?
import re
re.search('ab*c', 'abcaxc')
# <re.Match object; span=(0, 3), match='abc'>
re.search('ab\D+c', 'abcaxc')
# <re.Match object; span=(0, 6), match='abcaxc'>
# 贪婪匹配:正则表达式一般趋向于最大长度匹配,也就是所谓的贪婪匹配。
# 非贪婪匹配:就是匹配到结果就好,就少的匹配字符。
39.求结果:
a = [ i % 2 for i in range(10) ]
b = ( i % 2 for i in range(10) )
a = [i % 2 for i in range(10)]
print(a) # [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
b = (i % 2 for i in range(10))
print(b)
# 是生成器表达式(generator)
# <generator object <genexpr> at 0x0000023C4D1FF9A8>
40.求结果:
a. 1 or 2
b. 1 and 2
c. 1 < (2==2)
d. 1 < 2 == 2
print(1 or 2)
# 1
print(1 and 2)
# 2
print(1 < (2 == 2))
# False
print(1 < 2 == 2)
# True
41.def func(a,b=[]) 这种写法有什么坑?
# 因为b是可变类型,每次调用这个方法b不会每次都初始化[]
def func(a, b=[]):
b.append(a)
print(b)
func(1) # [1]
func(1) # [1, 1]
func(1) # [1, 1, 1]
# 想每次执行只输出[1] ,默认参数应该设置为None
42.如何实现 “1,2,3” 变成 [‘1’,’2’,’3’] ?
s = '1,2,3'
li = s.split(',') # 指定分隔符对字符串进行切片
print(li) # ['1', '2', '3']
43.如何实现[‘1’,’2’,’3’]变成[1,2,3] ?
li = ['1', '2', '3']
# 方法一:
new_li = [int(x) for x in li]
print(new_li) # [1, 2, 3]
# 方法二:
new_li2 = list(map(lambda x: int(x), li))
print(new_li2) # [1, 2, 3]
44.比较:a = [1,2,3] 和 b = [(1),(2),(3) ] 以及 b = [(1,),(2,),(3,) ] 的区别?
a = [1, 2, 3]
b1 = [(1), (2), (3)]
b2 = [(1,), (2,), (3,)]
for x in a:
print(type(x)) # <class 'int'>
for x in b1:
print(type(x)) # <class 'int'>
for x in b2:
print(type(x)) # <class 'tuple'>
# a和b1相同,因为只有一个元素的元组需要加,来表示(1,)
# b1表示的列表元素为整数,b2表示的列表元素是元组
45.如何用一行代码生成[1,4,9,16,25,36,49,64,81,100] ?
li = [x ** 2 for x in range(1, 11)]
print(li) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
46.一行代码实现删除列表中重复的值 ?
# 集合的主要作用是去重和关系运算.
li = [1, 1, 4, 7, 4, 9, 7, 9, 11]
new_li = list(set(li))
print(new_li) # [1, 4, 7, 9, 11]
47.如何在函数中设置一个全局变量 ?
def change_name():
global name
# (不建议使用global)在函数内修改全局变量,不能放在函数内局部变量后面
48.logging模块的作用?以及应用场景?
python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志。
49.请用代码简答实现stack 。
class Stack:
"""栈:先进后出"""
def __init__(self):
self.stack = []
def push(self, element):
"""进栈"""
self.stack.append(element)
def pop(self):
"""出栈"""
if len(self.stack) > 0:
return self.stack.pop()
else:
print("栈已经空了")
def get_top(self):
"""取栈顶"""
if len(self.stack) > 0: # 判断栈是否为空
return self.stack[-1]
else:
return None
50.常用字符串格式化哪几种?
# % 格式化字符串操作符
print('hello %s and %s' % ('df', 'another df')) # hello df and another df
# 字典形式的字符串格式化方法
print('hello %(first)s and %(second)s' % {'first': 'df', 'second': 'another df'}) # hello df and another df
# 字符串格式化(format):
# 1、使用位置参数
# 位置参数不受顺序约束,且可以为{},参数索引从0开始,format里填写{}对应的参数值。
msg = "my name is {}, and age is {}"
print(msg.format("hqs", 22)) # my name is hqs, and age is 22
# 2、使用关键字参数
# 关键字参数值要对得上,可用字典当关键字参数传入值,字典前加 ** 即可
hash = {'name': 'john', 'age': 23}
msg = 'my name is {name}, and age is {age}'
print(msg.format(**hash)) # my name is john,and age is 23
# 3、填充与格式化
# :[填充字符][对齐方式 < ^ >][宽度]
print('{0:*<10}'.format(10)) # 左对齐 # 10********
51.简述 生成器、迭代器、可迭代对象 以及应用场景?
生成器(generator):列表元素可以按照某种算法推算出来(有规律的数组),则可以在循环的过程中不断推算出后续的元素。这种方式就不必创建完整的list,可以节省大量的空间。python中,这种一边循环一边计算的机制,称为生成器:generator。
迭代器(Iterator):可以被next()函数调用并不断返回下一个值得对象称为迭代器(Iterator)。
可迭代对象(Iterable):可以直接作用于for循环的对象(其中包含集合数据类型:list\tuple\dict\set\str;还包含生成器表达式和生成器函数)。可以使用isinstance()判断一个对象是否是Iterable对象。
52.用Python实现一个二分查找的函数。
def binary_search(li, val):
"""
二分查找
:param li: 输入的列表(从小到大排序的列表)
:param val: 输入的待查找的值
:return:
"""
left = 0
right = len(li) - 1
while left < right: # 候选区有值
mid = (left + right) // 2 # 整除得到mid的下标
if li[mid] == val:
return mid # 找到待查找的值
elif li[mid] > val:
# 待查找的值在mid左侧
right = mid - 1 # 更新候选区为之前的左候选区
else:
left = mid + 1 # 更新候选区为之前的右候选区
else:
# 找不到待查找的值
return None
li = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(binary_search(li, 3)) # 2
53.谈谈你对闭包的理解?
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
通俗易懂的说法:当某个函数被当成对象返回时,夹带了外部变量,就形成了一个闭包。
闭包存在的意义:它夹带了外部变量(私货)。同一个的函数夹带了不同的私货,就实现了不同的功能。
54.os和sys模块的作用?
os 模块提供了很多允许你的程序与操作系统直接交互的功能。
sys模块能帮助程序员访问与python解释器联系紧密的变量和函数。
55.如何生成一个随机数?
# random模块可以很容易生成随机数和随机字符串。
import random
import string
print(random.randint(1, 100)) # 1-100之间取一个随机数
print(random.randrange(1, 100)) # 1-100之间取一个随机数
# randint & randrange区别:randint包含100,randrange不包含100!!!
# string模块可以生成随机字符串
print(string.digits) # 数字字符串 '0123456789'
print(string.ascii_letters)
# 字母字符串 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
print(string.ascii_lowercase)
# 小写字母字符串 'abcdefghijklmnopqrstuvwxyz'
print(string.ascii_uppercase)
# 大写字母字符串 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
print(string.punctuation)
# 特殊字符 '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
56.如何使用python删除一个文件?
import os
# 删除一个文件,必须指定文件路径
# os.remove("文件路径")
# 查看文件列表,不传参数默认显示当前路径下的文件列表
# os.listdir('文件路径')
57.谈谈你对面向对象的理解?
OOP(Object Oriented Programing)编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述。与面向过程机械式的思维方式形成鲜明对比,面向对象更加注重对现实世界而非流程的模拟,是一种“上帝式”的思维方式。
核心就是对象二字,对象就是特征与技能的结合体。
优点:
- 使程序更加容易扩展和易更改,使开发效率变得更高(对某个对象类属性的修改,会立刻反映到整个体系中)
- 基于面向对象的程序可以使他人更加容易理解代码逻辑。
缺点:
编程复杂度高、可控性差(无法像面向过程程序那样精准预测问题处理过程与结果,对象之间的交互,比较难预测最终的结果)
适用场景:
应用于需求经常变化的软件中,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
58.Python面向对象中的继承有什么特点?
在python中,新建的类可以继承一个或多个父类(其他语言只能继承一个父类),父类又可以称为基类或者超类。
仅在python2中才分新式类和经典类,在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类。
59.面向对象深度优先和广度优先是什么?
在子类继承多个父类时,属性查找方式分深度优先和广度优先两种。
当类是经典类时,多继承情况下,在要查找属性不存在时,会按照深度优先方式查找下去。
当类是新式类时,多继承情况下,在要查找属性不存在时,会按照广度优先方式查找下去。
60.面向对象中super的作用?
super() 函数是用于调用父类(超类)的一个方法。
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
作用:
- 根据 mro 的顺序执行方法
- 主动执行Base类的方法
61.是否使用过functools中的函数?其作用是什么?
Python的functools模块用以为可调用对象(callable objects)定义高阶函数或操作。
简单地说,就是基于已有的函数定义新的函数。
所谓高阶函数,就是以函数作为输入参数,返回也是函数。
62.列举面向对象中带双下划线的特殊方法
__dict__:查出一个字典,所有实例共享的变量和函数,dir()的子集
__name__:查看类的名字(这里的类名是字符串类型的)
__doc__:显示注释
__module__:类定义所在的模块
__init__:为对象定制自己独有的特征
__base__:只查看从左到右继承的第一个子类
__bases__:查看所有继承的父类
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发
__getitem__(self,key):返回键对应的值
__setitem__(self,key,value):设置给定键的值
__delitem__(self,key):删除给定键对应的元素
__str__,__repr__:改变对象的字符串显示
__format__:自定制格式化字符串
__call__:方法由对象后加括号触发,即:对象() 或者 类()()
63.如何判断是函数还是方法?
函数:函数是封装了一些独立的功能,可以直接调用,python内置了许多函数,同时可以自建函数来使用。
方法:方法和函数类似,同样封装了独立的功能,但是方法是需要通过对象来调用的,表示针对这个对象要做的操作,使用时采用点方法。
64.静态方法和类方法区别?
绑定方法:绑定给谁,就应该由谁来调用,谁来调用就会把调用者当作第一个参数自动传入
绑定到对象的方法:在类内定义的没有被任何装饰器修饰的,自动将对象当做第一个参数传入(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
绑定到类的方法:在类内定义的被装饰器@classmethod修饰的方法。自动将类当做第一个参数传入(其实对象也可以调用,但仍将类作为第一个参数传入)
非绑定方法:在类中用@staticmethod装饰器装饰的方法。不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。
65.列举面向对象中的特殊成员以及应用场景
1.
__doc__ # 输出:类的描述信息
2.
__module__和__class__
__module__表示当前操作的对象在那个模块
__class__表示当前操作的对象的类是什么
3.
__init__
构造方法, 通过类创建对象时, 自动触发执行。
4.
__del__
析构方法, 当对象在内存中被释放时, 自动触发执行。
5.
__call__
对象后面加括号, 触发执行。即: 对象()或类()()
class Foo:
def __init__(self):
print('__init__')
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
6.
__dict__类或对象中的所有成员
7.
__str__
如果一个类中定义了__str__方法, 那么在打印对象时, 默认输出该方法的返回值。
8.
__getitem__
__setitem__
__delitem__
类似字典一样索引的操作,以上分别表示获取,设置,删除。(当在类外面做了这些操作后触发这些函数,甚至可删除不存在的数据)
class Foo(object):
def __getitem__(self, key):
print('__getitem__', key)
def __setitem__(self, key, value):
print('__setitem__', key, value)
def __delitem__(self, key):
print('__delitem__', key)
obj = Foo()
result = obj["k1"]
# 自动触发执行 __getitem__ __getitem__ k1
obj['k2'] = 'wupeiqi'
# 自动触发执行 __setitem__ __setitem__ k2 wupeiqi
del obj['k1']
# 自动触发执行 __delitem__ __delitem__ k1
9.
__new__
用来将类实例化的
def __new__(cls, *args, **kwargs):
# 重构__new__方法:在实例化之前做些事情
print("Foo --new--")
return object.__new__(cls)
# 继承父类的__new__方法(此时cls是一个对象)
66. 1、2、3、4、5 能组成多少个互不相同且无重复的三位数
li = [1, 2, 3, 4, 5]
new_li = []
for i in li:
for j in li:
for m in li:
if i != j and i != m and j != m:
num_str = str(i) + str(j) + str(m)
num = int(num_str)
new_li.append(num)
new_set = set(new_li)
new_list = list(new_set)
new_list.sort()
print(new_list)
print(len(new_list)) # 60 5*4*3
67.什么是反射?以及应用场景?
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个实现反射的函数:
hasattr(object,name) # 判断object中有没有一个name字符串对应的方法或属性
getattr(object, name, default=None) # 获取属性
setattr(x, y, v) # 设置属性
delattr(x, y) # 删除属性
实现可插拔机制,常用于web框架的CBV配置文件获取类。
68.metaclass作用?以及应用场景?
元类是类的类,是类的模板。
元类作用:是控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为。
69.用尽量多的方法实现单例模式。
# 单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间
# 如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了
# settings.py文件内容如下
HOST = '1.1.1.1'
PORT = 3306
# 方式一:定义一个类方法实现单例模式
class Mysql:
__instance = None
def __init__(self, host, port):
self.host = host
self.port = port
@classmethod
def singleton(cls):
if not cls.__instance:
cls.__instance = cls(HOST, PORT)
return cls.__instance
obj1 = Mysql('1.1.1.2', 3306)
obj2 = Mysql('1.1.1.3', 3307)
print(obj1 is obj2) # False
obj3 = Mysql.singleton()
obj4 = Mysql.singleton()
print(obj3 is obj4) # True
# 方式二:定制元类实现单例模式
class Mymeta(type):
def __init__(self, name, bases, dic): # 定义类Mysql时就触发
# 事先先从配置文件中取配置来造一个Mysql的实例出来
self.__instance = object.__new__(self) # 产生对象
self.__init__(self.__instance, HOST, PORT) # 初始化对象
# 上述两步可以合成下面一步
# self.__instance=super().__call__(*args,**kwargs)
super().__init__(name, bases, dic)
def __call__(self, *args, **kwargs): # Mysql(...)时触发
if args or kwargs: # args或kwargs内有值
obj = object.__new__(self)
self.__init__(obj, *args, **kwargs)
return obj
return self.__instance
class Mysql(metaclass=Mymeta):
def __init__(self, host, port):
self.host = host
self.port = port
obj1 = Mysql() # 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址
obj2 = Mysql()
obj3 = Mysql()
print(obj1 is obj2 is obj3) # True
obj4 = Mysql('1.1.1.4', 3307)
# 方式三:定义一个装饰器实现单例模式
def singleton(cls): # cls=Mysql
_instance = cls(HOST, PORT)
def wrapper(*args, **kwargs):
if args or kwargs:
obj = cls(*args, **kwargs)
return obj
return _instance
return wrapper
@singleton # Mysql=singleton(Mysql)
class Mysql:
def __init__(self, host, port):
self.host = host
self.port = port
obj1 = Mysql()
obj2 = Mysql()
obj3 = Mysql()
print(obj1 is obj2 is obj3) # True
obj4 = Mysql('1.1.1.3', 3307)
obj5 = Mysql('1.1.1.4', 3308)
print(obj3 is obj4) # False
70.装饰器的写法以及应用场景。
import datetime
def log(func): # 装饰器接受一个函数作为参数,并返回一个函数
def wrapper(*args, **kw):
print('call %s(): ' % func.__name__)
return func(*args, **kw)
return wrapper
@log # 运用@语法把装饰器放置在函数定义处
def now():
print(datetime.datetime.now())
now()
"""
call now():
2020-01-13 15:58:31.348274
"""
# 应用场景:Django的csrf,缓存,登录认证,Falsk中的许多装饰器
71.异常处理写法以及如何主动跑出异常(应用场景)
try:
"""执行语句"""
except: # 异常类型
"""触发异常后执行的语句"""
finally:
"""有没有异常都执行的语句"""
# 主动抛出异常
raise # 异常类实例
72.什么是面向对象的mro
对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。
python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
73.isinstance作用以及应用场景?
isinstance(obj,cls)检查obj是否是类 cls 的对象
74.写代码并实现:
给定一个整数数组,返回两个数字的索引,使它们加起来等于一个特定的目标。您可以假设每个输入都会只有一个解决方案,并且不能两次使用相同的元素。
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1]
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
# 方法一:太抽象难以理解
for index, value in enumerate(nums):
next_index = index + 1
if target - value in nums[next_index:]:
return [index, nums[next_index:].index(target - value) + next_index]
# 方法二:
# 一次循环求差,然后在给定的list中寻找是否有相同值出现,求其index。
# 有一个小技巧是可以先用x代替原始元素。
# 只需要一次循环就可完成,在时间复杂度上优于第一种算法。
result = []
nums_1 = nums.copy() # 浅拷贝
i = 0
while len(nums) > 0:
pop = nums.pop(0) # 弹出列表的第一个元素
jj = nums_1[i] # jj对应的值为index为0-3的值
nums_1[i] = 'x'
if target - pop in nums_1:
next_index = nums_1.index(target - pop) # 如果targe-下一个目标值不为零 就将列表的第一个索引赋值给next_index
if i != next_index: # 如果弹出后的第一个索引不等于下一个目标索引就可以认定 这两个索引就是目标索引
result.append(i)
result.append(next_index)
break
nums_1[i] = jj # #如果不满足条件就将原始值赋给第一位
i += 1 # 每次循环初始索引加1
return result
nums = [2, 7, 11, 15]
s = Solution()
target = 9
print(s.twoSum(nums, target))
75.json序列化时,可以处理的数据类型有哪些?如何定制支持datetime类型?
# json只能支持int\str\list\tuple\dict
import json
import datetime
from json import JSONEncoder
class ComplexEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
else:
return super(ComplexEncoder, self).default(obj)
d = {'name': 'alex', 'data': datetime.datetime.now()}
print(json.dumps(d, cls=ComplexEncoder))
# {"name": "alex", "data": "2020-01-13 16:15:21"}
76.json序列化时,默认遇到中文会转换成unicode,如果想要保留中文怎么办?
import json
a = json.dumps({"ddf": "你好"}, ensure_ascii=False)
print(a)
# {"ddf": "你好"}
77.什么是断言?应用场景?
# assert断言——声明其布尔值必须为真判定,发生异常则为假。
info = {}
info['name'] = 'egon'
info['age'] = 18
# if 'name' not in info:
# raise KeyError('必须有name这个key')
#
# if 'age' not in info:
# raise KeyError('必须有age这个key')
# 用assert取代上述代码:
assert ('name' in info) and ('age' in info)
# 设置一个断言目的就是要求必须实现某个条件。
78.有用过with statement吗?它的好处是什么?
with语句的作用是通过某种方式简化异常处理,它是所谓的上下文管理器的一种
用法举例如下:
with open('output.txt', 'w') as f:
f.write('Hi there!')
当你要成对执行两个相关的操作的时候,这样就很方便,以上便是经典例子,with语句会在嵌套的代码执行之后,自动关闭文件。
这种做法的还有另一个优势就是,无论嵌套的代码是以何种方式结束的,它都关闭文件。如果在嵌套的代码中发生异常,它能够在外部exception handler catch异常前关闭文件。如果嵌套代码有return /continue / break语句,它同样能够关闭文件。
79. 使用代码实现查看列举目录下的所有文件。
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
print(BASE_DIR)
print(os.listdir(BASE_DIR))
"""
/Users/hqs/PycharmProjects/日常练习
['字符串倒序.py', '学生类统计实例化个数.py', '类计算器.py', '生成器日志调用.py', 'lamba_test.py']
"""
80.简述 yield和yield from关键字。
当一个函数中出现yield关键字的时候,那么这个函数就是一个生成器(generator)。
函数转化为generator后,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
yield from iterable就是for item in iterable: yield item的语法糖。
注意yield from 后面一定是可迭代对象(iterable)。