Time will tell.
1、说说Python和其他语言的区别
Python属于解释型语言,当程序运行时,是一行一行的解释,并运行,所以调式代码很方便,开发效率高。
Python定位是任其自由发展、优雅、明确、简单,所以在每个领域都有建树,所有它有着非常强大的第三方库。
特点:
Python的类库齐全并且使用简洁,语法简洁优美,功能强大,标准库与第三方库都非常强大,而且应用领域也非常广。可移植性,可扩展性,可嵌入性。
缺点:
运行速度慢。
区别:
(1)与java相比,在很多方面,Python比Java要简单,比如java中所有变量必须声明才能使用,而Python不需要声明,用少量的代码构建出很多功能;(高效的高级数据结构)。
(2)与php相比,python标准包直接提供了工具,并且相对于PHP代码更易于维护。
(3)Python与c相比:Python 和 C Python这门语言是由C开发而来。
2、Python有什么特点?
- 用少量的代码构建出很多功能(高效的高级数据结构)
- Python 拥有最成熟的程序包资源库之一
- Python完全支持面向对象
- Python 是跨平台且开源的
- 动态类型
3、简述解释型和编译型编程语言
- 解释型:就是边解释边执行(Python,php)
- 编译型:编译后再执行(c、java、c#)
4、Python的解释器种类及相关特点是什么?
CPython
-
当我们从Python官方网站下载并安装好Python 3.6后,我们就直接获得了一个官方版本的解释器:CPython。这个解释器是用C语言开发的,所以叫CPython。在命令行下运行python就是启动CPython解释器。
CPython是使用最广的Python解释器。教程的所有代码也都在CPython下执行。
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的字节码。
Python的解释器有很多,但使用最广泛的还是CPython。如果要和Java或.Net平台交互,最好的办法不是用Jython或IronPython,而是通过网络调用来交互,确保各程序之间的独。
如果你对更多内容、及Python实例练习题、面试题、自动化软件测试感兴趣的话可以加入我们175317069一起学习喔。
5、位和字节的关系
位(bit),1字节 = 8 位。数据存储是以“字节”(Byte)为单位,数据传输是以大多是以“位”(又名“比特”)为单位。
一个位就代表一个0或1(即一个二进制),二进制是构成存储器的最小单位,每8个位(bit,简写为b)组成一个字节(Byte,简写为B),字节是最小一级的信息单位。
6、b、B、KB、MB、GB的关系
b —> 位(bit)
B —> 字节(一个字节等于8位)
1B = 8 bit
1kb = 1024 B
1 MB = 1024 KB
1 GB = 1024 MB
7、PE8规范
- 使用4个空格而不是tab键进行缩进
- 每行长度不能超过79
- 使用空行来间隔函数和类,以及函数内部的大块代码
- 必要时候,在每一行下写注释
- 使用文档注释,写出函数注释
- 在操作符和逗号之后使用空格,但是不要在括号内部使用
- 命名类和函数的时候使用一致的方式,比如使用CamelCase来命名类,使用lower_case_with_underscores来命名函数和方法
- 在类中总是使用self来作为默认
- 尽量不要使用魔法方法
- 默认使用UTF-8,甚至ASCII作为编码方式
- 换行可以使用反斜杠,最好使用圆括号
- 不要在一句import中多个库
8、通过代码实现如下转换(进制之间转换)
# 二进制转换成十进制-->int
v = "0b1111011"
b = int(v,2)
print(b) # 123
# 十进制转换成二进制--->bin
v2 = 18
print(bin(int(v2)))
# 0b10010
# 八进制转换成十进制
v3 = "011"
print(int(v3))
# 11
# 十进制转换成八进制:---> oct
v4 = 30
print(oct(int(v4)))
# 0o36
# 十六进制转换成十进制:
v5 = "0x12"
print(int(v5,16))
# 18
# 十进制转换成十六进制:---> hex
v6 = 87
print(hex(int(v6)))
# 0x57
9、请编写一个函数实现将IP地址转换成一个整数
请编写一个函数实现将IP地址转换成一个整数。
如 10.3.9.12 转换规则为:
10 00001010
3 00000011
9 00001001
12 00001100
再将以上二进制拼接起来计算十进制结果:00001010 00000011 00001001 00001100 = ?
def v1(addr):
# 取每个数
id = [int(x) for x in addr.split(".")]
print(id)
return sum(id[i] << [24, 16, 8, 0][i] for i in range(4))
print(v1("127.0.0.1"))
# [127, 0, 0, 1]
# 2130706433
10、ascii、unicode、utf-8、gbk的区别
python2内容进行编码(默认ascii),而python3对内容进行编码的默认为utf-8
ascii:最多只能用8位来表示(一个字节),即:2**8 = 256,所以,ASCII码最多只能表示 256 个符号。
unicode:万国码,任何一个字符==两个字节
utf-8:万国码的升级版 一个中文字符 == 三个字节 英文是一个字节 欧洲的是 2个字节
gbk:国内版本 一个中文字符==2个字节 英文是一个字节
gbk 转 utf-8 需通过媒介 unicode
11、字节码和机器码的区别
机器码,学名机器语言指令,有时也被称为原生码,是电脑的CPU可直接解读的数据。
字节码是一种中间状态(中间码)的二进制代码(文件)。需要直译器转译后才能成为机器码。
字节码是一种中间状态(中间码)的二进制代码(文件)。需要直译器转译后才能成为机器码。
如果你对更多内容、及Python实例练习题、面试题、自动化软件测试感兴趣的话可以加入我们175317069一起学习喔。
12、三元运算写法和应用场景
应用场景:简化if语句
# 关于三元运算
# 结果+ if + 条件 + else + 结果
result='gt' if 1>3 else 'lt'
print(result) # lt
# 理解:如果条件为真,把if前面的值赋值给变量,否则把else后面的值赋值给变量。
lambda 表达式
temp = lambda x,y:x+y
print(temp(4,10)) # 14
可替代:
def foo(x,y):
return x+y
print(foo(4,10)) # 14
13、Python3和Python2中的 int 和 long 的区别
在python3里,只有一种整数类型int,大多数情况下,和python2中的长整型类似。
14、xrange 和 range 的区别
都在循环时使用,xrange内存性能更好,xrange用法与range完全相同,range一个生成list对象,xrange是生成器。
要生成很大的数字序列的时候,用xrange会比range性能优很多,因为不需要一上来就开辟一块很大的内存空间。
在python2中:
range([start,] stop[, step]),根据start与stop指定的范围以及step设定的步长,生成一个序列
xrange用法与range完全相同,所不同的是生成的不是一个数组,而是一个生成器。
在Python 3中:
- range() 是像 xrange() 那样实现,xrange()被抛弃。
15、文件操作时xreadlines和readlines的区别
readlines:返回一个列表
xreadlines:返回一个生成器
16、 lambda表达式格式以及应用场景是什么?
匿名函数:为了解决那些功能很简单的需求而设计的一句话函数
函数名 = lambda 参数 :返回值
#参数可以有多个,用逗号隔开
#匿名函数不管逻辑多复杂,只能写一行,且逻辑执行结束后的内容就是返回值
#返回值和正常的函数一样可以是任意数据类型
lambda 表达式
temp = lambda x,y:x+y
print(temp(4,10)) # 14
可替代:
def foo(x,y):
return x+y
print(foo(4,10)) # 14
17、说说pass的作用
pass 是空语句,是为了保持程序结构的完整性。pass 不做任何事情,一般用做占位语句。
18、*arg 和 **kwarg 作用
*args代表位置参数,它会接收任意多个参数并把这些参数作为元祖传递给函数。
**kwargs代表的关键字参数,返回的是字典,位置参数一定要放在关键字前面。
19、 is和==的区别
a = 'lishi'
str1 = "li"
str2 = "shi"
str3 = str1 + str2
print("a == str3",a == str3)
print("a is str3",a is str3)
print("id(a)",id(a))
print("id(str3)",id(str3))
# a == str3 True == ---> 只需要内容相等
# a is str3 False is ---> 只需要内存地址相等
# id(a) 38565848
# id(str3) 39110280
is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象,占用的内存地址是否相同。
== 比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了。默认会调用对象的__eq__()
方法。
20、谈谈Python的深浅拷贝,以及实现方法和应用场景
浅拷贝只是增加了一个指针指向一个存在的地址。
而深拷贝是增加一个指针并且开辟了新的内存,这个增加的指针指向这个新的内存,采用浅拷贝的情况,释放内存,会释放同一内存,深拷贝就不会出现释放同一内存的错误。
一层的情况:
import copy
# 浅拷贝
li1 = [1, 2, 3]
li2 = li1.copy()
li1.append(4)
print(li1, li2) # [1, 2, 3, 4] [1, 2, 3]
# 深拷贝
li1 = [1, 2, 3]
li2 = copy.deepcopy(li1)
li1.append(4)
print(li1, li2) # [1, 2, 3, 4] [1, 2, 3]
多层的情况:
import copy
# 浅拷贝 指向共有的地址
li1 = [1, 2, 3,[4,5],6]
li2 = li1.copy()
li1[3].append(7)
print(li1, li2) # [1, 2, 3, [4, 5, 7], 6] [1, 2, 3, [4, 5, 7], 6]
# 深拷贝 重指向
li1 = [1, 2, 3,[4,5],6]
li2 = copy.deepcopy(li1)
li1[3].append(7)
print(li1, li2) # [1, 2, 3, [4, 5, 7], 6] [1, 2, 3, [4, 5], 6]
21、Python垃圾回收机制
引用计数
标记清除
分代回收
21、Python的可变类型和不可变类型?
可变数据类型:列表、字典、可变集合。
不可变数据类型:数字、字符串、元组、不可变集合。
22、 常用模块都有那些?
re模块,os模块,json模块,time模块,爬虫里面的requests/beautifulsoup4(bs4)。
23、re的match和search区别?
re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
re.search 扫描整个字符串并返回第一个成功的匹配。
24、什么是正则的贪婪匹配?
匹配一个字符串没有节制,能匹配多少就去匹配多少,知道没有匹配的为止。
25、def func(a,b=[]) 这种写法有什么问题?
def func(a,b = []):
b.append(1)
print(a,b)
func(a=2)
func(2)
func(2)
'''
2 [1]
2 [1, 1]
2 [1, 1, 1]
函数的默认参数是一个list 当第一次执行的时候实例化了一个list
第二次执行还是用第一次执行的时候实例化的地址存储
所以三次执行的结果就是 [1, 1, 1] 想每次执行只输出[1] ,默认参数应该设置为None
'''
26、如何实现 “1,2,3” 变成 [‘1’,’2’,’3’]?
list("1,2,3".split(','))
27、如何实现[‘1’,’2’,’3’]变成[1,2,3]?
[int(x) for x in ['1','2','3']]
a = ['1','2','3']
b = [int(i) for i in a]
print(b)
# [1, 2, 3]
28、用代码实现删除列表中重复的值
list(set([1, 2, 3, 4, 45, 1, 2, 343, 2, 2]))
29、如何在函数中设置一个全局变量
python中的global语句是被用来声明全局变量的。
x = 2
def func():
global x
x = 1
return x
func()
print(x) # 1
30、logging模块的作用以及应用场景是什么?
logging:模块定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统。
作用:
- 可以了解程序运行情况,是否正常
- 在程序的出现故障快速定位出错地方及故障分析
31、用代码简答实现stack
- Stack() 创建一个新的空栈
- push(item) 添加一个新的元素item到栈顶
- pop() 弹出栈顶元素
- peek() 返回栈顶元素
- is_empty() 判断栈是否为空
- size() 返回栈的元素个数
# 实现一个栈stack,后进先出
class Stack:
def __init__(self):
self.items = []
def is_empty(self):
# 判断是否为空
return self.items == []
def push(self,item):
# 加入元素
self.items.append(item)
def pop(self):
# 弹出元素
return self.items.pop()
def peek(self):
# 返回栈顶元素
return self.items[len(self.items)-1]
def size(self):
# 返回栈的大小
return len(self.items)
if __name__ == "__main__":
stack = Stack()
stack.push("H")
stack.push("E")
stack.push("L")
print(stack.size()) # 3
print(stack.peek()) # L
print(stack.pop()) # L
print(stack.pop()) # E
print(stack.pop()) # H
32、常用字符串格式化哪几种?
1.占位符%
%d 表示那个位置是整数;%f 表示浮点数;%s 表示字符串。
print('Hello,%s' % 'Python')
print('Hello,%d%s%.2f' % (666, 'Python', 9.99)) # 打印:Hello,666Python10.00
2.format
print('{k} is {v}'.format(k='python', v='easy')) # 通过关键字
print('{0} is {1}'.format('python', 'easy')) # 通过关键字
33、简述生成器、迭代器、可迭代对象以及应用场景
迭代器
- 含有iter和next方法 (包含next方法的可迭代对象就是迭代器)
生成器
- 包括含有yield这个关键字,生成器也是迭代器,调动next把函数变成迭代器。
应用场景:
range/xrange
py2: range(1000000) ,会立即创建,xrange(1000000)生成器
py3:range(10000000)生成器
可迭代对象
一个类内部实现iter方法且返回一个迭代器。
应用场景:
- wtforms中对form对象进行循环时候,显示form中包含的所有字段。
class LoginForm(Form):
name = simple.StringField(
label='用户名',
validators=[
validators.DataRequired(message='用户名不能为空.'),
validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
],
widget=widgets.TextInput(),
render_kw={'class': 'form-control'}
)
pwd = simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空.'),
validators.Length(min=8, message='用户名长度必须大于%(min)d'),
validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
form = LoginForm()
for item in form:
print(item)
- 列表、字典、元组
-
装饰器
装饰器:
能够在不修改原函数代码的基础上,在执行前后进行定制操作,闭包函数的一种应用
场景:
- flask路由系统
- flask before_request
- csrf
- django内置认证
- django缓存
# 手写装饰器;
import functools
def wrapper(func):
@functools.wraps(func) #不改变原函数属性
def inner(*args, **kwargs):
执行函数前
return func(*args, **kwargs)
执行函数后
return inner
1. 执行wapper函数,并将被装饰的函数当做参数。 wapper(index)
2. 将第一步的返回值,重新赋值给 新index = wapper(老index)
@wrapper #index=wrapper(index)
def index(x):
return x+100
调用装饰器其实是一个闭包函数,为其他函数添加附加功能,不修改被修改的源代码和不修改被修饰的方式,装饰器的返回值也是一个函数对象。
比如:插入日志、性能测试、事物处理、缓存、权限验证等,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
34、用Python实现一个二分查找的函数
二分查找算法。简单的说,就是将一个列表先排序好,比如按照从小到大的顺序排列好,当给定一个数。
比如3,查找3在列表中的位置时,可以先找到列表中间的数li[middle]和3进行比较,当它比3小时,那么3一定是在列表的右边,反之,则3在列表的左边,比如它比3小,则下次就可以只比较[middle+1, end]的数,继续使用二分法,将它一分为二,直到找到3这个数返回或者列表全部遍历完成(3不在列表中) 。
优点是效率高,时间复杂度为O(logN); 缺点:数据要是有序的,顺序存储。
li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def search(someone, li):
l = -1
h = len(li)
while l + 1 != h:
m = int((l + h) / 2)
if li[m] < someone:
l = m
else:
h = m
p = h
if p >= len(li) or li[p] != someone:
print("元素不存在")
else:
str = "元素索引为%d" % p
print(str)
search(3, li) # 元素索引为2
35、谈谈你对闭包的理解
ef foo():
m=3
n=5
def bar():
a=4
return m+n+a
return bar
>>>bar = foo()
>>>bar()
12
bar在foo函数的代码块中定义。我们称bar是foo的内部函数。 在bar的局部作用域中可以直接访问foo局部作用域中定义的m、n变量。 简单的说,这种内部函数可以使用外部函数变量的行为,就叫闭包。
36、如何用python删除一个文件?
import os
file = r'D:\test.txt'
if os.path.exists(file):
os.remove(file)
print('delete success')
else:
print('no such file:%s' % file)
37、谈谈你对面向对象的理解
三大特性
面对对象是一种编程思想,以类的眼光来来看待事物的一种方式。将有共同的属性和方法的事物封装到同一个类下面。
继承:
- 将多个类的共同属性和方法封装到一个父类下面,然后在用这些类来继承这个类的属性和方法
封装:
-
将有共同的属性和方法封装到同一个类下面
第一层面:创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装。
第二层面:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
多态:
- Python天生是支持多态的。指的是基类的同一个方法在不同的派生类中有着不同的功能。
38、面向对象深度优先和广度优先是什么?
Python的类可以继承多个类,Python的类如果继承了多个类,那么其寻找方法的方式有两种:
- 当类是经典类时,多继承情况下,会按照深度优先方式查找 py3
- 当类是新式类时,多继承情况下,会按照广度优先方式查找 py2
简单点说就是:经典类是纵向查找,新式类是横向查找。
经典类和新式类的区别就是,在声明类的时候,新式类需要加上object关键字。在python3中默认全是新式类。
39、面向对象中super的作用
# 用于子类继承基类的方法
class FooParent(object):
def __init__(self):
self.parent = 'I\'m the parent.'
print('Parent')
print('1111')
def bar(self, message):
print("%s from Parent" % message)
class FooChild(FooParent):
def __init__(self):
# super(FooChild,self) 首先找到 FooChild 的父类(就是类 FooParent),然后把类B的对象 FooChild 转换为类 FooParent 的对象
super(FooChild, self).__init__()
print('Child')
# def bar(self, message):
# # super(FooChild, self).bar(message)
# print('Child bar fuction')
# print(self.parent)
if __name__ == '__main__':
fooChild = FooChild()
fooChild.bar('HelloWorld')
40、functools中的函数作用是什么?
# 用于修复装饰器
import functools
def deco(func):
@functools.wraps(func) # 加在最内层函数正上方
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@deco
def index():
'''哈哈哈哈'''
x = 10
print('from index')
print(index.__name__)
print(index.__doc__)
# 加@functools.wraps
# index
# 哈哈哈哈
# 不加@functools.wraps
# wrapper
# None
41、如何判断是函数还是方法?
看他的调用者是谁,如果是类,就需要传入一个参数self的值,这时他就是一个函数。
如果调用者是对象,就不需要给self传入参数值,这时他就是一个方法。
print(isinstance(obj.func, FunctionType)) # False
print(isinstance(obj.func, MethodType)) # True
class Foo(object):
def __init__(self):
self.name = 'lcg'
def func(self):
print(self.name)
obj = Foo()
print(obj.func) # >
print(Foo.func) #
# ------------------------FunctionType, MethodType------------#
from types import FunctionType, MethodType
obj = Foo()
print(isinstance(obj.func, FunctionType)) # False
print(isinstance(obj.func, MethodType)) # True
print(isinstance(Foo.func, FunctionType)) # True
print(isinstance(Foo.func, MethodType)) # False
# ------------------------------------------------------------#
obj = Foo()
Foo.func(obj) # lcg
obj = Foo()
obj.func() # lcg
"""
注意:
方法,无需传入self参数
函数,必须手动传入self参数
"""
42、静态方法和类方法区别
尽管 classmethod 和 staticmethod 非常相似,但在用法上依然有一些明显的区别。classmethod 必须有一个指向类对象的引用作为第一个参数,而 staticmethod 可以没有任何参数。
例如:
class Num:
# 普通方法:能用Num调用而不能用实例化对象调用
def one():
print ('1')
# 实例方法:能用实例化对象调用而不能用Num调用
def two(self):
print ('2')
# 静态方法:能用Num和实例化对象调用
@staticmethod
def three():
print ('3')
# 类方法:第一个参数cls长什么样不重要,都是指Num类本身,调用时将Num类作为对象隐式地传入方法
@classmethod
def go(cls):
cls.three()
Num.one() #1
#Num.two() #TypeError: two() missing 1 required positional argument: 'self'
Num.three() #3
Num.go() #3
i=Num()
#i.one() #TypeError: one() takes 0 positional arguments but 1 was given
i.two() #2
i.three() #3
i.go() #3
43、什么是反射?以及应用场景是什么?
反射的核心本质就是以字符串的形式去导入个模块,利用字符串的形式去执行函数。
Django中的 CBV就是基于反射实现的。
44、metaclass作用?以及应用场景?
metaclass用来指定类是由谁创建的。
类的metaclass 默认是type。我们也可以指定类的metaclass值。在python3中:
class MyType(type):
def __call__(self, *args, **kwargs):
return 'MyType'
class Foo(object, metaclass=MyType):
def __init__(self):
return 'init'
def __new__(cls, *args, **kwargs):
return cls.__init__(cls)
def __call__(self, *args, **kwargs):
return 'call'
obj = Foo()
print(obj) # MyType
45、实现单例模式的方法
1:使用模块
Python的模块就是天然的单例模式。
因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。
因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
例如:
class V1(object):
def foo(self)
pass
V1 = V1()
将上面代码保存在文件test.py,要使用时,直接在其他文件中导入此文件中的对象,这个对象既是单例模式的对象
如:from a import V1
2:使用装饰器
def Singleton(cls):
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton
@Singleton
class A(object):
a = 1
def __init__(self, x=0):
self.x = x
a1 = A(2)
a2 = A(3)
3:使用类
4:基于__new__方法实现
当我们实例化一个对象时,是先执行了类的__new__方法
当:(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式
46、异常处理写法以及如何主动跑出异常(应用场景)
# 触发异常
def temp_convert(var):
try:
return int(var)
except ValueError as Argument:
print ("参数没有包含数字%s"%Argument)
# 调用函数
temp_convert("xyz")
# 以10为基数的int()的无效文字:“xyz”
----------------------------------------------------------------------------
# raise语法
#raise [Exception [, args [, traceback]]]
# 语句中 Exception 是异常的类型,args 是自已提供的异常参数。
class Networkerror(RuntimeError):
def __init__(self, arg):
self.args = arg
try:
raise Networkerror("Bad hostname")
except Networkerror as e:
print(e.args)
47、isinstance作用以及应用场景
isinstance(对象,类) 判断这个对象是不是这个类或者这个类的子类的实例化
# # 判断a 属不属于A这个类(可以判断到祖宗类)
class A:
pass
class B(A):
pass
a = A()
b = B()
print(isinstance(b,A)) # ===> True 判断到祖宗类
# 任何与object都是True,内部都继承object
class A:pass
a = A() # 实例化
print(isinstance(a,object)) # True
应用场景:rest framework 认证的流程
48、 什么是断言?应用场景是什么?
assert 是的作用?断言
条件成立(布尔值为True)则继续往下,否则跑出异常,一般用于:满足某个条件之后,才能执行,否则应该跑出异常。
写API的时候,继承GenericAPIView
class GenericAPIView(views.APIView):
"""
Base class for all other generic views.
"""
# You'll need to either set these attributes,
# or override `get_queryset()`/`get_serializer_class()`.
# If you are overriding a view method, it is important that you call
# `get_queryset()` instead of accessing the `queryset` property directly,
# as `queryset` will get evaluated only once, and those results are cached
# for all subsequent requests.
queryset = None
serializer_class = None
# If you want to use object lookups other than pk, set 'lookup_field'.
# For more complex lookup requirements override `get_object()`.
lookup_field = 'pk'
lookup_url_kwarg = None
# The filter backend classes to use for queryset filtering
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
# The style to use for queryset pagination.
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
def get_queryset(self):
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)
queryset = self.queryset
if isinstance(queryset, QuerySet):
# Ensure queryset is re-evaluated on each request.
queryset = queryset.all()
return queryset
49、简述 OSI 七层协议
物理层:主要是基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0。
数据链路层:定义了电信号的分组方式。
网路层:引入一套新的地址用来区分不同的广播域/子网,这套地址即网络地址。
传输层:建立端口到端口的通信。
会话层:建立客户端与服务端连接。
表示层:对来自应用层的命令和数据进行解释,按照一定格式传给会话层。如编码、数据格式转换、加密解密、压缩解压 应用层:规定应用程序的数据格式。
50、什么是C/S和B/S架构?
c/s架构,就是client(客户端)与server(服务端)即:客户端与服务端的架构。
b/s架构,就是brosver(浏览器端)与sever(服务端)即:浏览器端与服务端架构
优点:统一了所有应用程序的入口、方便、轻量级。
51、简述 三次握手、四次挥手的流程
三次握手:
-
第一次握手
客户端先向服务端发起一次询问建立连接的请求,并随机生成一个值作为标识。第二次握手
服务端向客户端先回应第一个标识,再重新发一个确认标识。第三次握手
客户端确认标识,建立连接,开始传输数据。
四次挥手:
-
第一次挥手
客户端向服务端发起请求断开连接的请求。第二次挥手
服务端向客户端确认请求。第三次挥手
服务端向客户端发起断开连接请求。第四次挥手
客户端向服务端确认断开请求。
52、TCP和UDP的区别?
TCP/UDP区别
- TCP协议是面向连接,保证高可靠性传输层协议
- UDP:数据丢失,无秩序的传输层协议(qq基于udp协议)
53、为何基于tcp协议的通信比基于udp协议的通信更可靠?
tcp:可靠,因为只要对方回了确认收到信息,才发下一个,如果没收到确认信息就重发。
UDP:不可靠,它是一直发数据,不需要对方回应。
流式协议: TCP协议,可靠传输。
数据报协议: UDP协议,不可传输。
54、什么是socket?简述基于tcp协议的套接字通信流程
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部。
服务端:
创建socket对象,
绑定ip端口bind(),
设置最大链接数listen(),
accept()与客户端的connect()创建双向管道,等到联接,
send(), recv(), 收发数据
close()
客户端:
创建socket对象,
connect()与服务端accept()创建双向管道 ,
send(),
recv(),
close()
55、什么是粘包? socket 中造成粘包的原因是什什么? 哪些情况会发生粘包现象?
只有TCP有粘包现象,UDP永远不会粘包。
粘包:
在获取数据时,出现数据的内容不是本应该接收的数据,如:对方第一次发送hello,第二次发送world,
我方接收时,应该收两次,一次是hello,一次是world,但事实上是一次收到helloworld,一次收到空,这种现象叫粘包
原因:
粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
什么情况会发生:
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
56、IO多路复的作用
socketserver,多个客户端连接,单线程下实现并发效果,就叫多路复用。
与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
57、什么是防火墙以及作用?
防火墙是一个分离器、一个限制器,也是一个分析器,有效地监控了内部网和Internet之间的任何活动,保证了内部网络的安全。
作用:
- 防火墙是网络安全的屏障
- 可以强化网络安全策略
- 对网络存取和访问进行监控审计
- 防止内部信息的外泄
- 除了安全作用,防火墙还支持具有Internet服务特性的企业内部网络技术体系V**(虚拟专用网)
58、简述 进程、线程、协程的区别以及应用场景
线程是指进程内的一个执行单元,
# 进程
进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
# 线程
线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度
# 协程和线程
协程避免了无意义的调度,由此可以提高性能;但同时协程也失去了线程使用多CPU的能力
进程与线程的区别:
(1)地址空间:线程是进程内的一个执行单位,进程内至少有一个线程,他们共享进程的地址空间,而进程有自己独立的地址空间
(2)资源拥有:进程是资源分配和拥有的单位,同一个进程内线程共享进程的资源
(3)线程是处理器调度的基本单位,但进程不是
(4)二者均可并发执行
(5)每个独立的线程有一个程序运行的入口
协程与线程:
(1)一个线程可以有多个协程,一个进程也可以单独拥有多个协程,这样Python中则能使用多核CPU
(2)线程进程都是同步机制,而协程是异步
(3)协程能保留上一次调用时的状态
59、GIL锁是什么?
GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
GIL保护的是解释器级的数据,保护用户自己的数据则需要自己加锁处理
总结:
多线程用于IO密集型,如socket,爬虫,web。
多进程用于计算密集型,如金融分析。
- 每个cpython进程内都有一个GIL
- GIL导致同一进程内多个进程同一时间只能有一个运行
- 之所以有GIL,是因为Cpython的内存管理不是线程安全的
- 对于计算密集型用多进程,多IO密集型用多线程
60、什么是并发和并行?
并发:同一时刻只能处理一个任务,但一个时段内可以对多个任务进行交替处理(一个处理器同时处理多个任务)。
并行:同一时刻可以处理多个任务(多个处理器或者是多核的处理器同时处理多个不同的任务)。
类比:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。
61、解释什么是异步非阻塞
非阻塞:不等待。即:遇到IO阻塞不等待(setblooking=False),(可能会报错->捕捉异常)
- sk=socket.socket()
- sk.setblooking(False)
异步:回调,当达到某个指定的状态之后,自动调用特定函数。
异步体现在回调上,回调就是有消息返回时告知一声儿进程进行处理。非阻塞就是不等待,不需要进程等待下去,继续执行其他操作,不管其他进程的状态。
62、路由器和交换机的区别
1:交换机:是负责内网里面的数据传递(arp协议)根据MAC地址寻址。
路由器:在网络层,路由器根据路由表,寻找该ip的网段。
2:路由器可以处理TCP/IP协议。
3:路由器可以把一个IP分配给很多个主机使用,这些主机对外只表现出一个IP。 交换机可以把很多主机连起来,这些主机对外各有各的IP。
4:交换机是做端口扩展的,也就是让局域网可以连进来更多的电脑。路由器是用来做网络连接,也就是;连接不同的网络。
63、生产者消费者模型应用场景及优势
生产者与消费者模式是通过一个容器来解决生产者与消费者的强耦合关系,生产者与消费者之间不直接进行通讯,而是利用阻塞队列来进行通讯,生产者生成数据后直接丢给阻塞队列,消费者需要数据则从阻塞队列获取。
实际应用中,生产者与消费者模式则主要解决生产者与消费者生产与消费的速率不一致的问题,达到平衡生产者与消费者的处理能力,而阻塞队列则相当于缓冲区。
应用场景:用户提交订单,订单进入引擎的阻塞队列中,由专门的线程从阻塞队列中获取数据并处理
优势:
1、解耦
- 假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。
将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。
2、支持并发
- 生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只能一直等着
而使用这个模型,生产者把制造出来的数据只需要放在缓冲区即可,不需要等待消费者来取
3:支持忙闲不均
- 缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。
当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。
.
.
.
好啦,以上的资料就分享到这里了,如果你对更多内容、及Python实例练习题、面试题、自动化软件测试感兴趣的话可以加入我们175317069一起学习喔。会有各项学习资源发放,更有行业深潜多年的技术人分析讲解。期待你的加入!
欢迎【评论】、【点赞】、【关注】~
Time will tell.(时间会证明一切)