Python 面试问答

Python面试常见问答题整理

写在前面:
发布的文章同时也是自己的学习过程记录,包含一些个人记录和帖子的分享。
本篇文本出处摘自这里,部分内容有本人结合自身所需的增改

Python语言基础

1.列出常用Python标准库和第三方库?

标准库第三方库
import os操作系统相关操作 import numpy as np 数学
import sys命令行参数(sys.path) import pandas as pd 数据分析模块
import re正则表达式 import matplotlib 画图
import random产生随机数 requests http库
import math数学模块 scrapy 爬虫
import datetime日期时间 flask/djongo web框架
import threading线程 pytorch 机器学习
import multiprocessing进程 sikit-learn 机器学习
import glob 基于文件通配符搜索pymysql 数据库

2.Python内建数据类型有哪些?

类型名具体类型
数值型int, float, bool (True/False),complex
字符串str
列表list
元组tuple
集合set
字典dict

注意:Python没有char型数据

3.简述 with 方法打开处理文件帮我我们做了什么?

with方法详解

  • with语句适用于对资源进行访问的场合,无论是否发生异常都会进行必要的清理操作以释放资源,如文件使用后自动关闭,线程中自动进行锁的获取和释放等。
  • with关键字后面跟着的语句会返回一个上下文管理器,上下文管理器在使用__enter__()方法进入运行时上下文,操作结束后使用__exit__()退出运行时上下文。
  • 补充:如果不使用with关键字进行操作,直接以open执行文件,需要手动关闭文件释放占用的资源: file.close( )
  • 读文件:
with oepn('/path/demo.txt') as file:
	data = file.read()
  • 写文件:
with oepn('/path/demo.txt', 'w') as file:
	file.write('Hello tomorrow!')

4.列出 Python 中可变数据类型和不可变数据类型,为什么?

类别类型名特点
可变数据类型list,set, dict变量名指向的内存地址可以改变,对其修改是修改了当前地址内的对象。对可变数据类型使用“=”进行复制是浅拷贝,实际上相当于把两个名字不同的指针指向同一地址处的对象,修改其中一个时另外一个也会跟着改变,消除这种影响需要使用深拷贝。
不可变数据类型数值型,str,tuple变量名指向的内存地址不可变,对其“修改”相当于重新赋值,会开辟一个新的内存地址来存放。

5.Python 获取当前日期?

import datetime
datetime.datetime.now()

6.统计字符串每个单词出现的次数

from collections import Counter
result = dict( Counter(string.split()))

result = { word:string.split().count( word )  for word in string.split()}

Ref:别的不错的帖子

7.用 python 删除文件和用 linux 命令删除文件方法

