这篇文章上次修改于 803 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

- 语言特性

1.谈谈对Python和其他语言的区别

答:Python是一门语法简洁优美, 功能强大无比, 应用领域非常广泛, 具有强大完备的第三方库,它是一门强类型的可移植、可扩展、可嵌入的解释型编程语言,属于动态语言。
拿C语言和Python比:Python的第三方类库比较齐全并且使用简洁, 很少代码就能实现一些功能,如果用C去实现相同的功能可能就比较复杂。
但是对于速度来说Python的运行速度相较于C就比较慢了。所以有利的同时也有弊端,毕竟我们的学习成本降低了。

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

答:解释型语言是在运行程序的时候才翻译,每执行一次,要翻译一次,效率较低。 编译型就是直接编译成机型可以执行的,只翻译一次,所以效率相对来说较高。

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

答:CPythonc语言开发的,使用最广的解释器IPython基于cPython之上的一个交互式计时器,交互方式增强功能和cPython一样PyPy目标是执行效率,采用JIT技术。
对Python代码进行动态编译,提高执行效率JPython运行在Java上的解释器,直接把Python代码编译成Java字节码执行IronPython运行在微软.NET平台上的解释器,把Python编译成.NET的字节码。

4.Python3和Python2的区别?

答: 这里例举5条print在Python3中是函数必须加括号,Python2中print为class。Python2中使用xrange,Python3使用range。
Python2中默认的字符串类型默认是ASCII,Python3中默认的字符串类型是Unicode。Python2中 “/“(除) 的结果是整型,Python3中是浮点类型。
Python2中声明元类:metaclass = MetaClass, Python3中声明元类:class newclass(metaclass=MetaClass):pass。

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

答:Python2有int和long类型。int类型最大值不能超过sys.maxint,而且这个最大值是平台相关的。可以通过在数字的末尾附上一个L来定义长整型,显然,它比int类型表示的数字范围更大。
在Python3里,只有一种整数类型int,大多数情况下,和Python2中的长整型类似。

6.xrange和range的区别?

答:xrange是在Python2中的用法,Python3中只有rangexrange用法与range完全相同,所不同的是生成的不是一个list对象,而是一个生成器。

- 编码规范

7.什么是PEP8?

答:PEP8通常会听别人提到,但是具体的指什么内容呢,简单介绍下。 《PythonEnhancementProposal # 8》 (8 号 Python 增强提案)又叫 PEP8,
他针对的 Python 代码格式而编订的风格指南。

8.了解Python之禅么?

答:通过import this语句可以获取其具体的内容。它告诉大家如何写出高效整洁的代码。

9.了解DocStrings么?

答:DocStrings文档字符串是一个重要工具,用于解释文档程序,帮助你的程序文档更加简单易懂。主要是解释代码作用的。

10.了解类型注解么?

答:PEP484引入了类型提示,这使得可以对Python代码进行静态类型检查。 在使用Ide的时候可以获取到参数的类型,更方便传入参数。使用格式如下
def foo(num:int) -> None:
print(f”接收到的数字是:{num}”)
介绍下这个简单例子,我们可以在函数的参数部分使用参数名 +:+类型,来指定参数可以接受的类型,这里的话就是num参数为int类型,然后后面->接的是返回值的类型。
这里返回值为None,然后通过fstring格式化字符串输出传入的数字。

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

答:
类:总是使用首字母大写单词串,如MyClass。内部类可以使用额外的前导下划线。
变量:小写,由下划线连接各个单词。方法名类似
常量:常量名所有字母大写等

12.Python中的注释有几种?

答:总体来说分为两种,单行注释和多行注释。单行注释在行首是# 。多行注释可以使用三个单引号或三个双引号,包括要注释的内容。

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

答:可以使用docstring配合类型注解

14.如何给变量加注释?

答:可以通过变量名:类型的方式如下
a: str = “this is string type”

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

答:不允许tab键和空格键混用,这种现象在使用sublime的时候尤为明显。一般推荐使用4个空格替代tab键。

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

答:可以是可以,但是不推荐。因为一次导入多个模块可读性不是很好,所以一行导入一个模块会比较好。同样的尽量少用 from modulename import *,因为判断某个函数或者属性的来源有些困难,不方便调试,可读性也降低了。

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

答:给文件命名的时候不要和标准库库的一些模块重复,比如abc。 另外要名字要有意义,不建议数字开头或者中文命名。

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

