Python学到什么程度可以面试工作?

至少需要哪些知识?需要掌握到什么程度?
关注者
14,497
被浏览
2,508,217
登录后你可以
不限量看优质回答 私信答主深度交流 精彩内容一键收藏

本答案力求简洁和直击重点,代码部分使用Python3,更详细的解释请 Google ,回答有误请务必提醒答主,我将及时改正。内容较多,建议关注+收藏,我将随时更新。


  1. 列出 5 个常用 Python 标准库?
import os                  # 操作系统接口  示例:os.system('ls')
import sys                 # 命令行参数    示例:sys.path
import re                  # 正则模块      示例:re.match('www', 'www.zhihu.com')
import math                # 数学模块      示例:math.cos(math.pi / 3)
import time                # 日期模块      示例:time.sleep(3)
import random              # 随机数模块    示例:random.random()
import threading           # 线程模块      示例:threading.Thread(target=lambda a, b:a.append(b), args=([1, 2], 3))
import multiprocessing     # 进程模块      示例:multiprocessing.Process(target=method, args=([1, 2], 3))   # pickle模块不能序列化lambda,需要自定义函数


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

# 数值型--int、float、complex
# 布尔型--bool
# 字符串--str
# 列表--list
# 元组--tuple
# 字典--dict


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

  • with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。
  • with语句即“上下文管理器”,在程序中用来表示代码执行过程中所处的前后环境 上下文管理器:含有__enter__和__exit__方法的对象就是上下文管理器。
  • __enter__():在执行语句之前,首先执行该方法,通常返回一个实例对象,如果with语句有as目标,则将对象赋值给as目标。
  • __exit__():执行语句结束后,自动调用__exit__()方法,用户释放资源,若此方法返回布尔值True,程序会忽略异常。
  • 使用环境:文件读写、线程锁的自动释放等。


4. Python的可变和不可变数据类型?

# 不可变数据类型:即数据被创建之后,数据的值将不再发生改变,有数值、字符、元祖类型。
# 可变数据类型:数据别创建之后,数据的值可以发生变化,有列表、字典、集合类型。


5.Python 获取当前日期?

import datetime
print(datetime.datetime.now())


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

# coding:utf-8
def word_amount(sentence):
    """计算句子的单词数量
    计算句子中的单词数量,并返回结果字典
    :param sentence: 句子对象
    :return: 计算结果字典
    # 使用split()方法切割句子
    split_list = sentence.split()
    dict_result = {}
    # 遍历列表,判断每个值是否在新字典的键中,如果存在则键值加一,不存在设为1
    for word_name in split_list:
        if word_name not in dict_result.keys():
            dict_result[word_name] = 1
        else:
            dict_result[word_name] += 1
    return dict_result
if __name__ == '__main__':
    sentence = "I can because i think i can"
    dict_result = word_amount(sentence)
    print(dict_result)

评论区 @Felix 有更优的解法,非常感谢:

sentence = "I can because i think i can"
# count()用来统计字符串中word出现的次数,count(sub, start=None, end=None)有三个参数,依次是待统计字符,字符索引开头,索引结尾
result = {word: sentence.split().count(word) for word in set(sentence.split())}
print(result)

评论区 @Carno 还有更优解,万分感谢!

from collections import Counter
sentence = "I can because i think i can"
# Counter是一个简单的计数器,可以数组中统计字符出现的个数:
counts = Counter(sentence.split())
print(counts)  # Counter({'can': 2, 'i': 2, 'I': 1, 'because': 1, 'think': 1})

collections是一个很棒的内建模块,可以参考廖雪峰教程

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

# Python方法
import os
os.remove("demo.txt")
# linux方法
rm demo.txt


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

# coding:utf-8
def judge_value(num_value):
    """自定义异常函数
    自定义异常函数,用于抛出大于一定值的异常
    :param num_value:用于判断的值
    :return:异常信息
    if num_value > 10:
        # raise用于抛出自定义异常,格式为:raise 异常类型(异常注明)
        # 一旦触发则不再执行raise之后的代码
        raise ValueError("数量不能大于10")
    else:
        return "200"
if __name__ == '__main__':
    judge_value(10)


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

# coding:utf-8
def read_filedata(file_name):
    """读取文件数据
     读取指定文件中的所有数据,返回数据或者异常信息
    :param file_name: 文件路径
    :return: 文件数据或者异常信息
    file_obj = ""
    try:
        # 需要检测的异常代码片段
        file_obj = open(file_name, "r")
        result_data = file_obj.read()
    except IOError, e:
        # 发生“IOError”异常进行处理的代码片段
        file_obj = "文件不存在:"+str(e)
    else:
        # 没有引发“IOError”异常执行的代码片段
        # 返回读取到的数据
        return result_data
    finally:
        # 不管有没有引发错误都会执行的代码片段
        # isinstance()用于判断一个数据类型
        if isinstance(file_obj, str):
            return file_obj
        elif isinstance(file_obj, file):
            file_obj.close()
        else:
            return "未知错误,请检查您的代码..."
if __name__ == '__main__':
    result = read_filedata("abc.txt")
    print(result)


10. 遇到 bug 如何处理?

  • 首先查看报错信息,根据报错信息找到相应代码,通常一般的数据结构或者算法错误只要找到报错代码就可以顺利解决。
  • 如果遇到暂时不能解决的错误先不要慌,我们可以使用编译器的Debug模式或者自己在代码中加注断点进行代码排查。
  • 如果依然不能解决bug,我们可以拷贝报错信息,在搜索引擎中进行搜索。
  • 没有人写代码不出bug,如果你在一个bug上耗费时间超过半小时,可以与其他同事探讨(注意节制,可能有些费同事)。
  • 另辟蹊径:方法总比困难多,在进行快速开发时,我们应该优先实现功能而不是拘泥于运行效率,所以遇到一些暂时不能解决的BUG可以考虑另外的实现方法。

语言特性

  1. 谈谈对 Python 的了解和其他语言的区别?
Python是一门语法简洁优美,功能强大无比,应用领域非常广泛,具有强大完备的第三方库的一门 强类型 的动态,可移植,可扩展,可嵌入的解释型编程语言

经评论区 @Corbin Zhou 提醒,Python为强类型语言,这一点其实一直有争议,还有人说Python之父说过是“弱类型”,这里进行一个补充,回答时只要合理解释即可:

如果语言经常隐式地转换变量的类型,那这个语言就是 弱类型语言 ,如果很少会这样做,那就是 强类型语言
Python很少会隐式地转换变量的类型,所以Python是强类型的语言。

解释性 :解释型语言使用解释器将源码逐行解释成机器码并立即执行,不会进行整体性的编译和链接处理,相当于把编译语言中的编译和解释混合到一起同时完成。

优点:跨平台容易,只需提供特定平台的解释器;

缺点:运行效率较低,因为每次执行相当于都要进行一次编译。

编译型语言和解释型语言

简洁优雅 : 省略了各种大括号和分号,还有一些关键字,类型说明(自省);

面向对象 :Python和C++、Java一样都是面向对象编程语言;

跨平台 :简单搭建Python解释器可以在大部分平台运行。

Python和Java相比:

Python是动态类型语言,而Java是静态类型语言.
动态类型语言不需要事先声明变量的类型,而且变量的数据类型可以被修改
静态类型语言需要事先声明,并且不能修改;
Python和C相比:
对于使用:
Python的类库齐全并且使用简洁,很少代码实现的功能用C可能要很复杂
对于速度:
Python的运行速度相较于C,绝对是很慢了。Python的CPython解释器是C语言编写的。


2. 简述解释型和编译型编程语言?

请看上一条


3.Python 的解释器种类以及相关特点?

摘自 廖雪峰 教程
CPython
当我们从 Python官方网站 下载并安装好Python 3.x后,我们就直接获得了一个官方版本的解释器:CPython。这个解释器是用C语言开发的,所以叫CPython。在命令行下运行python就是启动CPython解释器。
CPython是使用最广的Python解释器。

IPython
IPython是基于CPython之上的一个交互式解释器,也就是说,IPython只是在交互方式上有所增强,但是执行Python代码的功能和CPython是完全一样的。好比很多国产浏览器虽然外观不同,但内核其实都是调用了IE。
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的字节码。


4. 说说你知道的Python3 和 Python2 之间的区别?

编码

Python2 中字符的类型:
str: 已经编码后的字节序列
unicode: 编码前的文本字符

Python3 中字符的类型:
str: 编码过的 unicode 文本字符
bytes: 编码前的字节序列

我们可以认为字符串有两种状态,即文本状态和字节(二进制)状态。Python2 和 Python3 中的两种字符类型都分别对应这两种状态,然后相互之间进行编解码转化。 编码就是将字符串转换成字节码,涉及到字符串的内部表示;解码就是将字节码转换为字符串,将比特位显示成字符。
在 Python2 中,str 和 unicode 都有 encode 和 decode 方法。但是 不建议对 str 使用 encode,对 unicode 使用 decode , 这是 Python2 设计上的缺陷。Python3 则进行了优化,str 只有一个 encode 方法将字符串转化为一个字节码,而且 bytes 也只有一个 decode 方法将字节码转化为一个文本字符串。

Python2中需要在文件头打上注释 # coding:utf-8 指定该程序使用的编码格式为UTF-8

print

Python2中的print是class

Python3中的print是函数

* Python 2 的 print 声明已经被 print() 函数取代了,这意味着我们必须包装我们想打印在小括号中的对象。

所以我们输出格式为

print("")    # py3
print ""     # py2
print("")

input

Python3:input 解析输入为str字符型
Python2: input 解析输入为int型,raw_input解析输入为 str 类型

算术符

Python3中/表示真除,%表示取余,//结果取整;Python2中带上小数点/表示真除,%表示取余,//结果取整

xrange

Python2中使用xrange()来创建一个迭代器对象,使用range()创建一个list数组;
Python3中使用range()创建迭代器对象,移除了xrange()方法。


5. Python3 和 Python2 中 int 和 long 区别?

Python 3里,只有一种整数类型 int,大多数情况下,它很像Python2里的长整型。
Python 2有为非浮点数准备的 int 和 long 类型。 int 类型的最大值不能超过 sys.maxint,而且这个最大值是平台相关的。


6. xrange 和 range 的区别?

Python2中使用xrange()来创建一个迭代器对象,使用range()创建一个list数组;
Python3中使用range()创建迭代器对象,移除了xrange()方法。

编码规范

7. 什么是 PEP8?

PEP是 Python Enhancement Proposal 的缩写,翻译过来就是 Python增强建议书
PEP8 ,简单说就是一种编码规范,是为了让代码“更好看”,更容易被阅读。


8. 了解 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 以编写优美的代码为目标)
明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
可读性很重要(优美的代码是可读的)
即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)
不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except:pass 风格的代码)
当存在多种可能,不要尝试去猜测
而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)
虽然这并不容易,因为你不是 Python 之父(这里的 Dutch 是指 Guido )
做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量)
如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)
命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)


9.了解 docstring 么?

DocStrings 文档字符串是一个重要工具,用于 解释文档程序 ,帮助你的程序文档更加简单易懂。
我们可以在函数体的第一行使用一对三个单引号 ''' 或者一对三个双引号 """ 来定义文档字符串。
你可以使用 __doc__ (注意双下划线)调用函数中的文档字符串属性。
DocStrings 文档字符串使用惯例:它的首行简述函数功能,第二行空行,第三行为函数的具体描述。
# coding:utf-8
def create_iterator(list_param):
    """创建迭代器
    使用生成器推导式创建一个迭代器,并返回迭代器
    :param list_parm: 迭代对象
    :return: 迭代器
    # 将列表推导式的“[]”改为“()”即为生成器推导式,众所周知,生成器返回一个迭代器对象
    return (value for value in list_param)
if __name__ == '__main__':
    # 遍历迭代器
    for i in create_iterator([1, 2, 3]):
        print(i)
    # 使用__doc__输出函数中的文档字符串属性
    print(create_iterator.__doc__)
    # 使用__dir__输出函数中的所有属性和方法
    print(create_iterator.__dir__())
补充
类的函数称为方法(method),模块里的函数称为函数(function);
每一个包,模块,类,函数,方法都应该包含文档,包括类的__init__方法;
包的文档写在__init__.py文件中;
文档有单行文档和多行文档;
单行文档:
不要重复函数的声明语句,例如:function(a, b) -> list;
指明做什么和返回什么,例如Do X and return a list.;
使用三引号,方便换行;

多行文档:
如果模块是一个脚本,也就是单文件程序,模块的文档应该写明脚本的使用方法;
模块的文档需要写明包含的类,异常,函数;
如果是包,在__init__.py中,写明包里面包含的模块,子包;
如果是函数或类方法,应该写明函数或方法的作用,参数,返回,副作用,异常和调用的限制等;
如果是类,写明类的行为,和实例参数,构造方法写在__init__中;
使用三引号,而且两个三引号都应该单独成行。


10. 了解类型注解么?

首先,Python是一种 动态语言 ,变量和函数的参数是不区分类型的。

Python解释器会在运行的时候动态判断变量和参数的类型,这样的好处是编写代码速度很快,很灵活,但是坏处也很明显,不好维护,可能代码写过一段时间重新看就很难理解了,因为那些变量、参数、函数返回值的类型,全都给忘记了。

在阅读别人的代码时,无法看出变量或参数的类型,这样对工作效率带来很大影响。

因此,在Python3中新添加了“类型注解”特性,可以给参数、函数返回值和变量的类型加上注解,该注解仅仅是 注释 而已,对代码运行不会产生任何影响,真正的变量类型还是由Python解释器决定,你所做的只是提高代码可读性,并不会像静态语言中变量类型定义后就无法修改(强转除外)。

上码:

# coding:utf-8
def list_to_str(param_list: list, connect_str: str=" ") -> str:
    """列表转字符串
    使用join方法将列表转为字符串并返回
    :param param_list: 列表
    :param connect_str: 需要插入的字符,默认为一个空格
    :return:转换成功的字符串
    demo_tuple: tuple = (1, 2)
    demo_dict: dict = {"1": 1}
    return connect_str.join(param_list)