# python方法:
	import os
	os.remove('demo.txt'# Linux方法:
	rm 'demo.txt'

8.写一段自定义异常代码

if xxx: raise ValueError('你这个值有问题')    # 或者其他异常类型,抛出错误后就不会执行下面的语句了
else: print('很棒,没问题')

9.举例说明异常模块中 try except else finally 的相关意义

• try:需要检测的异常代码片段
• except:发生需要重点检查的异常类型时执行的操作
• else:发生除了上面except中出现的异常以外其他的异常类型时执行的操作
• finally:不管是否发生异常都会执行的代码段

补充:Python异常检测

  • 单一异常
try:
    print(1/0)
except ZeroDivisionError as e:
        print(e)
  • 多个异常
for i in range(2):
    print(i)
    try:
        if i == 0:
            print(1/0)
        else:
            print(1 + 'str')
    except (ZeroDivisionError, TypeError) as e:
        print(e)
  • 异常捕获:通过 try:执行可能报错的语句,通过 except:提供异常的解释和修正方案,报错会从这里执行进行更正。
  • 异常报错解读:异常具有传递性,pycharm中提供的报错信息从高层向低层依次呈现,问题的根源大多在报错的最后一行出处。
  • 使用raise关键字来主动触发异常,并可以在括号内加入对该异常的说明。

10.遇到 bug 如何处理

• 根据报错信息找到相应代码,一般的数据结构和算法错误一般找到就能解决;
• 设置断点进行debug;
• 上搜索引擎,看看人家怎么解决的类似问题;
• 长时间解决不出来可以请教他人,别人可能迅速看出来问题而你自己看不出来,因为是你写的bug啊。。
• 此路不通,绕道迂回。

Python语言特性

1. Python与其他语言的区别

• 语法简洁优美:省略了大括号、分号,以及一些关键字、类型说明;
• 是一门动态、可移植、可扩展、可嵌入的解释型编程语言;
• 动态类型语言:变量不用事先声明类型,并且后期可以修改修改数据类型;
• 强类型语言:很少进行隐式的变量类型转换;
• 面向对象:```class```是类,```__init__()```是类的构造函数,函数是类的方法,```self```是类的实例代表当前对象的而地址。对象是通多类定义的数据结构实例,包含类变量和实例变量两种数据成员和类定义的方法。
• 具有强大完备的第三方库,功能强大;
• 应用广泛:web运维、自动化测试、爬虫、数据分析、人工智能等。

2. 解释型和编译型语言

○ 解释型语言(边解释边执行):使用解释器将代码逐行解释成机器码并立即执行,相当于把编译和解释混合到一起完成;跨平台容易,只需提供特定平台的解释器;但运行效率低。相当于每次执行都进行一次编译。
○ 编译型语言(编译后执行):集中转换,进行整体的编译和链接处理再运行。

3. python的解释器种类及相关特点

CPython:官方解释器,使用C语言开发;
IPython:基于CPython的交互式解释器,在其基础上交互式有所增强;
PyPy:采用JIT技术对python进行动态编译,可以显著提升速度,结果可能与CPython结果有所不同;
Jython:运行在Java平台的Python解释器,可以将Python代码直接编译成Java字节码执行;
IronPython:运行在微软.Net平台,可以直接把python代码编译成.Net的字节码。

4. python3与python2之间的区别

a. 编码:
	python2字符类型:str:编码后的字节序列,Unicode:编码前的文本字符;  
	python3字符类型:str:编码后的Unicode文本字符,bytes:编码前的字节序列。  
	python2中两种状态都有encode和decode方法,python3优化后str只有encode方法转化为字节码,bytes只有decode方法转化为文本字符串。
	python2中需要在文件头上加注释 # coding: utf-8 指定编码格式。
b. print
python2中print是class,python3中print是函数。

c. input
python2中input解析输入为int型,raw_input解析输入为str型;python3中input解析输入为str型。

d. 算术符
python2中/对于整数除法会将结果省去小数部分,对于浮点数会保留小数部分(真除),python3中/一律保留小数部分。

e. xrange
python2中使用xrange()创建迭代器对象,使用range()创建一个list;python3中移除了xrange()方法,使用range()创建一个迭代器对象。

5. python3和python2中int和long的区别

python2中有为非浮点数准备的int和long类型,int类型的最大值不超过sys.maxint,且这个值与平台相关。
python3中只有int类型没有long类型,大多数情况下这里的int很像python2中的long。

6. xrange和range的区别

python2中使用xrange()创建迭代器对象,使用range()创建一个list;python3中移除了xrange()方法,使用range()创建一个迭代器对象。

Python编码规范

7.什么是 PEP8?

PEP8官网

PEP【Python Enhancement Proposal】,即Python增强建议书,是一种代码规范,旨在增加代码可读性。

8.了解 Python 之禅么?

python的设计哲学:简洁、优雅。 import this:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

优美胜于丑陋;
明了胜于晦涩;
简洁胜于复杂;
复杂胜于凌乱;
扁平胜于嵌套;
间隔胜于紧凑;
可读性很重要;
为了特例的实用性也不可打破这些规则;
除非确实需要这样做,否则不要忽视错误;
当出现多种可能不要尝试去猜测,而是尽量取唯一一种明显的方法,可能这种方法一开始不那么明显但谁让你不是Python之父呢;
做好过不做,但不假思索地就去做也许还不如不做;
如果你无法向人描述你的方案,那肯定不是一个好方案;如果可以,那也许是个好方案;
命名空间是种绝妙的理念,让我们多多使用它吧!

9.了解 docstring 么?

docstring:文档字符串。用于解释程序,是模块、类、方法中的第一个声明性描述说明。由一对三重双引号引起来,如果其中有斜杠,引号前面加r;如果使用统一编码,引号前面加u;如果有多行,引号分别自成一行。

10.了解类型注解么?

Python是动态语言,变量和函数参数不区分类型,代码编写速度快。但阅读别人的代码或者回头看自己以前的代码可能需要一定时间理解这些变量返回值到底是什么类型。因此python3中添加了“类型注解”特性,可以给函数参数、返回值和变量加上注解,提高代码可读性。

  • 举例说明:
    原始pycharm使用内容:
def fun(data):
    data.update({"a": 1})

添加类型注释

def fun(data:Dict[str, int]):
    data.update({"a": 1})

更多详细内容参考此条链接

11.例举你知道 Python 对象的命名规范,例如方法或者类等。

• 变量:小写加下划线。
	○ 单下划线开头是私有变量,程序员之间约定不要在外部访问它,但实际上还是可以访问到的;
	○ 双下划线开头的变量是私有类型(private)的变量,只允许类内部访问,子类也不能访问;
	○ 双下划线开头和结尾的是内置变量,可以直接访问,如```__init__```,```__file__```,```__import__```等;
	○ 单下划线结尾的变量是为了和关键字区分,如```class_```。
• 全局变量和常量:全大写加下划线。
• 函数参数:小写加下划线。避免滥用```*args```和```*kwargs```,可能破坏函数健壮性。
• 函数,方法:小写加下划线。
	○ 私有方法:单下划线开头;
	○ 特殊方法(如运算符重载等):双下划线开头;
• 类:驼峰命名。基类前可加‘Base’或‘Abstract’。异常也是类,异常名为驼峰命名后加一个‘Error’。
• 模块和包:模块尽量使用简短全小写,为了提高可读性也可用下划线;包使用全小写,不推荐加下划线。

12.Python 中的注释有几种?

单行注释:使用#开始。
多行注释:使用三重单引号或双引号包含,注释中若需使用引号应该与外部不同的引号。

13.如何优雅的给一个函数加注释?

使用docstring。也就是PEP规范,官网分享

14.如何给变量加注释?

在代码后至少两个空格,使用单行注释#后一个空格再跟上注释语句;可以将注释对其但应注意一行最好不超过79个字符(PEP8)。

15.Python 代码缩进中是否支持 Tab 键和空格混用。

不可以混用! 但是看你具体怎么用,如果无关执行内容只是调整程序格式,保持美感,问题不大。
不同解释器对tab的解释是不同的,有的4字符宽有的8字符宽,如果混用的话就可能出现缩进错误。虽然使用空格会麻烦一丢丢,但是使用4个空格替代tab键会更好,有的编译器可以设置tab自动转空格可以试试。

16.是否可以在一句 import 中导入多个库?

• 可以,但不推荐。每行import中只导入一个库,更易于阅读、编辑、搜索、维护。
• 导入顺序:
	○ 标准库
	○ 第三方库
	○ 自定义模块
• 不建议from matplotlib import *:
	○ 占用更多内存空间,不必要的方法会被初始化;
	○ 代码可读性差,模块内突然会冒出来一个没有归属的方法,不知道从哪个库引入的。

17.在给 Py 文件命名的时候需要注意什么?

• 不与关键字重名;
• 不与标准库或第三方库重名,除非想重写;
• 不用中文命名任何路径和可执行文件;
• 不用字母数字下划线以外的字符,不用数字开头。

18.例举几个规范 Python 代码风格的工具

• pylint
• black
• autopep8
• flake8

Python字符串

19.列举 Python 中的基本数据类型?

类型名具体类型
数值型(number)int, float, bool (True/False),complex
字符串str
列表list
元组tuple
集合set
字典dict

20.如何区别可变数据类型和不可变数据类型

类别类型名特点
可变数据类型list,set, dict变量名指向的内存地址可以改变,对其修改是修改了当前地址内的对象。对可变数据类型使用“=”进行复制是浅拷贝,实际上相当于把两个名字不同的指针指向同一地址处的对象,修改其中一个时另外一个也会跟着改变,消除这种影响需要使用深拷贝。
不可变数据类型number,str,tuple变量名指向的内存地址不可变,对其“修改”相当于重新赋值,会开辟一个新的内存地址来存放。

如何区别:

  • 可变数据类型:在一个变量的内存id不变的情况下可以修改其值;
  • 不可变数据类型:一个变量的值修改之后其内存id也跟着修改了,本质上不是同一个变量了。

21.将"hello world"转换为首字母大写"Hello World"

def first_capitalize(sentence:str) -> str:
	'''
	**所有单词首字母大写**

	将句子中的每一个单词首字母均转化为大写并返回
	:param sentence: 要转化的句子
	:return: 转化后的句子
	'''
	words = sentence.split(' ')
	for i in range(len(words)):
		words[i] = words[i].capitalize()
		#Python capitalize() 将字符串的第一个字母变成大写,其他字母变小写。
	return ' '.join(words)#没见过的写法,要记住

if __name__ == '__main__':
	sentence = 'hello world'
	print(first_capitalize(sentence))

一行代码版本:

def first_capitalize(sentence:str) -> str:
	return ' '.join(list(map(lambda word: word.capitalize(), sentence.split())))
	#PS:这是匿名函数的写法,使用lambda只能执行一行代码,且只能临时使用一次

22.如何检测字符串中只含有数字?

使用isdigit()方法,若字符串string只由数字组成,string.isdigit()返回True,否则返回False。

23.将字符串"ilovechina"进行反转

方法1:使用列表的reverse函数(注意:字符串没有reverse函数)

string = list(string)
string.reverse()
string = ''.join(string)

方法2:使用切片

string[::-1]

方法3:使用reduce累加

from functools import reduce
string = reduce(lambda x, y: y+x, string)

方法4:使用栈模拟栈的先进后出就成功翻转了
这个思路也可以在C++中实现

# 模拟入栈
stack = list(string)
result_str = ''
for x in stack:
	# 模拟依次出栈,每次将最后的字符放进结果中
	result_str += stack.pop()
return result_str

24.Python中的字符串格式化方式你知道哪些?

  1. % - 格式化:当字符串较长或参数较多时,可读性和可维护性很差。
%s:字符
%d:整数
%f:浮点数
%x:十六进制整数
%r:原始字符串
  1. str.format:字典传参时需要重写参数名,不够优雅。
用中括号表示要传入的参数位置,format中的参数按中括号的顺序传入。

"Hello, {}. You are {}.".format(name, age)

支持字典形式传参,format中的参数不用严格按顺序传入。

"Hello, {name}. You are {age}.".format(name=name, age=age)
  1. f-string格式,py3.6开始支持,形式和format类似,形式更为简洁优雅。{}内支持任意表达式,可在其中进行运算和函数调用,性能也比前两种更优。
name = "Eric"
age = 74
f"Hello, {name}. You are {age}."

25.有一个字符串开头和末尾都有空格,比如“ adabdw ”,要求写一个函数把这个字符串的前后空格都去掉。

去除左边空格:string.lstrip()
去除右边空格:string.rstrip()
去除两边空格:string.strip()

26.获取字符串”123456“最后的两个字符。

切片:string[-2:]

27.一个编码为 GBK 的字符串 S,要将其转成 UTF-8 编码的字符串,应如何操作?

str.encode():将Unicode字符串编码成其他编码形式;
str.decode():将编码后的字符串解码成未编码的Unicode字符串;
chardet.detect(str):检查字符串类型并返回(需要import chardet)。

28. (1)s=“info:xiaoZhang 33 shandong”,用正则切分字符串输出’info’, ‘xiaoZhang’, ‘33’, ‘shandong’ a = "你好 中国 ",去除多余空格只留一个空格。

(1)正则切分字符串

  1. 题意:去除字母和数字以外的字符;
  2. compile:生成一个正则表达式对象;
  3. \w:匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集。\W:匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。
import re
pattern = re.compile(r'\W')

(2)去除多余空格

# 使用去除空格的专用函数:
a.strip()	# 去除两端空格
a.rstrip()	# 去除右端空格

29. (1)怎样将字符串转换为小写 (2)单引号、双引号、三引号的区别?

(1)字符串转换:

  1. 字符串中所有字符转大写:string.upper()
  2. 字符串中所有字符转小写:string.lower()
  3. 字符串中每个单词首字母大写:string.capitalize()
  4. 字符串首个单词的首字母大写:string.title()

(2)单引号、双引号、三引号的区别:
5. 单引号和双引号都可以表示字符和字符串,没有区别。只是需要注意,当使用单引号来定义一个字符串时,字符串中的单引号需要转义,而双引号被视为普通字符无需转义。同理,当使用双引号来定义一个字符串时,其中的双引号需要转义而单引号不需要。
6. 当使用单引号或双引号定义字符串时,想要定义一个多行字符串时需要在换行时加上字符\,最终并不能真正换行,而是隔着一段空白。使用三引号可以方便的定义一个多行字符串,字符串在代码中换行则在输出时也会换行。
7. 三引号用于文档字符串docstring,解释函数的作用、输入、输出。

Python列表

30.已知 AList = [1,2,3,1,2],对 AList 列表元素去重,写出具体过程。

  1. 转换成set再转回list。
AList = list(set(AList))
#因为set容器不允许存储重复的内容,和集合本身的数学含义相同
  1. 使用pandas的unique函数,函数返回array需要转换回list。
import pandas as pd
AList = pd.unique(AList).tolist()
#pandas第三方库的运用
  1. 使用dict.formkeys(seq[, value])):函数创建一个新的字典,seq参数是键列表,value参数是对应的值(默认为None)。