答:pylint和flake8

- 数据类型 - 字符串

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

答: Python3中有六个标准的数据类型:字符串(String)、数字(Digit)、列表(List)、元组(Tuple)、集合(Sets)、字典(Dictionary)。

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

答: 从对象内存地址方向来说
可变数据类型:在内存地址不变的情况下,值可改变(列表和字典是可变类型,但是字典中的key值必须是不可变类型)
不可变数据类型:内存改变,值也跟着改变。(数字,字符串,布尔类型,都是不可变类型)可以通过id()方法进行内存地址的检测。

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

答: 这个得看清题目是要求两个单词首字母都要大写,如果只是第一个单词首字母大小的话,只使用capitalize即可,但是这里是两个单词,所以用下面的方法。
arr = “hello world”.split(“ “)
new_str = f”{arr[0].capitalize()} {arr[1].capitalize()}”
print(new_str)
后来评论中有朋友提到了下面的方法,这里感谢这位朋友提醒。方案如下
“hello world”.title()
非常简单一句话搞定。

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

答:可以通过isdigit方法,例子如下
s1 = “12223”.isdigit()
print(s1)
s2 = “12223a”.isdigit()
print(s2)
# 结果如下:
# True
# False

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

答:
s1 = “ilovechina”[::-1]
print(s1)

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

答: % s,format,fstring(Python3.6开始才支持,现在推荐的写法)

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

答:因为题目要是写一个函数所以我们不能直接使用strip,不过我们可以把它封装到函数啊
def strip_function(s1):
return s1.strip()
s1 = “ adabdw “
print(strip_function(s1))

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

答:切片使用的考察,最后两个即开始索引是 - 2,代码如下
a = “123456”
print(a[-2::])

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

答:
a = “S”.encode(“gbk”).decode(“utf-8”, ‘ignore’)
print(a)

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

答:
(1)我们需要根据冒号或者空格切分
import re
s = “info:xiaoZhang 33 shandong”
res = re.split(r”:| “, s)
print(res)
(2)s = “你好 中国 “
print(“ “.join(s.split()))

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

答:
(1)使用字符串的lower()方法。

(2)单独使用单引号和双引号没什么区别,但是如果引号里面还需要使用引号的时候,就需要这两个配合使用了,
然后说三引号,同样的三引号也分为三单引号和三双引号,两个都可以声名长的字符串时候使用,如果使用docstring就需要使用三双引号。

数据类型 - 列表

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

答:list(set(AList))

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

答:
s = “1,2,3”
print(s.split(“,”))

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

答:
A、B中相同元素:
print(set(A) & set(B))
A、B中不同元素:
print(set(A) ^ set(B))

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

答:
l = [[1, 2], [3, 4], [5, 6]]
x = [j for i in l for j in i]
print(x)

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

答:使用extend和 + 都可以。
a = [1, 5, 7, 9]
b = [2, 2, 6, 8]
a.extend(b)
print(a)

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

答:
import random
a = [1, 2, 3, 4, 5]
random.shuffle(a)
print(a)

- 数据类型 - 字典

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

答:del 可以根据索引(元素所在位置)来删除的,没有返回值。 pop可以根据索引弹出一个值,然后可以接收它的返回值。

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

d1 = [
    {'name':'alice', 'age':38},
    {'name':'bob', 'age':18},
    {'name':'Carl', 'age':28},
]

答:
print(sorted(d1, key=lambda x:x[“age”]))

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

答: 合并字典方法很多,可以使用
a.update(b)
或者下面字典解包的方式
a = {“A”:1, “B”:2}
b = {“C”:3, “D”:4}
print({**a, **b})

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

答:# 需求 3: 把字典的 key 和 value 值调换;
d = {‘a’:’1’, ‘b’:’2’}
print({v:k for k, v in d.items()})

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

答: zip的使用,但是最后记得把zip对象再转换为字典。
a = (“a”, “b”)
b = (1, 2)
print(dict(zip(a, b)))

- 数据类型 - 综合

41.下列字典对象键类型不正确的是?

A:{1:0, 2:0, 3:0}
B:{"a":0, "b":0, "c":0}
C:{(1, 2):0, (2, 3):0}
D:{[1, 2]:0, [2, 3]:0}

答:D因为只有可hash的对象才能做字典的键,列表是可变类型不是可hash对象,所以不能用列表做为字典的键。

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