if __name__ == '__main__':
    result = list_to_str(["Hello", "world"])
    print(result)

以上代码可以看出,一般变量和函数参数注解格式为“参数:类型”,默认参数是在类型的后面加“=默认值”,函数的返回值注解格式为“-> 类型:”,函数的冒号在注解后方。

类型注解仅仅起到了注释作用,那我们应该如何知道它的正确性呢?

Python提供了一个工具方便我们测试类型注解的正确性

pip install mypy

使用方法:

mypy demo.py

若无错误则无输出,反之会输出如下:

D:\code\web\flaskweb>mypy demo.py
demo.py:12: error: Incompatible return value type (got "str", expected "int")


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

变量命名:字母数字下划线,不能以数字开头

单下划线开头变量
单下划线开头的变量标明是一个受保护(protected)的变量,原则上不允许直接访问,但外部类还是可以访问到这个变量。
这只是程序员之间的一个约定,用于警告说明这是一个私有变量,外部类不要去访问它。
双下划线开头变量
双下划线开头的,表示的是私有类型(private)的变量。 只能是允许这个类本身进行访问了, 连子类也不可以.
以双下划线开头,并且以双下划线结尾的,是内置变量.
内置变量是可以直接访问的,不是 private 变量,如__init__,__import__或是__file__。
★不要自己定义内置变量
xxx_,单下划线结尾的变量一般只是为了避免与 Python 关键字的命名冲突
USER_CONSTANT,大写加下划线,对于不会发生改变的全局变量,使用大写加下划线

函数和方法(类中叫做方法,模块中称作函数)命名:

总体而言应该使用小写和下划线,如: create_user():
私有方法 : 小写和一个前导下划线,如 def _create_user(self):
私有方法和私有变量一样,并不是真正的私有访问权限。
一般函数不要使用两个前导下划线(当遇到两个前导下划线时,Python 的名称改编特性将发挥作用)。
特殊方法 : 小写和两个前导下划线,两个后置下划线 def __ init_ _(self):
这种风格只应用于特殊函数,比如操作符重载等。
函数参数 : 小写和下划线,缺省值等号两边无空格 def __init__(self, param=None):
不要滥用 *args 和 **kwargs,可能会破坏函数的健壮性

类命名:

类总是使用驼峰格式命名,即所有单词首字母大写其余字母小写/
如:Class CreateUser():
类名应该简明,精确,并足以从中理解类所完成的工作。
常见的一个方法是使用表示其类型或者特性的后缀,例如:SQLEngine ,MimeTypes
对于基类而言,可以使用一个 Base 或者 Abstract 前缀

包和模块:

小写字母、数字和下划线


12.Python 中的注释有几种?

单行注释以“#”开头

# 单行注释

多行注释使用三个单引号或者双引号