AList = list(dict.formkeys(AList))
#字典的{key:value}结构特性
  1. 循环判断
AList = [1,2,3,1,2]
result = []
for x in AList:
	if x in result:
		continue
	else:
		result.append(x)

31.如何实现 “1,2,3” 变成 [“1”,“2”,“3”]

string = "1,2,3"
result = string.split(',')

32.给定两个 list,A 和 B,找出相同元素和不同元素

  1. 直接操作list:
same_items = [x for x in A if x in B]
diff_items = [x for x in A if x not in B] + [x for x in B if x not in A]
  1. 使用set操作:
same_items = list(set(A) & set(B))
diff_items = list(set(A) ^ set(B))
  • Python中集合交并补运算方法
    简单来说包括 &与 |或 ^交 属于<=
    集合的应用:增、删、冻结
    指路帖

33.[[1,2],[3,4],[5,6]]一行代码展开该列表,得出[1,2,3,4,5,6]

# 1. 列表推导式
result = [item for inner_list in outer_list for item in inner_list]

# 2. 循环嵌套展开
result = []
for inner_list in outer_list:
	for item  in inner_list:
		result.append(item)

# 3. 用sum对列表求和
result = sum(outer_list, [])

34.合并列表[1,5,7,9]和[2,2,6,8]

# 1. 使用'+'
result = list1 + list2

# 2. 使用extend
list1.extend(list2)

# 3. 使用append
for x in list2:
	list1.append(x)

35.如何打乱一个列表的元素?

import random
random.shuffle(a_list)

Python字典

36.字典操作中 del 和 pop 有什么区别

  1. demo_dict.pop(key[,default]):
    key:删除键key对应条目并返回其键值;
    defaul:可选,若字典中不存在key则返回default值。

  2. del demo_dict[key]:删除键key对应条目;

  3. 扩展:
    demo_dict.clear():清空字典所有条目;
    del demo_dict:删除字典。

37.按照字典的内的年龄排序

知识点:字典列表排序,处理的对象还是列表

demo_dict = [
	{'name': 'Amily', 'age': 16},
	{'name': 'Renee', 'age': 23},
	{'name': 'Vivian', 'age': 18},
]

# 1. 按age升序排列
demo_dict.sort(key=lambda item: item['age'])
# 等价于:
sorted(demo_dict, key=lambda item: item['age'])

# 2. 按age降序排列[reverse默认为False,即升序]
demo_dict.sort(key=lambda item: item['age'], reverse=True)

# 3. 先按age,再按name排序
demo_dict.sort(key=lambda item: (item['age'], item['name']))

扩展:字典排序【处理的对象是字典,字典没有sort方法,只能用sorted】

demo_dict ={'2':'3', '1':'2', '3':'0'}

# 1. 按键排序,返回键的有序列表::
keys = sorted(demo_dict)
# keys: ['1', '2', '3']

# 2. 按键排序,返回排序后的字典list:
result = sorted(demo_dict.items())
result = sorted(demo_dict.items(), key = lambda item: (item[0], item[1]))
# result: [('1', '2'), ('2', '3'), ('3', '0')]

# 3. 按值排序,返回排序后的字典list:
result = sorted(demo_dict.items(), key = lambda item: (item[1], item[0]))
# result: [('3', '0'), ('1', '2'), ('2', '3')]

38.请合并下面两个字典 a = {“A”:1,“B”:2},b = {“C”:3,“D”:4}

字典a更新为两字典合并后的结果:

a.update(b)

39.如何使用生成式的方式生成一个字典,写一段功能代码。

字典生成式:d = {key: value for (key, value) in iterable}

# 例1:
lis = [('name','zhangsan'),('age','11'),('phone','a')]
dic = {k:v for k,v in lis}
# dic: {'phone': 'a', 'name': 'zhangsan', 'age': '11'}

# 例2:
lis = ['1', '2', '3']
dic = {k:'defult_value' for k in lis}
# dic: {'1': 'defult_value', '2': 'defult_value', '3': 'defult_value'}

40.如何把元组(“a”,“b”)和元组(1,2),变为字典{“a”:1,“b”:2}

zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。
py3中zip()返回一个迭代器,需要手动转换成dict。

keys = ('a', 'b')
values = (1, 2)
zipped = zip(keys, values)
# dict(zipped): {'a': 1, 'b': 2}
# list(zipped): [('a', 1), ('b', 2)]

# 解压缩:
unzipped = list(zip(*zipped))
# unzipped: [('a', 'b'), (1, 2)]

Python综合

41.Python 常用的数据结构的类型及其特性?

------- | -------
数据结构 | 特性
list | 可变
set | 可变,元素唯一
dict | 可变,字典中的键是不可变数据类型,可变数据类型不可作为字典的键
tuple | 不可变

42.如何交换字典 {“A”:1,“B”:2}的键和值?

字典推导式:

d = {"A": 1,"B": 2}
res = {v: k for k, v in d.items()}
# res: {1: 'A', 2: 'B'}

43.Python 里面如何实现 tuple 和 list 的转换?

tuple转list:list(t)
list转tuple:tuple(l)

44.我们知道对于列表可以使用切片操作进行部分元素的选择,那么如何对生成器类型的对象实现相同的功能呢?

使用itertools.islice():

import itertools

# 斐波那契数列生成器
def fbnq(num):
	a, b = 1, 1
	for _ in range(num):
		a, b = b, a + b
		yield a

if __name__ == '__main__':
	generator = fbnq(10)
	# generator是一个迭代器: <generator object fbnq at 0x000001F12536BFC0>
	# 不能直接对生成器取值或切片,如generator[2],generator[2:]

	# itertools.islice对生成器进行切片:
	islice = itertools.islice(generator, 10, 20)
	# islice仍然是一个迭代器: <itertools.islice object at 0x0000022B4DAB3778>
	for x in islice:
        print(x)

45.请将[i for i in range(3)]改成生成器

gennerator = (i for i in range(3))

46.a="hello"和 b="你好"编码成 bytes 类型

a="hello"
a1 = a.encode()  
# 编码后 a1: b'hello'
# 检查编码后类型 chardet.detect(a1) : {'encoding': 'ascii', 'confidence': 1.0, 'language': ''}

b="你好"
b.encode()
b1 = b.encode()  
# 编码后 b1: b'\xe4\xbd\xa0\xe5\xa5\xbd'
# 检查编码后类型 chardet.detect(b1) :{'encoding': 'utf-8', 'confidence': 0.7525, 'language': ''}

47.下面的代码输出结果是什么?

a = (1, 2, 3, [4, 5, 6, 7], 8)
a[2] = 2