答:s = {“A”:1, “B”:2}
方法一:
dict_new = {value: key for key,value in s.items()}
方法二:
new_s = dict(zip(s.values(),s.keys()))

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

答: Python中的类型转换,一般通过类型强转即可完成
tuple转list是list()方法
list转tuple使用tuple()方法

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

答:这个题目考察了Python标准库的itertools模快的掌握情况,该模块提供了操作生成器的一些方法。 对于生成器类型我们使用islice方法来实现切片的功能。例子如下
from itertools import islice
gen = iter(range(10)) # iter()函数用来生成迭代器
# 第一个参数是迭代器,第二个参数起始索引,第三个参数结束索引,不支持负数索引
for i in islice(gen, 0, 4):
print(i)

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

答:通过把列表生产式的中括号,改为小括号我们就实现了生产器的功能即,
(i for i in range(3))

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

答: 这个题目一共三种方式,第一种是在字符串的前面加一个b,第二种可以使用bytes方法,第三种使用字符串encode方法。具体代码如下,
abc代表三种情况
a = b”hello”
b = bytes(“你好”, “utf-8”)
c = “你好”.encode(“utf-8”)
print(a, b, c)

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

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

答: 我们知道元组里的元素是不能改变的所以这个题目的答案是出现异常。

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

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

答:前面我说了元组的里元素是不能改变的,这句话严格来说是不准确的,如果元组里面元素本身就是可变类型,比如列表,那么在操作这个元素里的对象时,其内存地址也是不变的。
a[3]对应的元素是列表,然后对列表第一个元素赋值,所以最后的结果是: (1, 2, 3, [2, 5, 6, 7], 8)

- 操作类题目

49.Python交换两个变量的值

答:在Python中交换两个对象的值通过下面的方式即可
a, b = b, a
但是需要强调的是这并不是元组解包,通过dis模块可以发现,这是交换操作的字节码是ROT_TWO,意思是在栈的顶端做两个值的互换操作。

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

答:
read()每次读取整个文件,它通常用于将文件内容放到一个字符串变量中。如果希望一行一行的输出那么就可以使用readline(),
该方法会把文件的内容加载到内存,所以对于对于大文件的读取操作来说非常的消耗内存资源,此时就可以通过readlines方法,将文件的句柄生成一个生产器,然后去读就可以了。

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