"""
双引号多行注释
单引号多行注释
'''

单引号和双引号混用,使用一种引号后其注释中的所有引号应该使用另一种

"""
'双引号'中使用'单引号'
"单引号"中使用"双引号"
'''


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

见第9条


14. 如何给变量加注释?

变量注释使用行内注释,根据pep8规范应该在代码后至少有两个空格,注释由#和一个空格开始:

user_name = "Robin"    # 用户姓名
user_age = 26          # 用户年龄
user_gender = 1        # 用户性别,男为1,女为0

有代码洁癖的同学可以保持注释的对齐,但一行文本不宜超过79个字符(PEP8规范)。


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

Python是一门用空格缩进来区分代码层次的语言,其实Python并没有强制要求你用Tab缩进或者用空格缩进,甚至空格按几个都没有强制要求(但在PEP8中建议了使用4个空格作为缩进)
但是却绝对!绝对不能混用Tab和空格
python中不提倡使用tab缩进
不同编辑器对于TAB的解释是不同的,有的编辑器tab是4个字符宽,有的8个字符宽。
如果有的地方用TAB,有的地方用空格,在不同的地方,原本对齐的代码就可能会不对齐。


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

可以在一巨import中打入多个库,但是一般我们不这样做,原因有以下几点

更易于阅读
更易于搜索
更易于编辑
多行import更易于维护

另,导入多个模块语句最好以下面方式书写,使用空行将其分割

  • python 标准库模块
  • python 第三方模块
  • 自定义模块

有的程序员喜欢这样导入模块

from socker import *

这样写的好处就是不需要我们一个个列出“socket”需要的方法,但是这样引入的弊端如下

  • 占用更多的内存空间,不必要的方法或类可能会进行初始化
  • 代码可读性差,模块内部突然冒出一个没有见过也没有归属的方法,很是头疼


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

在为Python文件命名时,我们需要注意

  • 不能与Python中的关键字命名;
  • 不能以标准库或常用第三方库命名,除非你想重写这些库;
  • 不能用除字母数字下换线之外的字符命名,注意不要使用中文命名任何路径和可执行文件;
  • 数字不能作为开头。


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

Pylint
安装: pip install pylint
使用:pylint demo.py
Black
安装: pip install black
使用:black demo.py
Autopep8
安装: pip install autopep8
使用:autopep8 --in-place --aggressive demo.py

经评论区 @百万光年 提醒,还有flake8:

flake8 安装:
pip install flake8
使用:flake8 demo.py

个人使用最多的是Autopep8。


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

# 数值型--int、float、complex
# 布尔型--bool
# 字符串--str
# 列表--list
# 元组--tuple
# 字典--dict


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

可变数据类型:在内存id不变的情况下,数据的值可以改变

不可变数据类型:数据的值不能发生改变,如果值发生改变,那么内存id也会改变,这样就不是同一个数据了。

demo_list = [1, 2, 3]
print(id(demo_list))        # 4874760
demo_list.append(4)
print(id(demo_list))        # 4874760
demo_tuple = (1, 2, 3)
print(id(demo_tuple))       # 65021344
# demo_tuple.append(4)      # 不可变类型不能对值进行修改
demo_tuple = (1, 2, 3, 4)   # 重新对变量赋值,变量和数据类型需要区分,变量!=数据,变量只是数据的载体
print(id(demo_tuple))       # 42116344


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

#!/usr/bin/python3
# coding:utf-8
def first_capital(change_sentence: str)->str:
    """句子所有单词首字母大写
    将句子的首字母大写并返回
    :param change_sentence: 需要转换的句子
    :return: 返回转换后的字符串
    # 将句子使用split切割为list数组
    split_list = change_sentence.split()
    # 遍历列表长度值
    for i in range(len(split_list)):
        # 使用capitalize()函数将每个单词首字母转为大写
        split_list[i] = split_list[i].capitalize()
        # 也可以使用 upper() 方法,upper()可以把所有的小写字母转为大写,lower()是转小写
        # split_list[i] = split_list[i][0].upper() + split_list[i][1:]
    # 使用join将列表转为字符串
    split_list = " ".join(split_list)
    return split_list
if __name__ == '__main__':
    change_sentence = "hello world"
    print(first_capital("hello world"))
    # 有需要的同学可以使用匿名函数、列表推导式和map方法一行写出以上代码
    print(" ".join(list(map(lambda word: word.capitalize(), change_sentence.split()))))


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

isdigit() 方法检测字符串是否只由数字组成。

#!/usr/bin/python3
demo_str = "123456"
print(demo_str.isdigit())                       # 输出True
demo_str = "this is string example....wow!!!"
print(demo_str.isdigit())                       # 输出False


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

#!/usr/bin/python3
from functools import reduce
# 第一种方法,使用字符串切片
demo_str = "ilovechina"
print(demo_str[::-1])
# 第二种方法,使用列表的reverse方法
list_str = list(demo_str)
list_str.reverse()
print("".join(list_str))
# 第三种方法:reduce累加方法
# 具体步骤是将前两个字母初始化添加到lambda函数中,得到的结果在与下一个字母做累加直到结束
# 第一次:l + i = li
# 第二次:o + li = oli
# 第三次:v + oli = voli
# ...
# 第九次:a + nihcevoli = anihcevoli
print(reduce(lambda x, y: y+x, demo_str))
# 使用栈,先进后出
def stack_demo(demo_str):
    # 模拟全部入栈
    list_stack = list(demo_str)
    result_str = ""
    while len(list_stack) > 0:
        # 模拟出栈
        result_str += list_stack.pop()
    return result_str
print(stack_demo(demo_str))
# 遍历循环:略


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

#!/usr/bin/python3
demo_str = " adabdw "
# 去除两端空格使用:strip()
print(demo_str.strip())
# 去除右端空格使用:rstrip()
print(demo_str.rstrip())
# 去除左端空格使用:lstrip()
print(demo_str.lstrip())
# replace替换
print(demo_str.replace(" ", ""))
import re
print(re.sub(" ", "", demo_str))


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

#!/usr/bin/python3
demo_str = "123456"
print(demo_str[-2:])


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

#!/usr/bin/python3
import chardet
demo_str = "demo string".encode("gbk")
demo_str = demo_str.decode('gbk').encode('utf-8')
print(demo_str)
# chardet.detect()可以检测编码格式
print(chardet.detect(demo_str))

28. (1)s="info:xiaoZhang 33 shandong",用正则切分字符串输出['info', 'xiaoZhang', '33', 'shandong']

#!/usr/bin/python3
import re
demo_str = "info:xiaoZhang 33 shandong"
# compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象
# 由题可知需要去除出字母数字之外的字符,故使用"\W"做匹配
pattern = re.compile(r'\W')
# re.split()方法按照能够匹配的子串将字符串分割后返回列表
print(pattern.split(demo_str))

(2) a = "你好 中国 ",去除多余空格只留一个空格。

#!/usr/bin/python3
import re
a = "你好 中国 "
print(a.rstrip())


29. (1)怎样将字符串转换为小写

#!/usr/bin/python3
import re
demo_str = "HELLO WORLD"
# 转小写使用lower() 转大写使用upper()
print(demo_str.lower())

(2)单引号、双引号、三引号的区别?

在Python中我们都知道单引号和双引号都可以用来表示一个字符串,比如

str1 = 'python'
str2 = "python" 

str1和str2是没有任何区别的。但是如果遇到需要转义字符的情况,来看单引号和双引号的版本。

单引号版本:

str3 = 'We all know that \'A\' and \'B\' are two capital letters.'

双引号版本:

str4 = "We all know that 'A' and 'B' are two capital letters."

单引号需要加 '\' 来让编译器判断目前是转义字符,而双引号方便了很多。

反之,如果字符串中有双引号,为了避免使用转义符,可以使用单引号来定义这个字符串。

str5 = 'The teacher said: "Practice makes perfect" is a very famous proverb.'

三个单引号和三个双引号一般用于多行注释,也可以用来表示字符串:输出多行文本

str1 = """List of name:
Hello
World
print(str1)
# 使用三引号定于多行字符类型比使用双引号要友好许多


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

#!/usr/bin/python3
import pandas
# 第一种方法,使用set集合,先转为集合再转回列表
AList = [1, 2, 3, 1, 2]
result_list = list(set(AList))
print(result_list)
# 第二种,使用dict.fromkeys,该函数有两个参数,第一个是字典的键,第二个是对应值(默认为空str),用于创建一个字典类型
AList = [1, 2, 3, 1, 2]
result_list = list(dict.fromkeys(AList))
print(result_list)
# 第三种,遍历列表进行判断
AList = [1, 2, 3, 1, 2]
result_list = []
for i in AList:
    if i not in result_list:
        result_list.append(i)
    else:
        continue
print(result_list)
# 第四种,使用pandas.unique()方法,
AList = [1, 2, 3, 1, 2]
result_list = pandas.unique(AList).tolist()
print(result_list)


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

#!/usr/bin/python3
demo_str = "1,2,3"
result_list = demo_str.split(",")
print(result_list)


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

#!/usr/bin/python3
list_A = [1, 2, 3, 4, 5]
list_B = [4, 5, 6, 7, 8]
# 找相同
same_list = [i for i in list_A if i in list_B]
print(same_list)
# 找不同
different_list = [i for i in list_A if i not in list_B]
print(different_list)


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

#!/usr/bin/python3
question_list = [[1, 2], [3, 4], [5, 6]]
# 使用列表推导式嵌套的时候,注意前后的调用关系,前推导式的值需要在后面书写才能生效
# 需要输出的值放到推导式的最前面,生成输出值的推导式在最后面
print([list_int for inside_list in question_list for list_int in inside_list])


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

#!/usr/bin/python3
list_a = [1, 5, 7, 9]
list_b = [2, 2, 6, 8]
# 第一种方法:使用运算符“+”
combine_list = list_a + list_b
print(combine_list)
# 第二种方法:使用运算符extend()方法
list_a.extend(list_b)
print(list_a)
# 第三种方法:使用append
list_a = [1, 5, 7, 9]
for i in list_b:
    list_a.append(i)
print(list_a)


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

#!/usr/bin/python3
import random
demo_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
# 使用random.shuffle打乱一个list数组
random.shuffle(demo_list)
print(demo_list)


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

#!/usr/bin/python3
demo_dic = {"a": 1, "b": 2, "c": 3}
# pop方法删除指定的键值对,并返回删除的值
pop_str = demo_dic.pop("a")
print(demo_dic)
print(pop_str)
# del不会返回相应的值,只是将其删除
del demo_dic["b"]
print(demo_dic)


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

#!/usr/bin/python3
d1 = [
    {'name': 'alice', 'age': 38},
    {'name': 'bob', 'age': 18},
    {'name': 'ctrl', 'age': 28}
# 强大的sort方法,满足大多数排序算法,列表排序优先考虑sort!
list.sort( key=None, reverse=False)
key -- 主要是用来进行比较的元素,只有一个参数,
具体的函数的参数就是取自于可迭代对象中,指定可
迭代对象中的一个元素来进行排序。
reverse -- 排序规则,reverse = True 降序, reverse = False 升序(默认)。
d1.sort(key=lambda x: x['age'])
print(d1)


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

#!/usr/bin/python3
a = {"A": 1, "B": 2}
b = {"C": 3, "D": 4}
# 使用update方法
a.update(b)
print(a)
# 补充clear方法,清空字典中的键值对
a.clear()
print(a)


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

#!/usr/bin/python3
# 生成一个以姓名为键的字典,值为"student"
student_dic = {name: "student" for name in ["Robin", "Bob", "Jams"]}
print(student_dic)   # {'Robin': 'student', 'Bob': 'student', 'Jams': 'student'}  
# 将一个字典中的姓名首字母转为大写
name_dic = {1: "robin", 2: "bob", 3: "jams"}
# items() 以列表返回可遍历的(键, 值) 元组数组。
# dict_items([(1, 'robin'), (2, 'bob'), (3, 'jams')])
result_dic = {k: v.capitalize() for k, v in name_dic.items()}
print(result_dic)    # {1: 'Robin', 2: 'Bob', 3: 'Jams'}


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

#!/usr/bin/python3
tuple_key = ("a", "b")
tuple_val = (1, 2)
result_dic = dict(zip(tuple_key,tuple_val))
print(result_dic)


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

字典中的键是不可变类型,可变类型list和dict不能作为字典键
一个对象能不能作为字典的key,就取决于其有没有__hash__方法


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

#!/usr/bin/python3
demo_dic = {"A": 1, "B": 2}
# 使用字典推导式交换位置
result_dic = {v: k for k, v in demo_dic.items()}
print(result_dic)


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

#!/usr/bin/python3
demo_list = [1,2,3]
# 列表转元祖使用tuple()
result_tup = tuple(demo_list)
print(type(result_tup))             # <class 'tuple'>
print(result_tup)                   # (1, 2, 3)
# 元祖转列表使用list()
result_list = list(result_tup)  
print(type(result_list))            # <class 'list'>
print(result_list)                  # [1, 2, 3]


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

#!/usr/bin/python3
import itertools
def fbnq(num):
    """斐波那契生成器
    :param num: 生产数量
    :return: 斐波那契迭代器
    a, b = 1, 1
    for _ in range(num):
        a, b = b, a+b
        yield a
if __name__ == '__main__':
    gener = fbnq(20)
    print(gener)
    # 不能直接对生成器和迭代器进行切片
    # print(fbnq(20)[2])
    # 可以使用itertools.islice()对迭代器进行切片
    # itertools是一个很强大的内置模块,有需要可以了解一下
    gener_clip = itertools.islice(gener, 10, 20)
    for i in gener_clip:
        print(i)


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

(i for i in range(3))    # 方括号改为尖括号即可


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

#!/usr/bin/python3
a.encode()
b.encode()


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

a = (1, 2, 3, [4, 5, 6, 7], 8)
a[2] = 2             # TypeError: 'tuple' object does not support item assignment
  • 元祖是不可变类型,因此不能修改元祖内的值
  • a[2]=2 使得元祖中对索引值为“2”的元素进行了修改, 内存id 发生了变化


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

a = (1, 2, 3, [4, 5, 6, 7], 8)
a[3][0] = 2               # (1, 2, 3, [2, 5, 6, 7], 8)
  • 列表是可变数据类型,数据的值可以修改的
  • 这里只是修改了元祖子对象的值,而不是修改了元祖的值
  • 修改可变类型的值不会改变内存id,因此元祖的引用还是没有发生变化
  • 可以这么理解,只要不修改元祖中值的 内存id ,那么就可以进行“修改元祖”操作
  • 扩展,面试官可能会问到:元祖是否可以被修改?
  • 答:元祖是不可变数据类型,因此不能修改元祖中的值,但是如果元组中有可变数据类型,那么可以修改可变数据类型中的值,修改可变数据类型的值并不会使其内存id发生变化,所以元祖中元素中的内存id也没有改变,因此就做到了“修改元祖”操作。


49. Python 交换两个变量的值

a, b = b, a


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

read:读取整个文件。
readline:读取下一行,使用生成器方法。
readlines:读取整个文件到一个迭代器以供我们遍历


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

  • json序列化时,可以处理列表、字典、字符、数值、布尔和None
  • 定制datetime类型↓
#!/usr/bin/python3
from datetime import datetime
import json
from json import JSONEncoder
class DatetimeEncoder(JSONEncoder):
    """扩展JSONEncoder类中的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': 'alex', 'data': datetime.now()}
    print(json.dumps(dict_demo, cls=DatetimeEncoder))


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

#!/usr/bin/python3
import json
dict_demo = {"name": "旭东"}
# 使用dumps的默认参数ensure_ascii
print(json.dumps(dict_demo, ensure_ascii=False))


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

#!/usr/bin/python3
def read_file(file_name):
    """读文件
    读取文件并返回文件数据
    :param file_name: 文件名
    :return: 文件的所有数据
    with open(file_name, "r") as F:
        return F.read()
def write_filw(file_name, file_data):
    """写文件
    将数据写入到指定文件中
    :param file_name: 文件名
    :param file_data: 需要写入的数据
    :return:
    with open(file_name, "w") as F:
        F.write(file_data)
def letter_sort(letter_str, reverse_flag=False):
    """字母排序
    使用sorted排序算法
    :param letter_str: 排序字母字符串
    :param reverse_flag: 排序顺序,False为正序,True为反序
    :return: 排序后的新字符串
    return "".join(sorted(letter_str, reverse=reverse_flag))
if __name__ == '__main__':
    test1_data = read_file("test1.txt")
    test2_data = read_file("test2.txt")
    new_data = letter_sort(test1_data + test2_data)
    write_filw("new.txt", new_data)


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

#!/usr/bin/python3
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__':
    result_day = date_calculation("20190918", 30)
    print(result_day)


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

  • 闭包是一种特殊的函数,这种函数由多个函数的嵌套组成,且称之为外函数和内函数,外函数返回值是内函数的引用,此时就构成了闭包;
  • 闭包函数必须返回一个函数对象;
  • 闭包函数返回的那个函数必须引用外部变量;
  • 闭包可以保存运行环境,即在闭包内的变量是不能被轻易修改的;
  • 闭包的好处:提高代码的可复用性。
#!/usr/bin/python3
def out_func(n):
    """闭包函数
    :param n: 整数参数n
    :return: 内层函数
    def in_func(num):
        return n*num
    return in_func
if __name__ == '__main__':
    demo = out_func(3)
    print(demo(4))


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

def strappend(num):        # 函数作用、参数意义不明,需要加注释
    str='frist'            # 不能使用关键字"str"作为变量名
    for i in range(num):   # 遍历得到的元素"i"意义不明,无注释
        str+=str(i)        # 变量名和关键字在这个时候重名,必定报错,没有了str()方法
    return str
# 修改后
def str_append(append_cound: int) -> str:
    """字符串修改
    遍历append_cound,将遍历的值转为str类型并添加到字符串中
    :param append_cound: 遍历次数
    :return: 最终修改得到的新字符串
    append_str = "frist"
    # 遍历获取到“times”次数int类型
    for times in range(append_cound):
        append_str += str(times)
    return append_str
if __name__ == '__main__':
    print(str_append(4))


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

print([num for num in range(1,101) if num % 2 == 0])


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

with语句:“上下文管理器”,用于资源访问的场合,作用是资源释放和异常处理(详细内容在第3条问题汇总)

import threading
# 来一个用于线程锁的with使用
num = 0  # 全局变量多个线程可以读写,传递数据
thread_lock = threading.Lock()  # 创建一个锁
class Mythread(threading.Thread):
    def run(self):
        global num
        with thread_lock:               # with Lock的作用相当于自动获取和释放锁(资源)
            for i in range(1000000):    # 锁定期间,其他线程不可以运行
                num += 1
        print(num)


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

#!/usr/bin/python3
import json
dict_demo = {"a": 1, "b": 2}
# 序列化:使用json.dumps()将python类型转为json字符串
json_demo = json.dumps(dict_demo)
print(type(json_demo))
# 使用json.dump()将python数据序列化到指定文件中
with open("demo.json", "w") as file_obj:
    json.dump(dict_demo, file_obj)
# 反序列化:使用json.loads()将json字符类型转为python类型
dict_demo = json.loads(json_demo)
print(type(dict_demo))
# 使用json.load()将json字符类型从文件中读出来
with open("demo.json", "r") as file_obj:
    file_date = json.load(file_obj)
    print(file_date)


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

#!/usr/bin/python3
import re
def capital_count(file_name):
    """计算文件中的大写字母数量
    读取文件并计算文件数据的大写字母数量,返回大写字母数量
    :param file_name: 文件名
    :return: 文件中的大写字母数量
    # 定义大写字母数量变量
    upper_count = 0
    # 打开文件对象,读取文件数据
    with open(file_name, "r") as file_obj:
        file_data = file_obj.read()
    # 删除掉除字母之外的所有字符
    file_data = re.sub("[^a-zA-Z]", "", file_data)
    print(file_data)
    # 遍历所有字母,使用isupper()判断是否是大写字母,并且计数
    for i in file_data:
        if i.isupper():
            upper_count += 1
    return upper_count
if __name__ == '__main__':
    print(capital_count("test1.txt"))
.


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

#!/usr/bin/python3
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(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。


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

#!/usr/bin/python3
import redis
# 创建连接对象
connec_obj = redis.Redis(host='localhost', port=6379, db=0)
# 设置一个键值
connec_obj.set('test', '1')
# 读取一个键值
connec_obj.get('test')   # ->> '1'


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

#!/usr/bin/python3
import pymysql
# 打开数据库连接
db = pymysql.connect("localhost", "testuser", "test123", "TESTDB", charset='utf8' )
# 使用cursor()方法获取操作游标
cursor = db.cursor()
# 使用execute方法执行SQL语句
cursor.execute("SELECT VERSION()")
# 使用 fetchone() 方法获取一条数据
data = cursor.fetchone()
# 关闭数据库连接
db.close()


65. 了解 Redis 的事务么?

事务提供了一种"将多个命令打包,一次性提交并按顺序执行"的机制,提交后在事务执行中不会中断。只有在执行完所有命令后才会继续执行来自其他客户的消息。
Redis通过multi,exec,discard,watch实现事务功能。
1. multi:开始事务
2. exec:提交事务并执行
3. discard:取消事务
4. watch:事务开始之前监视任意数量的键
5. unwatch:取消WATCH命令对多有key的监控,所有监控锁将会被取消。
关于ACID:
1. 单独的隔离操作:事务中的所有命令会被序列化、按顺序执行,在执行的过程中不会被其他客户端发送来的命令打断
2. 没有隔离级别的概念:队列中的命令在事务没有被提交之前不会被实际执行
3. 不保证原子性:redis中的一个事务中如果存在命令执行失败,那么其他命令依然会被执行,没有回滚机制。


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

通俗解释
属性不可分割 :字段不能再分割,如“年级班级”可以分割为“年级”和“班级”两个字段
唯一主键 :一张表中需要有一个唯一主键用来区分每行数据,如“学生学号 ”
消除冗余和传递依赖 :不同表中不能存在重复的字段数据,如“学生”表中的“院系”字段和“班级”表中“院系”字段,我们可以关联两张表的字段而无需在“学生”表中再加一个“院系”。
详细解释请自行查找。


67. 了解分布式锁么?

在开发中可能会用到多线程和多进程,如果不同线程或者不同进程抢占同一个资源,对其行读写操作可能会导致数据不一致,导致数据不是在我们预想的情况下改变。这里为了保证线程或者进程安全,python中引入了线程锁和进程锁,保证了数据的一致性和完整性。

而为了保证分布式系统的数据安全,可以使用使用分布式锁来解决这一问题(秒杀场景)。 分布式锁其实可以理解为:控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性。 分布式锁的实现有很多种,常见的有redis、zookeeper和数据库mysql等。


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

引自: junli_chen

#!/usr/bin/python3
# coding=utf-8
import time
import redis
class RedisLock(object):
    def __init__(self, key):
        # 连接数据库,创建连接对象
        self.rdcon = redis.Redis(host='', port=6379, password="", db=1)
        # 设置锁的值
        self._lock = 0
        # 分布式锁的键
        self.lock_key = "%s_dynamic_test" % key
    @staticmethod
    def get_lock(cls, timeout=10):
        """获取redis分布式锁
        设置分布式锁,判断锁是否超时
        :param cls: 锁的类对象
        :param timeout: 锁超时时间
        :return:
        while cls._lock != 1:
            # 设置锁的过期时间
            timestamp = time.time() + timeout + 1
            # 设置redis分布式锁键值
            cls._lock = cls.rdcon.setnx(cls.lock_key, timestamp)
            # 判断锁的值是否为1,或者当前时间大于锁预期释放的时间,如果成立则退出循环,释放锁
            if cls._lock == 1 or (
                    time.time() > cls.rdcon.get(cls.lock_key) and
                    time.time() > cls.rdcon.getset(cls.lock_key, timestamp)):
                print("get lock")
                break
            else:
                time.sleep(0.3)
    @staticmethod
    def release(cls):
        """释放锁
        :param cls: 锁的类对象
        :return:
        # 判断当前时间是否大于锁最大释放时间
        if time.time() < cls.rdcon.get(cls.lock_key):
            print("release lock")
            cls.rdcon.delete(cls.lock_key)
def deco(cls):
    """分布式锁装饰器
    :param cls: 分布式锁类对象
    :return: 外层函数
    def _deco(func):
        def __deco(*args, **kwargs):
            print("before %s called [%s]." % (func.__name__, cls))
            cls.get_lock(cls)
            try:
                return func(*args, **kwargs)
            finally:
                cls.release(cls)
        return __deco
    return _deco
@deco(RedisLock("demoLock"))
def myfunc():
    print("myfunc() called.")
    # 设置20s模拟超过锁释放时间就自动释放锁的操作
    time.sleep(20)
if __name__ == "__main__":
    myfunc()


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

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
#!/usr/bin/python3
# coding=utf-8
import pymongo
from pymongo import ASCENDING, DESCENDING
# 连接数据库,创建连接对象
myclient = pymongo.MongoClient(mongodbUrl)
# 切换数据库
mydb = myclient[dbName]
# 创建索引,create_index()创建索引,可以有多个约束条件,值为1则升序,-1是降序
mydb.create_index([("date", DESCENDING), ("author", ASCENDING)])


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

装饰器主要是在不修改代码前提下进行功能的扩展,满足面向对象的“开闭原则”。

应用场景:
1,引入日志
2,函数执行时间统计
3,执行函数前预备处理
4,执行函数后清理功能
5,权限校验等场景
6,缓存
7,事务处理


71. Python 垃圾回收机制?

整数

小整数: Python 对小整数的定义是 [-5, 257) 这些整数对象是提前建立好的,不会被垃圾回收。在一个 Python 的程序中,所有位于这个范围内的整数使用的都是同一个对象。单个字母同样也是如此。

大整数:每一个大整数的创建均在内存中会分配一个内存空间,所以大整数的内存空间是需要被回收的。

引用计数为主,标记清除和分代回收为辅:

引用计数:

python里每一个东西都是对象,它们的核心就是一个结构体: PyObject
PyObject是每个对象必有的内容,其中 ob_refcnt 就是做为引用计数。当一个对象有新的引用时,它的 ob_refcnt 就会增加,当引用它的对象被删除,它的 ob_refcnt 就会减少
当引用计数为0时,该对象生命就结束了。
引用计数机制的优点:
1. 简单
2. 实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。
引用计数机制的缺点:
1. 维护引用计数消耗资源
2. 循环引用

标记清除

『标记清除(Mark—Sweep)』算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。它分为两个阶段:第一阶段是 标记阶段 ,GC会把所有的『活动对象』打上标记,第二阶段是把那些没有标记的对象『 非活动对象』进行回收 。从GCROOT出发,标记所有的可达对象,不可达的就清除掉。
标记清除算法作为Python的辅助垃圾收集技术主要处理的是一些容器对象,比如list、dict、tuple,instance等,因为对于字符串、数值对象是不可能造成循环引用问题。

分代回收

分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。
每个分代集合中索引值越大的代表存活时间越长,越不容易被回收。
分代回收是建立在标记清除技术基础之上。分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象


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

__call__允许一个类的实例像函数一样被调用
#!/usr/bin/python3
# coding=utf-8
class Entity(object):
    def __init__(self, size, x, y):
        self.x, self.y = x, y
        self.size = size
    def __call__(self, x, y):
        # 改变实例属性
        self.x, self.y = x, y
if __name__ == '__main__':
    # 创建实例
    demo_obj = Entity(1, 2, 3)
    # 实例可以像函数那样执行,并传入x y值,修改对象的x y
    demo_obj(4, 5)


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

在类外声明def为函数

类中声明def:使用类调用为函数,使用实例化对象调用为方法

可以使用isinstance()判断

#!/usr/bin/python3
# coding=utf-8
from types import FunctionType
from types import  MethodType
class DemoClass(object):
    def __init__(self):
    def run_method(self):
def demo_func():
if __name__ == '__main__':
    demo_obj = DemoClass()
    print(demo_obj.run_method)
    # out:<bound method DemoClass.run_method of <__main__.DemoClass object at 0x000000000277F588>>
    print(demo_func)
    # out:<function demo_func at 0x000000000248C1E0>
    print(DemoClass.run_method)
    # <function DemoClass.run_method at 0x0000000002924400>
    print(isinstance(demo_obj.run_method, FunctionType))   # False
    print(isinstance(demo_obj.run_method, MethodType))     # True


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

@classmethod 是类方法:访问和修改类属性,进行类相关的操作,通过类或示例对象调用,需要传递cls类对象为参数;
@staticmethod 是静态方法:不访问类属性和实例属性,通过类或实例调用,相当于一个普通函数。


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

类定义接口、函数定义接口


76. Python 中的反射了解么?

计算机中的反射,是在运行的时候来自我检查,并对内部成员进行操作。就是说这个变量的类型可以动态的改变,在运行的时候确定它的作用。
在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或自省。
具有反射能力的函数有type(),isinstance(),callable().dir().getattr()等

Python的反射是一个很强大的功能,个人认为每个Python程序员都应该掌握这一用法

这里我引用 橡皮头 的博客,非常详细的解释。


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

有关metaclass请移步廖大教程


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

这三种方法用于为对象属性的存在判断、获取和添加修改,简言之就是对象属性的“增、改、查”。

hasattr(object, name):判断对象是否存在name属性
class A():
    name = 'python'
    def func(self):
        return 'A()类的方法func()'
if __name__ == '__main__':
    print(hasattr(A, 'name'))           # True
    print(hasattr(A, 'age'))            # False
getattr(object, name[, default]):获取object对象name属性的值,若没有name属性,则返回default值
class A():
    name = 'python'
    def func(self):
        return 'Hello world'
if __name__ == '__main__':
    print(getattr(A, "name"))       # "python"
    print(getattr(A, "age"))        # Error:class A has no attribute 'age'
    print(getattr(A, "age", 18))    # 18
    print(getattr(A, "func")        # <unbound method A.func>
    print(getattr(A(), "func")()    # 'Hello world',获取到的方法需要实例化后才能调用,类方法则不需要
setattr(object, name, value)给object对象的name属性赋值value,如果对象原本存在给定的属性name,则setattr会更改属性的值为给定的balue,如果不存在属性name,会在对象中创建属性并赋值value
class A():
    name = 'python'
    def func(self):
        return 'Hello world'
if __name__ == '__main__':
    setattr(A, 'name', 'java')
    print(getattr(A, 'name'))             # java
    setattr(A, 'age', 20)
    print(getattr(A, "age")               # age


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

个人认为该题面试时被问到的几率很大,通过该题可以扩展出其他知识,掌握此题将会在面试中拥有主动权哦。

在Python中,所有以 "_ _" 双下划包起来的方法称为“魔法方法”
魔法方法Python解释器自动给出默认的,因此除非需要改变其内部功能,其它时刻刻使用默认魔法方法

最常用三个:"__ init__"、"__new __ "、" __ del __"

__new__是用来创建类并返回这个类的实例,
__init__将传入的参数来初始化该实例,以及初始化示例属性,与__new__共同构成了“构造函数”
__del__将实例化后的对象销毁,即为析构函数

类调用:__call__

__call__允许一个类像函数一样被调用

属性访问:__getattr__、__setattr__、__delattr__

__getattr__访问对象不存在的属性时,调用该方法,用于定义访问行为
__setattr__设置对象属性时调用
__delattr__删除对象属性时调用

上下文管理器:__enter__和__exit__

这两个方法请看上面第3题。

迭代器方法:__iter__和__next__

__iter__:返回一个容器迭代器,很多情况下会返回迭代器,尤其是当内置的iter()方法被调用的时候,以及当使用for x in container:方式循环的时候。迭代器是它们本身的对象,它们必须定义返回self的__iter__方法。
__next__:返回迭代器的下一个元素

还有很多,能答出三到四个就可以了,需要补充的重要方法请提醒答主。


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

demo_obj = range(1,11)    # 创建一个未知类型的对象
print(type(demo_dbj))     # 使用type()判断对象类型


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

结论先行:Python对可变对象(字典或列表)传址,对不可变对象(数字、字符或元祖)传值。

#!/usr/bin/python3
def demo_func(parm):
    """输出整数或者列表改变后的值
    :param parm: 整数或者列表
    :return: 
    if isinstance(parm, int):
        parm += 1
    elif isinstance(parm, list):
        parm.append(1)
    print(parm)
if __name__ == '__main__':
    # 定义整数类型
    int_parm = 1
    # 函数内整数值修改(不可变类型不能修改值,其实这里是变量另外赋值)
    demo_func(int_parm)   # 输出为2
    # 输出整数值,查看对象的值是否被修改
    print(int_parm)       # 输出为1,值未改变,说明传值
    # 定义列表类型
    list_patm = [1,2,3]
    # 函数修改列表
    demo_func(list_patm)   # 输出[1, 2, 3, 1]
    # 查看函数外部列表是否发生改变
    print(list_patm)       # 输出[1, 2, 3, 1],列表发生改变,说明传址


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

与77题重复


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

any()判断一个tuple或者list是否全为空,全空False, 不全为空返回True,空列表和空元祖为False;
all()判断一个tuple或者list是否全为非空,有一空则False, 全不空True,空列表和空元祖为True。


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

list(filter(lambda x: x % 2 == 1, (i for i in a)))   # py3需要使用list()转为list


85. 什么是猴子补丁?

猴子补丁的含义是指在动态语言中,不去改变源码而对功能进行追加和变更。

为什么叫猴子补丁?

1,这个词原来为Guerrilla Patch,杂牌军、游击队,说明这部分不是原装的,在英文里guerilla发音和gorllia(猩猩)相似,再后来就写了monkey(猴子)。
2,还有一种解释是说由于这种方式将原来的代码弄乱了(messing with it),在英文里叫monkeying about(顽皮的),所以叫做Monkey Patch。

使用协程时,通常在模块头部加入:gevent.monkey.patch_all(),用于将标准库中的thread/socket等给替换掉,这样我们在后面使用socket的时候可以跟平常一样使用,无需修改任何代码,但是它变成非阻塞的了。

总结:猴子补丁就是程序功能的追加或者变更。

网上还有一个 例子

之前做的一个游戏服务器,很多地方用的import json,后来发现ujson比自带json快了N倍,于是问题来了,难道几十个文件要一个个把import json改成import ujson as json吗?
其实只需要在进程startup的地方monkey patch就行了.是影响整个进程空间的.
同一进程空间中一个module只会被运行一次.
import json
import ujson
def monkey_patch_json():
    json.__name__ = 'ujson'
    json.dumps = ujson.dumps
    json.loads = ujson.loads
monkey_patch_json()
print 'main.py',json.__name__
import sub


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

Python内存池 :内存池的概念就是预先在内存中申请一定数量的,大小相等 的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够了之后再申请新的内存。这样做最显著的优势就是能够减少内存碎片,提升效率。
python中的内存管理机制——Pymalloc :python中的内存管理机制都有两套实现,一套是针对小对象,就是大小小于256bits时,pymalloc会在内存池中申请内存空间;当大于256bits,则会直接执行new/malloc的行为来申请内存空间。
内存释放参考垃圾回收


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

循环引用其它对象或引用自全局命名空间的对象的模块,在 Python 退出时并非完全释放。

这里我有疑惑,暂时没有找到合理的解释:垃圾回收中的标记清除不是可以解决循环引用的问题吗?


88.使用正则表达式匹配出<html><h1> 百度一下,你就知道 </html>中的地址 a="张明 98 分",用 re.sub,将 98 替换为 100

这题我看了好几遍没整明白,重新立一个好了

使用正则表达式匹配出 '<html><h1><div>a="张明 98 分"</div></html>' 中的地址 a="张明 98 分",用 re.sub,将 98 替换为 100

正则一直是我的软肋,这回一定要好好补补。。。

#!/usr/bin/python3
import re
html_str = '<html><h1><div>a="张明 98 分"</div></html>'
result_str = re.sub(r'\d{1, 2}', "100", html_str)
print(result_str)


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

1. 什么是贪婪匹配:贪婪匹配在匹配字符串时总是尝试匹配尽可能多的字符。
2. 什么是非贪婪匹配:与贪婪匹配相反,非贪婪匹配在匹配字符串时总是尝试匹配尽可能少的字符。
3. Python里数量词默认是贪婪模式的,在"*","?","+","{m,n}"后面加上?,可使贪婪模式变成非贪婪模式。
#!/usr/bin/python3
import re
demo_str = "abcdacsdn"
print("原始字符串  " + demo_str)
# 非贪婪匹配
non_greedy = "a.*?d"
print("非贪婪匹配 = " + non_greedy)
pattern = re.compile(non_greedy)
restult_list = re.findall(pattern , demo_str)
print("非贪婪匹配")
print(restult_list)
# 贪婪匹配
greedy = "a.*d"
print("贪婪匹配 = " + greedy)
pattern = re.compile(greedy)
restult_list = re.findall(pattern , demo_str)
print("贪婪匹配结果")
print(restult_list)


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

#!/usr/bin/python3
import re
text = input("Please input your Email address:\n")
# 匹配任意的邮箱,@前是19位的字符数字下换线组合
if re.match(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$',text):
    print('Email address is Right!')
else:
    print('Please reset your right Email address!')


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

Python中的pass是空语句,是为了保持程序结构的完整性;
pass不做任何事情,一般用做占位语句;
一般在搭建程序框架的时候或在判断语句中使用。


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

Python3.x中输入()函数接受一个标准输入数据,返回为字符串类型。
Python2.x中输入()相等于eval(raw_input(prompt)),用来获取控制台的输入。
raw_input()将所有输入作为字符串看待,返回字符串类型。而input()在对待纯数字输入时具有自己的特性,它返回所输入的数字的类型(int,float)。


93. python 中的 is 和==

is是身份运算符,判断两个对象的内存id是否相等
==是比较运算符,判断两个对象的值是否相等
进行值比较的时候使用==,判断是否是同一对象的时候使用is


94. Python 中的作用域(变量的作用域)

L (Local) 局部作用域
E (Enclosing) 闭包函数外的函数中
G (Global) 全局作用域
B (Built-in) 内建作用域
以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。


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

三元运算符就是在赋值变量的时候,可以直接加判断,然后赋值格式
条件为真时的结果 if 判段的条件 else 条件为假时的结果
先定义变量:
a = 1
b = 2
第一种写法:
erroStr = "More" if a > b else "Less"
print(erroStr) # 运行结果为:Less
第二种写法:
print({True: "More", False: "Less"}[a > b]) # 运行结果为:Less
第三种写法:
print(("FalseValue", "TrueValue")[a > b]) # 运行结果为:FalseValue
其中我们比较常见的是第一种。
第二三种是挺简洁的,但是写在项目里怕是接手的同事要抓狂了。


96. 了解 enumerate 么?

enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

以下是 enumerate() 方法的语法:

enumerate(sequence, [start=0])
参数
sequence -- 一个序列、迭代器或其他支持迭代对象。
start -- 下标起始位置。
#!/usr/bin/python3
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1))       # 下标从 1 开始
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]


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

请看第一题


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

使用global


99. pathlib 的用法举例

pathlib 模块提供了一组面向对象的类,这些类可代表各种操作系统上的路径,程序可通过这些类操作路径。
#!/usr/bin/python3
from pathlib import Path
# 1.查看路径
# 使用cmd()方法输出当前的工作目录
now_path = Path.cwd()
# 使用home()输出用户的主目录
home_path = Path.home()
print("当前工作目录", now_path, type(now_path))
print("home目录", home_path, type(home_path))
# 2. 路径拼接
# 将字符串转为Pathlib.Path类型
dir_path = Path(r"D:\code\web\flaskweb")
print(dir_path, type(dir_path))
# 使用 "/" 直接拼接路径
dir_path = Path(r"D:\code\web") / "flaskweb"
print(dir_path, type(dir_path))
# 3.读写文件
# 使用基础的open()函数
demo_file = Path.cwd() / 'test.md'
with open(demo_file, mode='r') as fid:
    file_data = fid.read()
print(file_data)
# 使用pathlib的open()方法
demo_file = Path.cwd() / 'test.md'
# 这样写的好处就是open里面我们不需要再去传入路径了,
# 直接指定文件读写模式即可。实际上这里的open方法,
# 底层也是调用了os.open的方法。使用哪种方式看个人的喜好。
with demo_file.open("r") as fid:
    file_data = fid.read()
print(file_data)
# 也可以不使用with open的形式即可以进行读写
# .read_text(): 找到对应的路径然后打开文件,读成str格式。等同open操作文件的"r"格式。
# .read_bytes(): 读取字节流的方式。等同open操作文件的"rb"格式。
# .write_text(): 文件的写的操作,等同open操作文件的"w"格式。
# .write_bytes(): 文件的写的操作,等同open操作文件的"wb"格式
# 4.使用resolve可以通过传入文件名,来返回文件的完整路径
py_path =Path("demo.py")
# 需要注意的是"demo.py"文件要和我当前的程序文件在同一级目录。
print(py_path.resolve())

更多pathlib的使用介绍请看


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

重复:第9题


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

最大次数为1000次,如何突破请看:

答主表示一脸懵逼


102. 什么是面向对象的 mro

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

案例:

#!/usr/bin/python3
class A(object):
    def f(self):
        print("A.f")
class B(A):
    def f(self):
        print("B.f")
class C(A):
    def f(self):
        print("C.f")
class D(B, C):
    def f(self):
        # super().f()    # 此时按照mro调用顺序可知调用的是B类的方法
        # 要想调用C类的方法,查看mro之后使用super调用C的上一类
        super(B, self).f()
        # super(a_type, obj), 其中第一个实参a_type是个类对象,第二个实参obj是个实例对象
        # 再次科普:self是实例对象本身,而不是类本身
if __name__ == '__main__':
    print(D.__mro__)
    # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
    d = D()
    d.f()


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

isinstance: 判断对象是否是一个已知的类型

isinstance(object, classinfo)
object -- 实例对象。
classinfo -- 可以是直接或间接类名、基本类型或者由它们组成的元组。

使用场景举例:

判断对象的数据类型,如参数和返回值判断,根据不同的数据类型
判断类的继承关系,isinstance可以用作判断是否继承了某个父类

科普: type和isinstance

type只输出当前类名,不管继承关系
isinstance在使用当前类的父类做判断时,输出为True(多重继承适用)
class A:
class B(A):
isinstance(A(), A)    # returns True
type(A()) == A        # returns True
isinstance(B(), A)    # returns True
type(B()) == A        # returns False


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

官方解释:

Assert statements are a convenient way to insert debugging assertions into a program.
断言语句是将调试断言插入程序的便捷方式

assert condition:在condition为True时不触发,False触发AssertionError错误

>>> assert 1==1
>>> assert 1==0
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    assert 1==0
AssertionError

如果没有特别的目的,断言应该用于如下情况:

  • 防御性的编程
  • 运行时对程序逻辑的检测
  • 合约性检查(比如前置条件,后置条件)
  • 程序中的常量
  • 检查文档


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

lambda表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。

lambda表达式:lambda 参数1,参数2...: 参数表达式

适用场景:

简单功能的函数实现
不需要关注函数命名
复用性不高或只用一次的函数

举例:输出1到100内的奇数

# 答主很喜欢用filder和lambda的组合
print(list(filter(lambda x: x % 2 == 1, range(1, 101))))

列表的排序:按照绝对值大小排序

list_demo = [3, 5, -4, -1, 0, -2, -6]
# sorted和lambda也是很好的组合,这里的abs是绝对值函数
print(sorted(list_demo, key=lambda x: abs(x)))

闭包lambda

#!/usr/bin/python3
def get_y(a, b):
    return lambda x: a*x + b
y1 = get_y(3, 1)
print(y1(1))  # 结果为4


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

在Python 3.x中取消了经典类,默认都是新式类,并且不必显式的继承object,也就是说:
class Person(object):pass
class Person():pass
class Person:pass
三种写法并无区别,推荐第一种


Python2.x中,默认都是经典类,只有显式继承了object才是新式类,即:
class Person(object):pass 新式类写法
class Person():pass 经典类写法
class Person:pass 经典类写法

新式类和经典类的最大的区别:继承搜索顺寻的变化

新式类多继承搜索顺序(广度优先):先在水平方向查找,然后再向上查找
经典类多继承搜索顺序(深度优先):先深入继承树左侧查找,然后再返回,开始查找右侧


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

dir()函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;
带参数时,返回参数的属性、方法列表。
如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。
#!/usr/bin/python3
class A(object):
    def f(self):
        print("A.f")
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确认导入的包


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

BaseException  # 所有异常的基类
 +-- SystemExit  # 解释器请求退出
 +-- KeyboardInterrupt  # 用户中断执行(通常是输入^C)
 +-- GeneratorExit  # 生成器(generator)发生异常来通知退出
 +-- Exception  # 常规异常的基类
      +-- StopIteration  # 迭代器没有更多的值
      +-- StopAsyncIteration  # 必须通过异步迭代器对象的__anext__()方法引发以停止迭代
      +-- ArithmeticError  # 各种算术错误引发的内置异常的基类
      |    +-- FloatingPointError  # 浮点计算错误
      |    +-- OverflowError  # 数值运算结果太大无法表示
      |    +-- ZeroDivisionError  # 除(或取模)零 (所有数据类型)
      +-- AssertionError  # 当assert语句失败时引发
      +-- AttributeError  # 属性引用或赋值失败
      +-- BufferError  # 无法执行与缓冲区相关的操作时引发
      +-- EOFError  # 当input()函数在没有读取任何数据的情况下达到文件结束条件(EOF)时引发
      +-- ImportError  # 导入模块/对象失败
      |    +-- ModuleNotFoundError  # 无法找到模块或在在sys.modules中找到None
      +-- LookupError  # 映射或序列上使用的键或索引无效时引发的异常的基类
      |    +-- IndexError  # 序列中没有此索引(index)
      |    +-- KeyError  # 映射中没有这个键
      +-- MemoryError  # 内存溢出错误(对于Python 解释器不是致命的)
      +-- NameError  # 未声明/初始化对象 (没有属性)
      |    +-- UnboundLocalError  # 访问未初始化的本地变量
      +-- OSError  # 操作系统错误,EnvironmentError,IOError,WindowsError,socket.error,select.error和mmap.error已合并到OSError中,构造函数可能返回子类
      |    +-- BlockingIOError  # 操作将阻塞对象(e.g. socket)设置为非阻塞操作
      |    +-- ChildProcessError  # 在子进程上的操作失败
      |    +-- ConnectionError  # 与连接相关的异常的基类
      |    |    +-- BrokenPipeError  # 另一端关闭时尝试写入管道或试图在已关闭写入的套接字上写入
      |    |    +-- ConnectionAbortedError  # 连接尝试被对等方中止
      |    |    +-- ConnectionRefusedError  # 连接尝试被对等方拒绝
      |    |    +-- ConnectionResetError    # 连接由对等方重置
      |    +-- FileExistsError  # 创建已存在的文件或目录
      |    +-- FileNotFoundError  # 请求不存在的文件或目录
      |    +-- InterruptedError  # 系统调用被输入信号中断
      |    +-- IsADirectoryError  # 在目录上请求文件操作(例如 os.remove())
      |    +-- NotADirectoryError  # 在不是目录的事物上请求目录操作(例如 os.listdir())
      |    +-- PermissionError  # 尝试在没有足够访问权限的情况下运行操作
      |    +-- ProcessLookupError  # 给定进程不存在
      |    +-- TimeoutError  # 系统函数在系统级别超时
      +-- ReferenceError  # weakref.proxy()函数创建的弱引用试图访问已经垃圾回收了的对象
      +-- RuntimeError  # 在检测到不属于任何其他类别的错误时触发
      |    +-- NotImplementedError  # 在用户定义的基类中,抽象方法要求派生类重写该方法或者正在开发的类指示仍然需要添加实际实现
      |    +-- RecursionError  # 解释器检测到超出最大递归深度
      +-- SyntaxError  # Python 语法错误
      |    +-- IndentationError  # 缩进错误
      |         +-- TabError  # Tab和空格混用
      +-- SystemError  # 解释器发现内部错误
      +-- TypeError  # 操作或函数应用于不适当类型的对象
      +-- ValueError  # 操作或函数接收到具有正确类型但值不合适的参数
      |    +-- UnicodeError  # 发生与Unicode相关的编码或解码错误
      |         +-- UnicodeDecodeError  # Unicode解码错误
      |         +-- UnicodeEncodeError  # Unicode编码错误
      |         +-- UnicodeTranslateError  # Unicode转码错误
      +-- Warning  # 警告的基类
           +-- DeprecationWarning  # 有关已弃用功能的警告的基类
           +-- PendingDeprecationWarning  # 有关不推荐使用功能的警告的基类
           +-- RuntimeWarning  # 有关可疑的运行时行为的警告的基类
           +-- SyntaxWarning  # 关于可疑语法警告的基类
           +-- UserWarning  # 用户代码生成警告的基类
           +-- FutureWarning  # 有关已弃用功能的警告的基类
           +-- ImportWarning  # 关于模块导入时可能出错的警告的基类
           +-- UnicodeWarning  # 与Unicode相关的警告的基类
           +-- BytesWarning  # 与bytes和bytearray相关的警告的基类
           +-- ResourceWarning  # 与资源使用相关的警告的基类。被默认警告过滤器忽略。


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

  • copy 仅拷贝对象本身,而不拷贝对象中引用的其它对象。
  • deepcopy 除拷贝对象本身,而且拷贝对象中引用的其它对象。(子对象)
copy不会为子对象额外创建新的内存空间,当子对象被修改之后,这个子对象的引用都会发生改变;
deepcopy是一个新对象的创建,只是用了和被拷贝对象相同的值,子对象改变不会影响被拷贝对象


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

args 是 arguments 的缩写,表示位置参数;
kwargs 是 keyword arguments 的缩写,表示关键字参数
def demo_func(*args, **kwargs):
    # arg是一个元祖类型
    print(args[1])
    # kwargs是一个字典类型
    print(kwargs.keys())
if __name__ == '__main__':
    # 直接传参,但关键字类型必须为str
    demo_func(1, 2, 3, a=1, b=2)
    # 使用*和**进行解包
    demo_func(*(1, 2, 3), **{"a": 1, "b": 2})


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

单下划线
单下划线开头 的命名方式被常用于模块中,在一个模块中以单下划线开头的变量和方法会被默认划入模块内部范围。当使用 from my_module import * 导入时,单下划线开头的变量和方法是不会被导入的。但使用 import my_module 导入的话,仍然可以用 my_module._var 这样的形式访问属性或方法。
单下划线结尾 的命名方式也存在,但是不常用,其实也不推荐用。这种命名方式的作用就是为了和 python 的一些内置关键词区分开来,假设我们想给一个变量命名为 class,但是这会跟 python 的关键词 class 冲突,所以我们只好退一步使用单下划线结尾命名,也就是 class_。
双下划线
双下划线开头和结尾的是一些 python 的“魔术”对象,如类成员的 __init__、__del__、__add__、__getitem__ 等,以及全局的__file__、__name__ 等。 python 官方推荐永远不要将这样的命名方式应用于自己的变量或函数,而是按照文档说明来使用。
双下划线开头的命名方式有实际的作用,采用这种命名的变量或方法无法直接通过 “对象名.变量名(方法名)” 这样的方式访问。


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

r : 读取文件,若文件不存在则会报错
w: 写入文件,若文件不存在则会先创建再写入,会覆盖原文件
a : 写入文件,若文件不存在则会先创建再写入,但不会覆盖原文件,而是追加在文件末尾
rb,wb:分别于r,w类似,用于读写二进制文件
r+ : 可读、可写,文件不存在也会报错,写操作时会覆盖
w+ : 可读,可写,文件不存在先创建,会覆盖
a+ :可读、可写,文件不存在先创建,不会覆盖,追加在末尾


114. 举例 sort 和 sorted 的区别

demo_list = [1, 3, 4, 2, 7, 5]
# sorted是一个函数,返回一个新的list
result_list = sorted(demo_list)
print(result_list)
# sort是实例方法,直接作用在list本身,没有返回新的list
demo_list.sort()
print(demo_list)


115. 什么是负索引?

负索引是指使用负数做为索引,-1代表数组的最后一位


116. pprint 模块是干什么的?

# pprint用于输出一个整齐美观Python数据的结构
import pprint
demo_list = [str(i)*20 for i in range(10)]
# indent是指句首缩进
pp_object = pprint.PrettyPrinter(indent=4)
pp_object.pprint(demo_list)  # 整齐输出
print(demo_list)             # 只输出一行


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


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


119. 讲讲 Python 中的位运算符


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

1、二进制数字由0和1组成,我们使用0b或0B前缀表示二进制数
print(int(0b1010))  #10
2、使用bin()函数将一个数字转换为它的二进制形式
print(bin(0xf))  #0b1111
3、八进制数由数字0-7组成,用前缀0o或0O表示8进制数
print(oct(8))  #0o10
4、十六进数由数字0-15组成,用前缀0x或者0X表示16进制数
print(hex(16))  #0x10
print(hex(15))  #0xf


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

a, b, = 1, 2


122.已知: Alist = [1, 2, 3] Bset = {1, 2, 3}

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

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

  • python的 列表 内部实现是数组(具体实现要看解析器, CPython的实现 ),因此就有数组的特点。超过容量会增加更多的容量,set, get 是O(1),但del, insert, in的性能是O(n)。
  • 关于 字典 需要了解的是hash函数和哈希桶。一个好的hash函数使到哈希桶中的值只有一个,若多个key hash到了同一个哈希桶中,称之为哈希冲突。查找值时,会先定位到哈希桶中,再遍历hash桶。更详细的信息请点这里。在hash基本没有冲突的情况下get, set, delete, in方面都是O(1)。
  • 集合 内部实现是dict的。在in操作上是O(1), 这一点比list要强。

由此可知:

(1)查找操作set优于list;

(2)插入操作两个相同。


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

def binary_Search(search_list: list, search_num: int):
    """二分查找
    利用二分法找到list数组中的值
    :param search_list: 目标list
    :param search_num: 待查询值
    :return:
    # 最小的下标
    min_index = 0
    # 最大的下标
    max_index = len(search_list) - 1
    # 当前索引下标
    now_index = 0
    while True:
        # 中间的下标每次向下取整
        mid_index = (min_index + max_index) // 2
        if search_num > search_list[mid_index]:
            # 小于需要的猜的数,则将最小下标变为中间的,因为中间的已经猜过,所以要加1
            min_index = mid_index + 1
        elif search_num == search_list[mid_index]:
            print("找到数据", "索引是{}".format(mid_index))
            print("一共查找{}次".format(now_index))
            break
        else:
            # 大于需要的猜的数,则将最大下标变为中间的,因为中间的已经猜过,所以要减1
            max_index = mid_index - 1
        # 索引值加一
        now_index += 1
if __name__ == "__main__":
    list1 = [i for i in range(0, 1000)]
    num = 0
    binary_Search(list1, num)


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

class SingleCase(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance
if __name__ == '__main__':
    a = SingleCase()
    b = SingleCase()


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

#!/usr/bin/python3
def fbnq(num):
    """斐波那契生成器
    :param num: 生产数量
    :return: 斐波那契迭代器
    a, b = 1, 1
    for _ in range(num):
        a, b = b, a+b
        yield a
if __name__ == '__main__':
    gener = fbnq(20)
    print(gener)
    for i in gener:
        print(i)


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

#!/usr/bin/python3
from collections import Counter
result = Counter([1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4])
print(result)
# Counter({2: 4, 4: 4, 3: 3, 1: 1})


128. 写一个冒泡排序

def bubble_sort(parm_list):
    """冒泡排序
    冒泡排序思路:判断前后两个值的大小,若前大于后则调换两个值位置
    每一轮循环都可以将最大值放到末尾,所以需要迭代次数为数组的大小
    因为每次都将最大值放到最后,所以内层迭代就不需要全部检测一遍
    :param parm_list:
    :return:
    for n in range(len(parm_list)):
        for now_index in range(len(parm_list)-n-1):
            if parm_list[now_index] > parm_list[now_index + 1]:
                parm_list[now_index], parm_list[now_index + 1] = \
                    parm_list[now_index + 1], parm_list[now_index]
    print(parm_list)
if __name__ == '__main__':
    bubble_sort([1, 2, 3, 7, 5, 4, 6])


129. 写一个快速排序

def quick_sort(parm_list):
    """快排
    每次选取第一个值为基准值,再把列表中比基准值大的组成新列表,小的组成另一个新列表
    再次对两个新列表进行操作,直到新列表为空
    :param parm_list: 参数列表
    :return:
    if not parm_list:
        return []
    else:
        pivot = parm_list[0]
        # 利用递归每次找出大于和小于基准值得两个新列表
        lesser = quick_sort([x for x in parm_list[1:] if x < pivot])
        greater = quick_sort([x for x in parm_list[1:] if x >= pivot])
        # 最后将排列好的值相加
        return lesser + [pivot] + greater
if __name__ == '__main__':
    demo_list = [4, 23, 5, 6, 43, 14, 9, -23, 2, 6, 123, 12, 3, 3, 3, 3, 1]
    print(quick_sort(demo_list))


130. 写一个拓扑排序

拓扑排序:

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: blog.csdn.net/qq_417132
在一个有向图中,对所有的节点进行排序,要求没有一个节点指向它前面的节点。
先统计所有节点的入度,对于入度为0的节点就可以分离出来,然后把这个节点指向的节点的入度减一。
一直做改操作,直到所有的节点都被分离出来。
如果最后不存在入度为0的节点,那就说明有环,不存在拓扑排序,也就是很多题目的无解的情况。
下面是算法的演示过程。

Python拓扑实现:参考链接

def topology_sort(relation_parm):
    """简单拓扑排序
    简单实现拓扑排序算法,不考虑时间复杂度
    :param relation_parm: 拓扑关系字典参数
    :return: 排序后结果
    in_degrees = dict((u, 0) for u in relation_parm)            # 初始化所有顶点入度为0,转为字典类型
    vertex_num = len(in_degrees)                                # 字典键值个数
    for key in relation_parm:                                   # 遍历关系参数字典,求出每个顶点的入度值
        for value in relation_parm[key]:                        # 遍历每个键的值,将关系其他顶点的键值+1到in_degrees字典中
            in_degrees[value] += 1                              # 计算每个顶点的入度
    zero_list = [u for u in in_degrees if in_degrees[u] == 0]   # 筛选入度为0的顶点
    result_list = []                                            # 创建结果空列表
    while zero_list:                                            # 循环判断列表为空
        last_zero = zero_list.pop()                             # 删除最后一个入度为0的对象
        result_list.append(last_zero)                           # 结构列表添加刚删除的对象
        for zero_value in relation_parm[last_zero]:             # 遍历以入度为0作为键的字典值
            in_degrees[zero_value] -= 1                         # 和last_zero有关系的键值-1
            if in_degrees[zero_value] == 0:                     # 判断-1过后的键值是否为0
                zero_list.append(zero_value)                    # 再次筛选入度为0的顶点
    if len(result_list) != vertex_num:                          # 如果循环结束后结果列表对象个数不等于原始键个数,说明存在环
        print("there's a circle.")
    else:
        return result_list
if __name__ == '__main__':
    relation_dict = {
        'a': 'bcd',
        'b': '',
        'c': 'be',
        'd': 'e',
        'e': '',
        'f': 'de'
    print(topology_sort(relation_dict))


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



138. 在 requests 模块中,requests.content 和 requests.text 什么区别

.content中间存的是字节码 .text存的是.content编码后的字符串


139. 简要写一下 lxml 模块的使用方法框架

from lxml import etree
text = '''
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
# 获取 html 内容 元素,转为etree类型
htmlEmt = etree.HTML(text)
print(type(htmlEmt))
# 将内容元素转换为字符串
result = etree.tostring(htmlEmt)
# utf-8 格式输出,lxml有自动修正html的功能
print(result.decode("utf-8"))
# 获取所有的 <li> 标签
result = htmlEmt.xpath('//li')
print(result)
# 获取标签数量
print(len(result))
result = htmlEmt.xpath('//li/@class')
print(result)
# 获取 <li> 标签下 href 为 link1.html 的 <a> 标签
result = htmlEmt.xpath('//li/a[@href="link1.html"]')
print(result)
print(result[0].text)
# 获取最后一个 <li> 的 <a> 的 href
result = htmlEmt.xpath('//li[last()]/a/@href')
print(result)
# 获取倒数第二个元素的内容
result = htmlEmt.xpath('//li[last()-1]/a')
print(result[0].text)
# 获取 class 为 item-0 的标签名
result = htmlEmt.xpath('//*[@class="item-0"]')