答:由于元组是不可变数据类型,对其进行修改会报错。 TypeError: 'tuple' object does not support item assignment

48.下面的代码输出的结果是什么?

a = (1, 2, 3, [4, 5, 6, 7], 8)
a[3][0] = 2

a[3]是列表,可变数据类型,可以进行修改。这里修改的是元组的子对象而不是元组,元组的内存id不会改变,即引用没有改变,是允许的。
即常考的元组和列表之间的区别

Python操作类题目

49.Python 交换两个变量的值

a, b = b, a

50.在读文件操作的时候会使用 read、readline 或者 readlines,简述它们各自的作用

  • read: 读取整个文件,将文件内容放到一个字符串变量中。如果文件过大,一次读取全部内容到内存容易造成内存不足,更推荐大家使用逐行读取文件的方式。
  • readline: 读取文件中的一行,包含最后的换行符“\n”,返回的是一个字符串对象,保持当前行的内存。
  • readlines: 每次按行读取整个文件内容,包含最后的换行符“\n”,将读取到的内容放到一个字符串列表中,返回list类型。
with open('path.txt') as file:
	file_content = file.read()
	line_content = file.readline([size]) 	# size参数可选,指定一次最多读取的字符数
	lines_content = file.readlines()

51.json 序列化时,可以处理的数据类型有哪些?如何定制支持 datetime 类型?

  • 序列化:把python的对象编码转换为json格式的字符串,反序列化:把json格式字符串解码为python数据对象。
  • 序列化时可以处理的数据类型:列表、元组字典、字符、数值、布尔值、None。(没有set和datetime)
  • 定制datetime类型:
from datetime import datetime
import json
from json import JSONEncoder

class DatetimeEncoder(JSONEncoder):
	'''扩展JSONEcoder中的default方法

	判断传入类型是否为datetime类型:
	若是则转为str字符,否则返回父类的值
	'''
	def default(self, o):
		if isinstance(o, datetime):
			return o.strftime('%Y-%m-%d %H:%M:%S')
		else:
			return super(DatetimeEncoder, self).default(o)

if __name__ == '__main__':
	dict_demo = {'name':'Renee', 'data':datetime.now()}
	print(json.dumps(dict_demo,cls=DatetimeEncoder))
  • isinstance(object, classinfo)认为子类是一种父类类型,考虑继承关系;type(object)不会认为子类是一种父类类型,不考虑继承关系。
  • super(type[, object-or-type]):调用父类的方法;
  • json.dumps():将 Python 对象编码成 JSON 字符串。

52.json 序列化时,默认遇到中文会转换成 unicode,如果想要保留中文怎么办?

将dumps的默认参数 ensure_ascii 设置为False。

53.有两个磁盘文件 A 和 B,各存放一行字母,要求把这两个文件中的信息合并(按字母顺序排列),输出到一个新文件 C 中。

分为三步:读文件、信息合并、写文件。

def read_file(file_path):
	'''读文件

	读取文件并返回文件内容
	:param file_path: 文件路径
	:return: 文件内容
	'''
	with open(file_path, 'r') as file:
		return file.read()


def write_file(file_path, file_data):
	'''写文件
	
	将数据写入指定文件中
	:param file_path: 要写入的文件路径
	:param file_data: 要写入的数据
	:return: 
	'''
	with open(file_path, 'w') as file:
		file.write(file_data)


def letter_sort(letter_str, reverse_flag=False):
	'''按字典排序

	:param letter_str: 需要排序的字母字符串
	:param reverse_flag: 排序顺序,False为正序,True为反序
	:return: 排序后的新字符串
	'''
	return ''.join(sorted(letter_str, reverse=reverse_flag))


if __name__ == '__main__':
	data1 = read_file('test1.txt')
	data2 = read_file('test2.txt')
	new_data = letter_str(data1 + data2)
	write_file('new.txt', new_data)

54.如果当前的日期为 20190530,要求写一个函数输出 N 天后的日期,(比如 N 为 2,则输出 20190601)。

from datetime import datetime
from datetime import timedelta


def date_calculation(now_date, offset):
    '''获取日期

    获取几天前或几天后的日期
    :param now_date: 当前日期
    :param offset: 日期偏移量,负数为向前偏移
    :return: 偏移后的日期
    '''
    now_date = datetime.strptime(now_date, '%Y-%m-%d').date()
    offset_date = timedelta(days=offset)
    return (now_date + offset_date).strftime('%Y-%m-%d')


if __name__ == '__main__':
    print(date_calculation('2019-12-13', 7))

55.写一个函数,接收整数参数 n,返回一个函数,函数的功能是把函数的参数和 n 相乘并把结果返回。

闭包:

  • 将内部函数的引用作为返回值;
  • 保存运行环境,在闭包内的变量不能被轻易修改;
  • 提高代码复用性。
def out_func(n):
    '''闭包函数

    :param n: 整数参数n
    :return: 内层函数
    '''
    def in_func(num):
        return num * n

    return in_func


if __name__ == '__main__':
    demo = out_func(3)
    print(demo(4))

此例中n=3, num=4, 最后输出12.

56.下面代码会存在什么问题,如何改进?

def strappend(num):        # 函数作用、参数意义不明,需要加注释
	str = 'frist'          # 不能使用关键字"str"作为变量名
	for i in range(num):   # 遍历得到的元素"i"意义不明,无注释
		str += str(i)      # 变量名和关键字在这个时候重名,必定报错,没有了str()方法
return str

修改后的代码:

def str_append(num):
    '''字符串添加

    :param num: 添加数字【0到num-1】到字符串末尾
    :return: 修改后的字符串
    '''
    s = 'frist'
    # 获取数字0~num-1,添加到字符串末尾
    for i in range(num):
        s += str(i)
    return s


if __name__ == '__main__':
    print(strappend(5))

57.一行代码输出 1-100 之间的所有偶数。

print([i for i in range(101) if i % 2 == 0])

58.with 语句的作用,写一段代码?

with语句:上下文管理器,用于资源访问的场合,自动在使用后进行资源的释放和异常处理。
线程锁:

import threading

num = 0			# 全局变量,多个线程可以读写,用于数据chuandi
thread_lock = threading.Lock()  # 创建一个锁


class MyThread(threading.Thread):
    """线程锁

    实现一个线程锁
    :param threading.Thread:当前线程
    :return:
    """

    def run(self):
        global num
        with thread_lock:			# 当前上下文,自动获取和释放线程资源
            for i in range(1000):  # 锁定期间,其他线程不能运行
                num += 1
        print(num)

59.python 字典和 json 字符串相互转化方法

import json

# 序列化:

dict_demo = {'a': 1, 'b': 2}
# 1. 使用json.dumps()将python数据类型转化为json字符串
json_demo = json.dumps(dict_demo)
# 2. 使用json.dump()将python数据序列化后保存到指定文件
with open('demo.json') as file:
    json.dump(dict_demo, file)


# 反序列化:

# 1. 使用json.loads()将json字符类型转化为python类型
dict_demo = json.loads(json_demo)
print(dict_demo)
# 2. 使用json.load()将json字符类型从指定文件中提取出来并转化为python类型
with open('demo.json', 'r') as file:
	file_data = json.load(file)
	print(file_data)

60.请写一个 Python 逻辑,计算一个文件中的大写字母数量

import re
def capital_count(file_path):
	'''计算文件中大写字母的个数
	
	读取文件,计算文件中大写字母的个数
	:param file_path:文件路径
	:return: 文件中大写字母的数量
	'''
	with open (file_path, 'r') as file:
		data = file.read()

	# 将非字母删除
	data = re.sub('^[a-zA-Z]', '', data)

	# 逐个判断是否为大写字母并计数
	cnt = 0
	for c in data:
		if c.isupper():
			cnt += 1
	return cnt


if __name__ == '__main__':
	print(capital_count('test.txt'))

61. 请写一段 Python连接 Mongo 数据库,然后的查询代码。

import pymongo

# 连接本地数据库
db_client = pymongo.MongoClient('mongodb://localhost:27017/')

# 切换到testdb数据库
test_db = db_client['testdb']