答: 可以处理的数据类型是str、int、list、tuple、dict、bool、None, 因为datetime类不支持json序列化,所以我们对它进行拓展。
# 自定义时间序列化
import json
from datetime import datetime, date
# JSONEncoder 不知道怎么去把这个数据转换成 json 字符串的时候
# ,它就会去调 default()函数,所以都是重写这个函数来处理它本身不支持的数据类型,
# default()函数默#认是直接抛异常的。
class DateToJson(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime(‘%Y-%m-%d %H:%M:%S’)
elif isinstance(obj, date):
return obj.strftime(‘%Y-%m-%d’)
else :
return json.JSONEncoder.default(self, obj)
d = {‘name’: ‘cxa’, ‘data’: datetime.now()}
print(json.dumps(d, cls=DateToJson))

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

答:可以通过json.dumps的ensure_ascii参数解决,代码示例如下:
import json
a = json.dumps({“name”:”张三”}, ensure_ascii = False)
print(a)

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

答:
# 文件 A.txt 内容为 ASDCF
# 文件 B.txt 内容为 EFGGTG
with open(“A.txt”) as f1:
f1_txt = f1.readline()
with open(“B.txt”) as f2:
f2_txt = f2.readline()
f3_txt = f1_txt + f2_txt
f3_list = sorted(f3_txt)
with open(“C.txt”, “a+”) as f:
f.write(“”.join(f3_list))
# 输出的文件C的内容为ACDEFFGGGST

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

答:这个题目考察的是datetime里的timedelta方法的使用,参数可选、默认值都为0:
datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
通过这个参数可以指定不同的日期类型进行加减操作,这里我们需要改的是days,代码如下
import datetime

def datetime_operate(n:int):
    now = datetime.datetime.now()  # 获取当前时间
    _new_date = now + datetime.timedelta(days=n)  # 获取指定天数后的新日期
    new_date = _new_date.strftime("%Y%m%d")  # 转换为指定的输出格式
    return new_date
if __name__ == '__main__':
    print(datetime_operate(4))

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

答:这个题目考查了闭包的使用代码示例如下,返回函数之类型是函数对象。
def mul_operate(num):
def g(val):
return num * val
return g
m = mul_operate(8)
print(m(5))

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

def strappend(num):
    str = 'first'
    for i in range(num):
    str += str(i)
    return str

答: 首先不应该使用Python的内置类似str作为变量名这里我把它改为了s, 另外在Python, str是个不可变对象,每次迭代都会生成新的存储空间,
num越大,创建的str对象就会越多,内存消耗越大。使用yield 改成生成器即可, 还有一点就是命名规范的位置,函数名改为_分割比较好,完整的代码如下:
def str_append(num):
s = ‘first’
for i in range(num):
s += str(i)
yield s

if __name__ == '__main__':
    for i in str_append(3):
        print(i)

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

答:可以通过列表生成式,然后使用与操作如果如1与之后结果为0则表明为偶数,等于1则为奇数。
# 方法1
print([i for i in range(1, 101) if i & 0x1 == 0])
# 方法2:测试发现方法二效率更高
print(list(range(2, 101, 2)))

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

with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。
其他的内容看下面我之前写的代码。
# 一般访问文件资源时我们会这样处理:
f = open(
‘c:\test.txt’, ‘r’)
data = f.read()
f.close()
# 这样写没有错,但是容易犯两个毛病:
# 1. 如果在读写时出现异常而忘了异常处理。
# 2. 忘了关闭文件句柄
# 以下的加强版本的写法:
f = open(‘c:\test.txt’, ‘r’)
try:
data = f.read()
finally:
f.close()
# 以上的写法就可以避免因读取文件时异常的发生而没有关闭问题的处理了。代码长了一些。
# 但使用 with 有更优雅的写法:
with open(r’c:\test.txt’, ‘r’) as f:
data = f.read()
# with 的实现
class Test:
def enter(self):
print(‘enter() is call!’)
return self
def dosomething(self):
print(‘dosomethong!’)
def exit(self, exc_type, exc_value, traceback):
print(‘exit() is call!’)
print(f’type:{exc_type}’)
print(f’value:{exc_value}’)
print(f’trace:{traceback}’)
print(‘exit() is call!’)
with Test() as sample:
pass
# 当对象被实例化时,就会主动调用__enter__()方法,任务执行完成后就会调用__exit__()方法,
# 另外,注意到,exit()方法是带有三个参数的(exc_type, exc_value, traceback),
# 依据上面的官方说明:如果上下文运行时没有异常发生,那么三个参数都将置为 None,
# 这里三个参数由于没有发生异常,的确是置为了 None, 与预期一致.
# 修改后不出异常了
class Test:
def enter(self):
print(‘enter() is call!’)
return self
def dosomething(self):
x = 1 / 0
print(‘dosomethong!’)

        def __exit__(self, exc_type, exc_value, traceback):
            print('__exit__() is call!')
            print(f'type:{exc_type}')
            print(f'value:{exc_value}')
            print(f'trace:{traceback}')
            print('__exit()__ is call!')
            return True
    with Test() as sample:
        pass

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

答:在Python中使用dumps方法将dict对象转为Json对象,使用loads方法可以将Json对象转为dict对象。
dic = {‘a’: 123, ‘b’: “456”, ‘c’: “liming”}
json_str = json.dumps(dic)
print(json_str)
dic2 = json.loads(json_str)
print(dic2)

# '{"a": 123, "b": "456", "c": "liming"}'
# {'a': 123, 'b': '456', 'c': 'liming'}

我们再来看一个特殊的例子
    import json
    dic = {'a': 123, 'b': "456", 'c': "liming"}
    dic_str = json.loads(str(dic).replace("'", "\""))
    print(dic_str)
下面我解释下上面代码是测试什么:
首先json.loads(jsonstr)这里面的参数只能是jsonstr格式的字符串.当我们使用str将字典dic转化为字符串以后,
得到的结果为: "{'a': 123, 'b': '456', 'c': 'liming'}"。如果直接使用json.loads(str(dic))你会发现出现错误,
原因就是,单引号的字符串不符合Json的标准格式所以再次使用了replace("'", "\"")。得到字典
其实这个例子主要目的是告诉大家Json的标准格式是不支持单引号型字符串的,否则会出现以下错误。
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column2(char1)

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

答:
with open(‘A.txt’) as fs:
count = 0
for i in fs.read():
if i.isupper():
count += 1
print(count)

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

答:
# -- coding: utf-8 --
import pymongo
db_configs = {
‘type’: ‘mongo’,
‘host’: ‘地址’,
‘port’: ‘端口’,
‘user’: ‘spider_data’,
‘passwd’: ‘密码’,
‘db_name’: ‘spider_data’
}
class Mongo():
def init(self, db=db_configs[“db_name”], username=db_configs[“user”],
password=db_configs[“passwd”]):
self.client = pymongo.MongoClient(f’mongodb://{db_configs[“host”]}:db_configs[“port”]’)
self.username = username
self.password = password
if self.username and self.password:
self.db1 = self.client[db].authenticate(self.username, self.password)
self.db1 = self.client[db]

    def find_data(self):
        # 获取状态为0的数据
        data = self.db1.test.find({"status": 0})
        gen = (item for item in data)
        return gen

if __name__ == '__main__':
    m = Mongo()
    print(m.find_data())

62.说一说Redis的基本类型

答: Redis支持五种数据类型: string(字符串) 、 hash(哈希)、list(列表) 、 set(集合) 及zset(sortedset: 有序集合)。

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

答:
from redis import StrictRedis, ConnectionPool
redis_url = “redis://:xxxx@112.27.10.168:6379/15”
pool = ConnectionPool.from_url(redis_url, decode_responses=True)
r = StrictRedis(connection_pool=pool)

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

答:
conn = pymysql.connect(host=’localhost’,
port=3306, user=’root’,
passwd=’1234’, db=’user’, charset=’utf8mb4’) # 声明mysql连接对象
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 查询结果以字典的形式
cursor.execute(‘sql语句字符串’) # 执行sql语句
conn.close() # 关闭链接

65.了解Redis的事务么

答: 简单理解,可以认为redis事务是一些列redis命令的集合,并且有如下两个特点:
1.事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
2.事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。 一般来说,事务有四个性质称为ACID,分别是原子性,一致性,隔离性和持久性。一个事务从开始到执行会经历以下三个阶段:
开始事务\命令入队\执行事务
代码示例:
import redis
import sys
def run():
try:
conn = redis.StrictRedis(‘192.168.80.41’)
# Python中redis事务是通过pipeline的封装实现的
pipe = conn.pipeline()
pipe.sadd(‘s001’, ‘a’)
sys.exit()
# 在事务还没有提交前退出,所以事务不会被执行。
pipe.sadd(‘s001’, ‘b’)
pipe.execute()
pass
except Exception as err:
print(err)
pass
if name == “main“:
run()

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

答: 经过研究和对使用中问题的总结,对于设计数据库提出了一些规范,这些规范被称为范式一般需要遵守下面3范式即可:
- 第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。
- 第二范式(2NF):首先是1NF,另外包含两部分内容,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。
- 第三范式(3NF):首先是2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列A依赖于非主键列B,非主键列B依赖于主键的情况。

67.了解分布式锁么

答: 分布式锁是控制分布式系统之间的同步访问共享资源的一种方式。 对于分布式锁的目标,我们必须首先明确三点:
- 任何一个时间点必须只能够有一个客户端拥有锁。
- 不能够有死锁,也就是最终客户端都能够获得锁,尽管可能会经历失败。
- 错误容忍性要好,只要有大部分的Redis实例存活,客户端就应该能够获得锁。
分布式锁的条件
互斥性:分布式锁需要保证在不同节点的不同线程的互斥
可重入性:同一个节点上的同一个线程如果获取了锁之后,能够再次获取这个锁。
锁超时:支持超时释放锁,防止死锁
高效,高可用:加锁和解锁需要高效,同时也需要保证高可用防止分布式锁失效,可以增加降级。
支持阻塞和非阻塞:可以实现超时获取失败,tryLock(longtimeOut) 支持公平锁和非公平锁
分布式锁的实现方案
1、数据库实现(乐观锁)
2、基于zookeeper的实现
3、基于Redis的实现(推荐)

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

答: REDIS分布式锁实现的方式:SETNX + GETSET, NX是NoteXists的缩写,如SETNX命令就应该理解为:SET if NoteXists。 多个进程执行以下Redis命令:
SETNX
lock.foo < current
Unix
time + lock
timeout + 1 >
如果SETNX返回1,说明该进程获得锁,SETNX将键lock.foo的值设置为锁的超时时间(当前时间 + 锁的有效时间)。
如果SETNX返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试SETNX操作,以获得锁。

import time
import redis
from conf.config import REDIS_HOST, REDIS_PORT, REDIS_PASSWORD

class RedisLock:
    def __init__(self):
        self.conn = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD, db=1)
        self._lock = 0
        self.lock_key = ""

    @staticmethod
    def my_float(timestamp):
    """
    Args:
        timestamp:
    Returns:
        float或者0
        如果取出的是None,说明原本锁并没人用,getset已经写入,返回0,可以继续操作。
    """
        if timestamp:
            return float(timestamp)
        else:
            # 防止取出的值为None,转换float报错
            return 0

    @staticmethod
    def get_lock(cls, key, timeout=10):
        cls.lock_key = f"{key}_dynamic_lock"
        while cls._lock != 1:
            timestamp = time.time() + timeout + 1
            cls._lock = cls.conn.setnx(cls.lock_key, timestamp)
            # if 条件中,可能在运行到or之后被释放,也可能在and之后被释放
            # 将导致 get到一个None,float失败。
            if cls._lock == 1 or (
                    time.time() > cls.my_float(cls.conn.get(cls.lock_key)) and
                    time.time() > cls.my_float(cls.conn.getset(cls.lock_key, timestamp))):
                break
            else:
                time.sleep(0.3)

    @staticmethod
    def release(cls):
        if cls.conn.get(cls.lock_key) and time.time() < cls.conn.get(cls.lock_key):
            cls.conn.delete(cls.lock_key)

def redis_lock_deco(cls):
    def _deco(func):
        def __deco(*args, **kwargs):
            cls.get_lock(cls, args[1])
            try:
                return func(*args, **kwargs)
            finally:
                cls.release(cls)

        return __deco

    return _deco

@redis_lock_deco(RedisLock())
def my_func():
    print("myfunc() called.")
    time.sleep(20)

if __name__ == "__main__":
    my_func()

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

答:
# -- coding: utf-8 --
# @Time : 2018/12/28 10:01 AM
import pymongo
db_configs = {
‘type’: ‘mongo’,
‘host’: ‘地址’,
‘port’: ‘端口’,
‘user’: ‘spider_data’,
‘passwd’: ‘密码’,
‘db_name’: ‘spider_data’
}

class Mongo():
    def __init__(self, db=db_configs["db_name"], username=db_configs["user"],
                 password=db_configs["passwd"]):
        self.client = pymongo.MongoClient(f'mongodb://{db_configs["host"]}:{db_configs["port"]}')
        self.username = username
        self.password = password
        if self.username and self.password:
            self.db1 = self.client[db].authenticate(self.username, self.password)
        self.db1 = self.client[db]

    def add_index(self):
    # 通过create_index添加索引
        self.db1.test.create_index([('name', pymongo.ASCENDING)], unique=True)

    def get_index(self, ):
    # 查看索引列表
        indexlist = self.db1.test.list_indexes()
        for index in indexlist:
            print(index)

if __name__ == '__main__':
    m = Mongo()
    m.add_index()
    print(m.get_index())
  • 高级特性

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

答: 装饰器就是一个函数,它可以在不需要做任何代码变动的前提下给一个函数增加额外功能,启动装饰的效果。 它经常用于有切面需求的场景,
比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。 下面是一个日志功能的装饰器
from functools import wraps
def log(label):
def decorate(func):
@wraps(func)
def _wrap(*args, **kwargs):
try:
func(*args, **kwargs)
print(“name”, func.name)
except Exception as e:
print(e.args)
return _wrap
return decorate
@log(“info”)
def foo(a, b, c):
print(a + b + c)
print(“in foo”)
decorate=decorate(foo)
if name == ‘main‘:
foo(1, 2, 3)
decorate()

71.Python垃圾回收机制?

答:Python不像C++,Java等语言一样,他们可以不用事先声明变量类型而直接对变量进行赋值。对Python语言来讲,对象的类型和内存都是在运行时确定的。
这也是为什么我们称Python语言为动态类型的原因。主要体现在下面三个方法:
1.引用计数机制
2.标记 - 清除
3.分代回收

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

答: _call_可以把类实例当做函数调用。 使用示例如下
class Bar:
def call(self, *args, **kwargs):
print(‘in call’)
if name == ‘main‘:
b = Bar()
b()

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

答:看代码已经结果就懂了
from types import MethodType, FunctionType
class Bar:
def foo(self):
pass
def foo2():
pass
def run():
print(“foo 是函数”, isinstance(Bar().foo, FunctionType))
print(“foo 是方法”, isinstance(Bar().foo, MethodType))
print(“foo2 是函数”, isinstance(foo2, FunctionType))
print(“foo2 是方法”, isinstance(foo2, MethodType))
if name == ‘main‘:
run()
‘’’
foo是函数,False
foo是方法,True
foo2是函数,True
foo2是方法,False
‘’’

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

答:
相同之处:@staticmethod和 @classmethod都可以直接类名.方法名()来调用,不用在示例化一个类。
@classmethod:我们要写一个只在类中运行而不在实例中运行的方法。如果我们想让方法不在实例中运行,可以这么做:
def iget_no_of_instance(ins_obj):
return ins_obj.class.no_inst
class Kls(object):
no_inst = 0
def init(self):
Kls.no_inst = Kls.no_inst + 1
ik1 = Kls()
ik2 = Kls()
print(iget_no_of_instance(ik1))
@staticmethod:经常有一些跟类有关系的功能但在运行时又不需要实例和类参与的情况下需要用到静态方法IND = ‘ON’
class Kls(object):
def init(self, data):
self.data = data
@staticmethod
def check_ind():
return (IND == ‘ON’)
def do_reset(self):
if self.check_ind():
print(‘Reset done for:’, self.data)
def set_db(self):
if self.check_ind():
self.db = ‘New db connection’
print(‘DB connection made for: ‘, self.data)
ik1 = Kls(12)
ik1.do_reset()
ik1.set_db()

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

答: 接口提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数。但
是在Python中根本就没有一个叫做interface的关键字,如果非要去模仿接口的概念,可以使用抽象类来实现。抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
使用abc模块来实现抽象类。

76.Python中的反射了解么?

答:Python的反射机制设定较为简单,一共有四个关键函数分别是getattr、hasattr、setattr、delattr。

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

答: metaclass即元类,metaclass是类似创建类的模板,所有的类都是通过他来create的(调用new),这使得你可以自由的控制创建类的那个过程,实现你所需要的功能。
我们可以使用元类创建单例模式和实现ORM模式。

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

答:这三个方法属于Python的反射机制里面的,
hasattr可以判断一个对象是否含有某个属性,
getattr可以充当get获取对象属性的作用,
而setattr可以充当person.name = “liming”的赋值操作。代码示例如下:
class Person():
def init(self):
self.name = “liming”
self.age = 12
def show(self):
print(self.name)
print(self.age)
def set_name(self):
setattr(Person, “sex”, “男”)
def get_name(self):
print(getattr(self, “name”))
print(getattr(self, “age”))
print(getattr(self, “sex”))

    def run():
        if hasattr(Person, "show"):
            print("判断 Person 类是否含有 show 方法")
            Person().set_name()
            Person().get_name()

    if __name__ == '__main__':
        run()

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

答:

  • 1.init:类的初始化方法。它获取任何传给构造器的参数(比如我们调用x = SomeClass(10, ‘foo’) , __init__就会接到参数10和 ‘foo’ 。
    __init__在Python的类定义中用的最多。
  • 2.new:new__是对象实例化时第一个调用的方法,它只取下cls参数,并把其他参数传给__init
    __new__很少使用,但是也有它适合的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候.
  • 3.del:__new__和__init__是对象的构造器,__del__是对象的销毁器。它并非实现了语句del x(因此该语句不等同于x.del())。
    而是定义了当对象被垃圾回收时的行为。当对象需要在销毁时做一些处理的时候这个方法很有用,比如socket对象、文件对象。
    但是需要注意的是,当Python解释器退出但对象仍然存活的时候,__del__并不会执行。 所以养成一个手工清理的好习惯是很重要的,比如及时关闭连接。

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

答:可以通过type方法

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

答:Python中的传参即不是传值也不是传地址,传的是对象的引用。

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

答:可以使用元类实现一个单例模式,代码如下:
class Singleton(type):
def init(self, *args, **kwargs):
print(“in init“)
self.__instance = None
super(Singleton, self).init(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        print("in __call__")
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance

class Foo(metaclass=Singleton):
    pass  # 在代码执行到这里的时候,元类中的__new__方法和__init__方法其实已经被执行了,而不是在 Foo 实例化的时候执行。且仅会执行一次。

foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2)

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

答:
any(x):判断x对象是否为空对象,如果都为空、0、false,则返回false,如果不都为空、0、false,则返回true。
all(x):如果all(x)参数x对象的所有元素不为0、’’、False或者x为空对象,则返回True,否则返回False。

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

答:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(list(filter(lambda x: x % 2 == 1, a)))
其实现在不推荐使用filter, map等方法了,一般列表生成式就可以搞定了。

85.什么是猴子补丁?

答: 猴子补丁(monkeypatching):在运行时动态修改模块、类或函数,通常是添加功能或修正缺陷。猴子补丁在代码运行时内存中)发挥作用,不会修改源码,因此只对当前运行的程序实例有效。
因为猴子补丁破坏了封装,而且容易导致程序与补丁代码的实现细节紧密耦合,所以被视为临时的变通方案,不是集成代码的推荐方式。大概是下面这样的一个效果
def post():
print(“this is post”)
print(“想不到吧”)
class Http():
@classmethod
def get(self):
print(“this is get”)
def main():
Http.get = post # 动态的修改了 get 原因的功能,
if name == ‘main‘:
main()
Http.get()

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

答:
垃圾回收:Python不像C + +,Java等语言一样,他们可以不用事先声明变量类型而直接对变量进行赋值。对Python语言来讲,对象的类型和内存都是在运行时确定的。
这也是为什么我们称Python语言为动态类型的原因(这里我们把动态类型可以简单的归结为对变量内存地址的分配是在运行时自动判断变量类型并对变量进行赋值。
引用计数:Python采用了类似Windows内核对象一样的方式来对内存进行管理。每一个对象,都维护这一个对指向该对对象的引用的计数。
当变量被绑定在一个对象上的时候,该变量的引用计数就是1,(还有另外一些情况也会导致变量引用计数的增加),系统会自动维护这些标签,并定时扫描,当某标签的引用计数变为0的时候,该对就会被回收。
内存池机制:Python的内存机制以金字塔行,1、2层主要有操作系统进行操作;第0层是C中的malloc,free等内存分配和释放函数进行操作;
第1层和第2层是内存池,有Python的接口函数PyMem_Malloc函数实现,当对象小于256K时有该层直接分配内存;第3层是最上层,也就是我们对Python对象的直接操作在C中如果频繁的调用malloc与free时,
是会产生性能问题的.再加上频繁的分配与释放小块的内存会产生内存碎片。Python在这里主要干的工作有:如果请求分配的内存在1~256字节之间就使用自己的内存管理系统, 否则直接使用malloc。
这里还是会调用malloc分配内存,但每次会分配一块大小为256k的大块内存。经由内存池登记的内存到最后还是会回收到内存池,并不会调用C的free释放掉以便下次使用。
对于简单的Python对象,例如数值、字符串,元组(tuple不允许被更改)采用的是复制的方式(深拷贝?),也就是说当将另一个变量B赋值给变量A时,虽然A和B的内存空间仍然相同,但当A的值发生变化时,会
重新给A分配空间,A和B的地址变得不再相同。

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

答:不是的,循环引用其他对象或引用自全局命名空间的对象的模块,在Python退出时并非完全释放。另外,也不会释放c库保留的内存部分

- 正则表达式

88. (1)使用正则表达式匹配出 < html > < h1\ > www.baidu.com < / h1 > < / html > 中的地址, (2)a = “张明 98 分”,用re.sub,将98替换为100

答:
第一问答案
import re
source = “www.baidu.com"
pat = re.compile(“(.*?)”) print(pat.findall(source)[0])
第二问答案
import re s = “张明 98 分”
print(re.sub(r”\d+”, “100”, s))

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

答:(.)为贪婪模式极可能多的匹配内容, (.?)为非贪婪模式又叫懒惰模式,一般匹配到结果就好,匹配字符的少为主,示例代码如下
import re s = “文本 1 文本 2”
pat1 = re.compile(r”(.?)")
print(pat1.findall(s))
pat2 = re.compile(r”(.
)")
print(pat2.findall(s)) # 输出 # [‘文本 1’, ‘文本 2’] # [‘文本 1 文本 2’]

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

答:关于邮箱的匹配这个还真的是一个永恒的话题。 电子邮件地址有统一的标准格式:用户名 @ 服务器域名。用户名表示邮件信箱、注册名或信件接收者的用户标识,@符号后是你使用