# 切换到’sites‘文档
sites_obj = test_db['sites']

# find_one() 方法查询集合中的一条数据
first_data = sites_obj.find_one()
print(first_data)

62.说一说 Redis 的基本类型。

  • 字符串: string
  • 列表: list
  • 哈希: hash
  • 集合: set
  • 有序集合sorted set: zset

63. 请写一段 Python连接 Redis 数据库的代码。

import redis

# 创建连接对象
conn_obj = redis.Redis(host='localhost', port=6379, db=0)

# 设置一个键值
conn_obj.set('test', '1')

# 读取一个键值
conn_obj.get('test')

64. 请写一段 Python 连接 MySQL 数据库的代码。

游标:一条SQL语句对应一个资源集合,而游标是访问其中一行数据的接口,相当于资源集合的下标。

import pymysql

# 连接数据库
db = pymysql.connect('localhost', 'testuser', 'test123', 'TESTDB', charset='utf8')

# 使用cursor()方法操作游标
cursor = db.cursor()

# 使用execute()方法执行SQL语句
cursor.execute('SELECT VERSION()')

# 使用fetclone()方法获取一条数据
data = cursor.fetclone()

# 关闭数据库连接
db.close()

65.了解 Redis 的事务么?

  1. 事务的概念:事务提供了“将多个命令打包一次性提交,并按顺序执行”的能力,事务在执行过程中不会被打断,只有事务全部执行完毕才会继续执行其他命令。
  2. redis事务实现:redis使用multi,exec,discard,watch实现事务:
  • multi:开始事务;
  • exec:提交并执行事务
  • discard:取消事务
  • watch:事务开始之前监视任意数量的键
  • unwatch:取消watch命令的监视
  1. redis的ACID:
  • 单独的隔离操作:事务中的命令在提交后按顺序执行,不会被其他命令打断;
  • 没有隔离级别的概念:队列中的命令在实际提交之前不会执行;
  • 不保证原子性:事务中若有一个任务执行失败,仍会继续执行后面的任务,不会回滚。

66.了解数据库的三范式么?

范式是为了解决“冗余数据,插入异常,删除异常,修改异常”,低级别范式依赖于高级别范式,1NF是最低级别的范式。

  • 第一范式:属性不可分
  • 第二范式:每个非主属性完全依赖于键码
  • 第三范式:非主属性不传递依赖于键码

67.了解分布式锁么?

当使用多进程或多线程的时候,多个进程或线程对同一资源进行抢占,对其读写操作可能引发数据不一致。因此,为了保证数据安全,引入了进程锁和线程锁对分布式系统共享资源的访问进行控制,通过互斥来保证一致性。
常见的分布式锁的实现有MySQL,zookeeper,redis等。

68.用 Python 实现一个 Reids 的分布式锁的功能。

略。

69.写一段 Python 使用 Mongo 数据库创建索引的代码。

import pymongo
from pymongo import ASCENDING, DECENDING

# 连接数据库,创建连接对象
my_client = pymongo.MongoClient(mongodburl)

# 切换数据库
my_db = my_client[dbName]

# 创建索引,create_index()创建索引,可以有多个约束条件,值1为升序,-1为降序
my_db.creat_index([('date', DECENDING), ('author', ASCENDING)])

Python高级特性

70.函数装饰器有什么作用?请列举说明?

函数装饰器是用来修改其他函数功能的函数,主要是在不修改原始代码的情况下对函数进行功能扩展,满足面向对象的“开闭原则”。
补充:
开闭原则:对扩展开放对修改关闭的原则

应用场景:

引入日志
程序运行时间统计
函数执行前准备工作
函数执行后清理工作
权限校验
缓存
事务处理

71.Python 垃圾回收机制?

1. 整数

  • 小整数:python将[-5,257]间的整数视为小整数,在一个python程序中,所有这个范围内的整数使用同一个对象。小整数对象是提前建立好的,因此不会被回收(单个字母同理)。
  • 大整数:每一个大整数的创建都会在内存中新分配内存空间,使用结束后会被回收。
    2. 机制:引用计数为主,标记清除和分代回收为辅。
  • 引用计数: 每个对象有一个结构体PyObject,其中的oj_refcnt为对象的引用计数。当对象新增引用时oj_refcnt加一,反之减一。当oj_refcnt为0时立即将其回收。优点是简单和实时性,缺点是维护oj_refcnt耗费资源,并且可能造成循环引用。
  • **标记清除:**基于追踪回收,第一阶段将所有的活动对象打上标记,第二阶段将非活动对象进行回收。标记清除作为引用计数的辅助方式进行容器对象(list, tuple,dict等)的回收,因为字符串和数值不会出现循环引用。
  • **分代回收:**空间换时间,将内存根据不同的对象存活时间划分集合,每个集合称为一代。年轻代(第0代),中年代(第1待),老年代(第2代)分别对应一个链表,三个代的垃圾回收频率随存活时间增大而减小。同样也是辅助回收容器对象。

72.魔法函数 __call__怎么使用?

__call__允许一个实例对象像函数一样被调用。

class Entity:
	def __init__(self, a, b):
		self.a = a
		self.b = b

	def __call__(self, a, b):
		self.a = a
		self.b = b

	def get(self):
		print(self.a, self.b)


if __name__ == '__main__':
	demo = Entity(1, 2)
	demo(3, 4)
	demo.get()

输出3 4

73.如何判断一个对象是函数还是方法?

定义:在类外使用def定义的为函数。在类内的,使用类调用为函数,使用实例化对象调用的为方法。
可以使用isinstance()进行判断。

from types import FunctionType
from types import MethodType

class DemoClass():
	def __init__(self):
		pass

	def run(self):
		pass

def demo_func():
	pass

if __name__ == '__main__':
	demo_obj = DemoClass()
	print(isinstance(demo_func, FunctionType))
	print(isinstance(DemoClass.run, FunctionType))
	print(isinstance(demo_obj.run, MethodType))

输出:

True
True
True

74.@classmethod 和@staticmethod 用法和区别

@classmethod 和@staticmethod都是装饰器,装饰的成员函数可以不实例化用类名.方法名()进行调用。
用途:

  • @classmethod: 多用于工厂模式,在实例化对象之前进行某些逻辑操作;
  • @staticmethod: 可以看作该类的一个工具、辅助函数。
    区别:
  • @classmethod 需要传递参数cls,因此可以访问和修改类的属性、方法和实例化对象等,避免硬编码。@classmethod可以判断自己是通过基类还是子类调用。
  • @staticmethod 不需要selfcls参数,和一般函数的使用方法一样。

75.Python 中的接口如何实现?

  • 接口:只定义了一些功能,而没有去实现,需要被一个类继承之后去实现其中的一个或多个功能。
  • python中的接口:由抽象类和抽象方法实现的,接口不能实例化,只能由继承类去实现功能。

76.Python 中的反射了解么?

  • 反射:在运行的时候自我检查,并对内部成员进行操作。变量的类型可以动态改变,到使用的时候再确定其作用;
  • python中的反射:可以通过一个对象找出其type、class、attribute和method的能力成为反射能力。具有反射能力的有type()、isinstance()、callable()、getattri()等函数。

77.metaclass 作用?以及应用场景?

metaclass创建类,可以认为类是metaclass创建出的实例,可以动态修改类。
场景:将数据库的表映射为类的时候,每个类都要根据表动态定义。

78.hasattr() getattr() setattr()的用法

  • hasattr(Object, name):判断对象是否有name属性;
  • getattr(Object, name[, defaul]):返回对象的name属性值,若没有属性则返回默认值;
  • setattr(Object, name, value):更改对象的name属性为value,若不存在则创建属性。

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

魔法方法:也就是前后加下划线的方法,这些方法在python中已经定好,在进行某些操作时会被自动调用。
最常用的:__new__,__init__,__del__

  • __new__:创建类并返回这个类的实例;
  • __init__:使用传入的参数初始化实例,与__new__一同构成了构造函数;
  • __del__:删除实例化的对象,即为析构函数。
    类操作:
  • __class__:获得已知对象的类,obj.__class__
  • __call__:可以让实例对象像函数一样被调用;
    属性访问
  • __getattr__:获取属性值;
  • __setattr__:设置属性值;
  • __delattr__:删除属性;
    上下文管理器
  • __enter__:进入上下文(with后面的语句出发);
  • __exit__:退出当前执行上下文(结束with语句时);
    迭代器方法
    __iter__:返回一个迭代器;
    __next__:返回迭代器的下一个元素。

80.如何知道一个 Python 对象的类型?

使用type()判断对象类型。

81.Python 的传参是传值还是传址?

  • 参数为可变类型时为传址,如list、dict;
  • 参数为不可变类型时为传值,如int、string、tuple。

82.Python 中的元类(metaclass)使用举例

与77题重复了。

83.简述 any()和 all()方法

  • any():判断给定的可迭代参数是否有至少一个不为空、0、False,是则返回ture;
  • all():判断给定的可迭代参数是否全为空、0、False,是则返回ture;

84.filter 方法求出列表所有奇数并构造新列表,a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

b = list(filter(lambda x: x % 2 == 1, a))

85.什么是猴子补丁?

在动态语言中,不改变源码对功能进行追加或修改。

86.在 Python 中是如何管理内存的?

python内存池:内存池就是预先在内存中申请一定数量的,大小相等的内存块留作备用。当有内存需求时先从内存池中分配,不够了再去申请新的内存。这样可以减少内存碎片,提升效率。

python中的内存管理机制–Pymalloc:在对象小于256bit的时候直接从内存池分配,否则执行new/malloc申请空间。

87.当退出 Python 时是否释放所有内存分配?

循环引用其他对象,或引用自全局命名空间的模块,在python退出时并未完全释放。
解决:循环垃圾回收器可以回收循环引用的对象,使用global()获取全局对象并手动删除。

Python正则表达式

88.使用正则表达式匹配出张明 98 分中的地址 a=“张明 98 分”,用 re.sub,将 98 替换为 100

import re
s = '<html><h1>张明 98 分</html>'
res = re.sub(r'\d{2,}', '100', s)

89.正则表达式匹配中(.)和(.?)匹配区别?

**解析正则表达式r'(.*) are (.*?) .*'就知道了。

  1. r表示raw字符串,忽略字符串中的转义字符,这个pattern中没有反斜杠,所以前面的r可有可无;
  2. (.*):为第一个匹配分组,匹配除换行符之外的所有字符,贪婪匹配,匹配尽可能多的字符;
  3. (.*?):为第二个匹配分组,*, +, {m, n}后面加了一个?之后变成非贪婪模式,只匹配一个;
  4. 最后的.*没有加括号,匹配结果不计入分组中;
  5. match_group.group()等同于match_group.group(0),为所有匹配分组;group(1)、group(2)…分别匹配第1、2…个分组。
  6. re.M:多行匹配,影响 ^ 和 $;re.I:使匹配对大小写不敏感。
import re
line = "The cats are smarter than dogs"
matchObj = re.match( r'(.*) are (.*?) .*', line, re.M|re.I)
print(matchObj.group(1))
print(matchObj.group(2))

输出:

The cats
smarter

90.写一段匹配邮箱的正则表达式

r'^[0-9a-zA-Z_]{0, 19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$'

Python 其他内容

91.解释一下 python 中 pass 语句的作用?

pass是空语句,不做任何操作,一般用作占位,为了保证结构完整性。
一般在搭建程序框架或写if语句的时候使用。

92.简述你对 input()函数的理解

input()接收用户输入。
python2中input()接收其他数据为字符串,接收纯数值为数值(int, float),rawinput()接收的数据为字符串。
python3中只有input(),数据类型为字符串。

93.python 中的 is 和==

  • is 是身份运算符,判断两个对象内存id是否相等(即:是否为同一对象);
  • == 是比较运算符,判断两个对象的值是否相等;

94.Python 中的作用域

  • 作用域:也就是命名空间,python创建、修改和查找变量都在命名空间中。变量在赋值创建时,其赋值的地方决定了其命名空间,即他的可见范围。

  • LEGB

    L(Local):局部作用域;
    E(Enclosing): 内层函数引用到了外层函数的变量就成了闭包,这个外层函数的变量就是闭包作用域。;
    G(Global): 全局作用域,global声明的全局变量;
    B(Built-in): 内建作用域,是一个名为builtins的内置模块,但是必须要导入之后才能使用内置作用域;
    使用LEGB的顺序查找

95.三元运算写法和应用场景?

res = True if a > b else False:a>b为条件,条件为真时结果为True,否则结果为False。

96.了解 enumerate 么?

enumerate()函数用于将可迭代对象(list,set,string等)组合成(索引,数据)的形式,一般用在for循环中遍历。
enumerate(sequence, [start=0])

97.列举 5 个 Python 中的标准模块。

os, re, random, math, datetime, threading, multiprocessing, functools, numpy, pandas, matplotlib, pytorch, sikit-learn, pymysql

98.如何在函数中设置一个全局变量

  1. 在所有函数体外定义的变量均为全局变量;
  2. 使用 global 关键字定义的变量均为全局变量。

99.pathlib 的用法举例

pathlib 模块提供了一组面向对象的类,这些类可以代表各操作系统上的路径,程序可以使用这些类操作路径。
可用于查看路径,拼接路径,读写文件。

from pathlib import Path

print(Path.cwd())		# 当前工作路径
print(Path.home())		# home路径

100.Python 中的异常处理,写一个简单的应用场景

举个栗子,写文件的时候检查是否有权限:

try:
	# 待检查的代码块
	file = open('test.text', 'w')
	file.write('test somthing')
except IOError as e:
	# 想要检测的异常类型
	print('except: ', e)
else:
	# 若没有发生异常
	print('200 OK')
finally:
	# 无论前面结果如何都会执行
	print('I am always here')

101.Python 中递归的最大次数,那如何突破呢?

  • 不同系统和python版本的最大递归次数不同,可以查看:我的win10为1000次;超过递归最大深度会引发不可预知的错误,如C栈溢出或其他错误。
  • 突破:手动设置。
import sys
version = sys.version				# python版本
limit = sys.getrecursionlimit()		# 最大递归深度

print(version)						# 3.6.3
print(limit)						# 1000

sys.setrecursionlimit(3000)			# 手动设置最大递归深度
limit = sys.getrecursionlimit()			
print(limit)						# 3000

102.什么是面向对象的 mro

MRO: Method Resolution Order(方法解析顺序)。
MRO是类的方法的解析顺序表,也就是继承父类方法时的解析表。
MRO是多继承和钻石继承的问题上的核心内容,规定了什么时候、如何调用父类的方法。
类名.__mro__:输出类的解析继承关系顺序。

class A:
	def f(self):
		print('A')

class B(A):
	def f(self):
		print('B')

class C(A):
	def f(self):
		print('C')

class D(B, C):
	def f(self):
		super().f()			# 调用B的函数
		super(B, self).f()	# 调用C的函数

if __name__ == '__main__':
	print(D.__mro__)
	# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
	# class A 继承自object

	obj_d = D()
	obj_d.f()		

103.isinstance 作用以及应用场景?

  • 用法isinstance(obj, calssinfo),obj为实例对象,classinfo可以为直接或间接类名、基本类型、或他们的元组。
  • 场景:判断对象的数据类型,判断类的继承关系。type无法判断继承关系。
class A:
	pass

class B(A):
	pass

print(isinstance(A(), A))	# True
print(type(A()) == A)		# True
print(isinstance(B(), A))	# True
print(type(B()) == A)		# False

104.什么是断言?应用场景?

assert用于判断一个表达式,当表达式为False的时候返回错误。
断言可以在不满足条件的情况下直接返回错误,而不必等到程序运行失败。
场景:如程序只能在Linux运行,可以在运行之前判断是否满足。

assert expression [, args]

# 相当于
if not expression:
	raise AssertionError(args)

105.lambda 表达式格式以及应用场景?

lambda表达式即为匿名函数,通常用在函数体很简单的情况下,用lambda表达式替代函数定义。
用法: lambda 参数1, 参数2... : 参数表达式
场景:

功能简单的函数;
不用关注命名的函数;
只用一次或很少复用的函数

例1:按绝对值排序

list_demo = [3, 5, -4, -1, 0, -2, -6]
res = sorted(list_demo, key = lambda x: abs(x))

例2:输出1-100内的奇数

res = list(filter(lambda x: x % 2 == 1, range(1,101)))

例3:闭包lambda

def outer(a, b):
	return lambda x: a*x + b

out = outer(2, 2)
print(out(3))		# 输出8

106.新式类和旧式类的区别

区别: 新式类多继承搜索顺序为广度优先:先在水平方向查找,再向上查找。旧式类多继承搜索顺序为深度优先:先深入继承树,再返回。
实现:
python2中默认都是经典类,只有显式继承了object的类才是新式类:

  • class A(object): pass # 新式类
  • class A(): pass # 经典类
  • class A: pass # 经典类
    python3中取消了经典类,默认都是新式类,且不需要显式继承object:
  • class A(object): pass # 新式类,推荐写法
  • class A(): pass # 新式类
  • class A: pass # 新式类

107.dir()是干什么用的?

  • dir() 不带参数时,返回当前作用域的所有变量、方法和定义的类型列表;
  • dir() 带参数时,返回参数的属性、方法。
class A:
	def f(self):
		print('A')


if __name__ == '__main__':
	print(dir())
	# ['A', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

	print(dir(A))
	# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'f']

108.一个包里有三个模块,demo1.py, demo2.py, demo3.py,但使用 from tools import *导入模块时,如何保证只有 demo1、demo3 被导入了。

在Python包中新建__init__.py文件确认导入的包,文件内容如下:

from Demo1 import demo1
from Demo2 import demo2

109.列举 5 个 Python 中的异常类型以及其含义

KeyboardInterrupt:用户中断执行(一般是输入^C);
MoudleNotFoundError:无法找到模块或找到的是None
RecursionError:超过递归最大深度;
TimeoutError:函数在系统级别超时;
IndentationError:缩进错误;
SystemError:解释器发现内部错误;
TypeError:操作或函数发现类型错误的参数;
ValueError:操作或函数发现类型正确但值错误的参数;
KeyError:映射中没有这个键;

110.copy 和 deepcopy 的区别是什么?

  • copy:浅拷贝,仅拷贝对象本身,不拷贝其引用;浅拷贝不会为子对象创建地址空间,当拷贝后的对象的子对象发生改变后,原对象的子对象也改变。
  • deepcopy:深拷贝,拷贝对象和其引用的对象;深拷贝会为子对象创建新的地址空间,拷贝后的对象的子对象只是初始化成了和原对象的子对象同样的值,其修改不会相互影响。
import copy

a = [1, 2, [3, 4], 5]

cp = copy.copy(a)
deep_cp = copy.deepcopy(a)

a[0] = 0		# 修改对象
a[2][0] = 0		# 修改子对象

print(cp)		# [1, 2, [0, 4], 5]
print(deep_cp)	# [1, 2, [3, 4], 5]

111.代码中经常遇到的*args, **kwargs 含义及用法。

  • *args: 传入一个包含任意元素的位置参数元组,*用于解压缩元组;
  • **kwargs: 传入一个包含任意元素的关键字参数字典,**用于解压缩字典;
  • 位置参数必须位于关键字参数之前,用于定义时无法确定参数个数的函数。
def fun(*args, **kwargs):
	print(args)		# (1, 2, 3)
	print(kwargs)	# {'a': 4, 'b': 5}

fun(1, 2, 3, a = 4, b = 5)

112.Python 中会有函数或成员变量包含单下划线前缀和结尾,和双下划线前缀结尾,区别是什么?

  • 单下划线开头:通常用于模块, 模块内以单下划线开头的变量和方法默认划入模块内部范围。在使用from module import *的时候不会引入,在使用import module时会引入。
  • 单下划线结尾:单下划线结尾通常用于用户自定义变量或方法名与python内置关键字冲突时,加上一个下划线在结尾以示区别。不推荐这样,最好重新命名。
  • 双下划线开头结尾:python的魔术对象,比如用于面向对象的__new__, __init__, __del__,用于类的__class__, __call__,用于属性操作的__getattr__, __setattr__, __delattr__, 用于迭代器的__iter__, __next__,用于上下文管理器的__enter__, __exit__

113.w、a+、wb 文件写入模式的区别

r:读文件,文件不存在会报错;
w:写文件,文件不存在时新建文件,存在时会覆盖原数据;
a:追加数据,文件不存在时新建文件,存在时不会覆盖原数据;
rb,wb:同上,操作二进制文件;
r+:可读可写,文件不存在时报错;
w+:可读可写,文件不存在时新建文件,存在时会覆盖原数据;
a+:可读可追加,文件不存在时新建文件,存在时不会覆盖原数据;

114.举例 sort 和 sorted 的区别

  • sort是列表的方法,直接在原列表排序;
  • sorted是函数,不改变原可迭代对象,而是返回新的排序好的对象。

115.什么是负索引?

负索引是指使用负数作为索引,-a代表倒数第a位。

116.pprint 模块是干什么的?

pprint用于整齐美观的输出数据。

import pprint

a = [str(i) * 10 for i in range(10)]
pp_obj = pprint.PrettyPrinter(indent = 4)	# 句首缩进
pp_obj.pprint(a)	# 缩进+分行 输出
print(a)			# 一行输出

117.解释一下 Python 中的赋值运算符

=, +=, -=, *=, **=, /=, //=, %=

118.解释一下 Python 中的逻辑运算符

and, or, not

119.讲讲 Python 中的位运算符

&, |, ^(a^a=0, a^0=a), ~(~x = -x-1), <<, >>

120.在 Python 中如何使用多进制数字?

  • 二进制:'0b/0B’加上01串为二进制数,使用int()和bin()进行二进制与十进制之间的转换。
  • 八进制:'0o/0O’加上范围为1-7的字符串为八进制数,使用int()和oct()进行二进制与十进制之间的转换。
  • 十六进制:'0x/0X’加上范围为[1-9,A-F]的字符串为十六进制数,使用int()和hex()进行二进制与十进制之间的转换。
# 二进制
print(int(0b101))	# 5
print(bin(5))		# 0b101

# 八进制
print(int(0o12))	# 10
print(oct(11))		# 0o13

# 十六进制
print(int(0xFF))	# 255
print(hex(17))		# 0x11

121.怎样声明多个变量并赋值?

a, b = 1, 2

Python 简单算法题练习

122.已知:

(1) 从 AList 和 BSet 中 查找 4,最坏时间复杂度哪个大?

(2) 从 AList 和 BSet 中 插入 4,最坏时间复杂度哪个大?

  1. 查找:list由数组实现,get在平均和最坏情况下均为O(1);set(dict)由hash实现,get/in平均O(1)最坏O(n)。
  2. 插入:list插入时需要移动元素,且时间复杂度随元素增多而变大,平均和最坏均为O(n);set的插入和元素个数无关,平均O(1),最坏O(n)。

123.用 Python 实现一个二分查找的函数

二分查找元素在列表中的索引,若元素不在列表中则返回其应该插入的索引。

def bin_serach(nums, target):
	'''二分查找

	利用二分查找查找元素索引,若不存在则返回其应该插入的索引
	:param nums:目标列表
	:param target: 带查找数值
	:return: 元素索引/插入索引
	'''
	# corner case
	if not nums:
		return -1

	l, r = 0, len(nums)
	while l <= r:
		mid = (l + r) // 2
		if target < nums[mid]:
			r = mid - 1
		elif target > nums[mid]:
			l = mid + 1
		else:
			return mid
	return l


# # case0: -1
# # nums = []
# nums = None
# target = 4

# # case1: 2
# nums = [1, 3, 4, 6, 9]
# target = 4

# # case2: 3
# nums = [1, 3, 4, 6, 9]
# target = 5

# case3: 0
nums = [1, 3, 4, 6, 9]
target = 0

res = bin_serach(nums, target)
print(res)

124.python 单例模式的实现方法

补充:装饰器的基本概念

  1. 装饰器实现
def singleton(cls):
	_instance = {}

	def inner(*args, **kwargs):
		# 若全局都没有cls的实例则创建一个,否则返回这个实例
		if cls not in _instance:
			_instance[cls] = cls(*args, **kwargs)
		return _instance[cls]
	return inner

@singleton
class Demo:
	def __init__(self, val):
		self.val = val


if __name__ == '__main__':
	
	demo1 = Demo(1)
	print('demo1.val: ', demo1.val)		# demo1.val: 1

	demo2 = Demo(2)
	print('demo2.val: ', demo2.val)		# demo2.val: 1

	print(demo1 is demo2)				# True
  1. __new__实现
class Singleton(object):
	_instance = None

	def __new__(cls, *args, **kwargs):
		# 若全局都没有cls的实例则创建一个,否则返回这个实例
		if cls._instance is None:
			cls._instance = super().__new__(cls, *args, **kwargs)
		return cls._instance



if __name__ == '__main__':
	
	demo1 = Singleton()
	demo2 = Singleton()

	print(demo1 is demo2)		# True

125.使用 Python 实现一个斐波那契数列

补充:
生成器的基本概念
这个博主也很厉害
生成器的作用
实现一个斐波那契生成器:

def fib(n):
	if n == 0:
		yield 0

	prev2, prev1 = 0, 1
	for _ in range(n):
		prev2, prev1 = prev1, prev1 + prev2
		yield prev2


fib_iter = fib(0)		# generator实例化生成器 将需要的功能转化为可迭代性的工具并且有返回值
for i in fib_iter:
	print(i)

126.找出列表中的重复数字

def find_duplication(nums):
	'''找出数组中的重复数字

	找出数组中的重复数字
	:param nums:目标数组
	:return: 重复数字列表,没有重复数字则返回[]
	'''
	if not nums:#程序编写需要考察多种输入情况,因此不能忽视
		return []

	res = set()
	showed = set()
	for num in nums:
		if num in showed:
			res.add(num)
		else:
			showed.add(num)
	return list(res)


# # case0: []
# # nums = []
# nums = None

# # case1: 
# nums = [1, 2, 3]

# # case2:
# nums = [1, 1, 2, 3]

# case3:
#上述注释掉的部分是为了确保各种测试用例都能够通过程序编译
#在测试岗面试中,面试官在考察编程能力的同时其实还会看中思维逻辑的严密性
#因此,会考察你对测试用例的设计,确保情况覆盖率
nums = [1, 1, 2, 3, 3]

res = find_duplication(nums)
print(res)

使用collections.Counter():

if not nums:
	return []

cnt = dict(Counter(nums))
return [item[0] for item in cnt.items() if item[1] > 1]

127.找出列表中的单个数字

#使用字典{key:value}的结构去重且能够记数
cnt = dict(Counter(nums))
return [item[0] for item in cnt.items() if item[1] == 1]

十大排序算法

128.写一个冒泡排序

def buble_sort(nums):
	'''冒泡排序

	使用冒泡排序升序排列列表,依次将未排序列表的最大值冒泡的最后
	:param nums:目标数组
	:return: 排序后的列表
	'''
	if not nums:
		return []

#原代码有误,重新编写
	for i in range(0,len(nums)):
		for j in range(0,len(nums)-i-1):
		{
			if(nums[j]>nums[j+1])
				nums[j], nums[j+1]=nums[j+1], nums[j]
		}
	return nums


# # case0: []
# # nums = []
# nums = None

# case1: 
nums = [1, 4, 2, 3]

# # case2:
# nums = [1, 3, 1, 2, 3]


res = buble_sort(nums)
print(res)

129.写一个快速排序

辅助空间 + 递归: 平均时间复杂度O(nlogn),平均空间复杂度O(nlogn):

def quick_sort(nums):
	'''快速排序

	在序列中找出一个轴值,一轮排序后将小于轴值的数放到其左边,大于轴值的数放到其右边。
	然后在左右子序列中递归此项操作,直到子序列的元素个数小于等于1.
	:param nums:目标数组
	:return: 排序后的列表
	'''
	n = len(nums)
	if n <= 1:
		return nums

	pivot = nums[n // 2]#返回整数部分
	nums.remove(pivot)

	left, right = [], []
	for num in nums:
		if num <= pivot:
			left.append(num)
		else:
			right.append(num)
	return quick_sort(left) + [pivot] + quick_sort(right)
```#都不用遍历,找一个pivot就完了?因为 return 部分的表达式是可递归的结果

就地递归:平均时间复杂度O(nlogn),平均空间复杂度O(logn):
```python
def quick_sort(nums):
	'''快速排序

	在序列中找出一个轴值,一轮排序后将小于轴值的数放到其左边,大于轴值的数放到其右边。
	然后在左右子序列中递归此项操作,知道子序列的元素个数小于等于1.
	:param nums:目标数组
	:return: 排序后的列表
	'''
	n = len(nums)
	quick_sort_recursive(nums, 0, n - 1)


def quick_sort_recursive(nums, l, r):
	if l >= r:
		return
	index = partition(nums, l, r)
	quick_sort_recursive(nums, l, index - 1)
	quick_sort_recursive(nums, index + 1, r)


def partition(nums, l, r):
	pivot = nums[r]
	i = l - 1
	for j in range(l, r):
		if nums[j] <= pivot:
			i += 1
			nums[i], nums[j] = nums[j], nums[i]
	nums[i + 1], nums[r] = nums[r], nums[i +1]
	return i + 1

迭代【栈】:平均时间复杂度O(nlogn),平均空间复杂度O(logn):

def quick_sort(nums):
	'''快速排序

	在序列中找出一个轴值,一轮排序后将小于轴值的数放到其左边,大于轴值的数放到其右边。
	然后在左右子序列中递归此项操作,知道子序列的元素个数小于等于1.
	:param nums:目标数组
	:return: 排序后的列表
	'''
	n = len(nums)
	quick_sort_recursive(nums, 0, n - 1)


def quick_sort_recursive(nums, l, r):
	if l >= r:
		return
	stack = []
	stack.append(l)
	stack.append(r)

	while stack:
		r = stack.pop()
		l = stack.pop()

		if l >= r:
			continue

		pivot = nums[r]
		i = l - 1
		for j in range(l, r):
			if nums[j] <= pivot:
				i += 1
				nums[i], nums[j] = nums[j], nums[i]
		nums[i + 1], nums[r] = nums[r], nums[i + 1]
		stack.extend([l, i, i + 2, r])

130.写一个拓扑排序

拓扑排序一般用来解决有执行顺序约束的调度问题。例子参见leetcode207。

131.python 实现一个二进制计算。

计算机内存储二进制补码。

132.有一组“+”和“-”符号,要求将“+”排到左边,“-”排到右边,写出具体的实现方法。

def char_sort(s: str) -> str:
	if len(s) <= 1:
		return s
	return ''.join(s.split('+') + s.split('-'))

133.单链表反转

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head

        new_head, temp = None, None
        curr = head
        while curr:
            temp = curr.next
            curr.next = new_head
            new_head = curr
            curr = temp
        return new_head


head = ListNode(0)
head.next = ListNode(1)
head.next.next = ListNode(2)
head.next.next.next = ListNode(3)

new_head = reverse_linked_list(head)
while new_head:
	print(new_head.val)
	new_head = new_head.next

134.交叉链表求交点

拼接链表,顺序遍历:

class ListNode:
	def __init__(self, val):
		self.val = val
		self.next = None


def find_intersect(head1: ListNode, head2: ListNode) -> ListNode:
	p1, p2 = head1, head2
	while p1 and p2:
		if p1 == p2:
			return p1
		p1 = p1.next if p1.next else head2
		p2 = p2.next if p2.next else head1
	return None		# 没有交点


head = ListNode(0)
head.next = ListNode(1)
head.next.next = ListNode(2)		# 交点
head.next.next.next = ListNode(3)
joint = head.next.next
head2 = ListNode(9)
head2.next = joint

intersect = find_intersect(head, head2)
print(intersect.val if intersect)

135.用队列实现栈

使用队列实现栈的下列操作:

push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空

参考leetcode 225.

136.找出数据流的中位数

大小堆,参考leetcode 295.

137.二叉搜索树中第 K 小的元素

中序遍历,参考leetcode 230.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值