python面试题

Python串讲---面试题

第一部分

1.简述列举了解的编程语言及语言间的区别?

编译型语言:一次性全部编译成二进制码,再去运行

解释型语言:编译一句,运行一句

一、python 解释型 简洁高效,容易上手

二、 java 混合型 (JVM、JIT编译器)学习成本,开发周期慢,web方向偏重

三、 c 编译型 属于底层语言,只有面向过程,没有面向对象

四、c++ 编译型 属于底层语言,既有面向过程,也有面向对象

五、go 编译型 应用在区块链,高并发高可用,游戏方向

 

2.列举Python2和Python3的区别?

python2:

一、print “123”

二、range 返回 list

三、默认编码 --ascii码(无中文)

四、两种类 经典类 和新式类

class Car() : pass 经典类(多继承当中搜索原则以深度优先)

class Car(object) : pass 新式类(多继承当中搜索原则以广度优先)

类.mro() => 继承顺序列表

五、除法:结果是整型

六、int,long(长整型)

七、raw_input => 等价于 python3 input,如光写input 就得输入数字

python3:

一、print()

二、range 返回的是可迭代对象

三、默认编码 --utf-8

四、都是新式类

五、除法:结果是小数(无论整数相除还是浮点数相除)

六、int

七、input

 

3.看代码写结果

v1 = 1 or 2                输出: 1
v2 = 3 and 7 or 9 and 0    输出: 7
​
​
# 逻辑运算符优先级 () > not > and > or
# 逻辑短路
and 全真则真,一假则假
or  一真则真,全假则假
​
True or 表达式  => True  (单个运算符和多个运算符的情况,都可以直接判定结果)
False and 表达式 => False(单个运算符的时候可以)
​
# 布尔值为假的十种情况: bool() => False
   0,0.0,False,0j,'',[],(),set(),{},None
​
v1 = 1 or 2
print(v1) # 1
​
v2 = 3 and 7 or 9 and 0 
# v2 = 7 or  0
# v2 = 7
print(v2)
​
"""
数据类型:
   Number(int , float , complex , bool)
   容器:str list tuple set dict
   
# 复数 : 用在数据分析,人工智能中
complexvar = 3 + 4j
3 : 实数
4j: 虚数
j: 如果有一个数,他的平方等于-1 ,那么这个数就是j,科学家认为有,表达高精度类型
"""
 

4.比较以下值有什么不同?

v1 = [1,2,3]           数字
v2 = [(1),(2),(3)]     数字 括号写些什么类型数据,就是什么数据
v3 = [(1,),(2,),(3,)]  元组
​
​
​
# 逗号才是区分是否是元组的标识符
v1 = [1,2,3] # 列表[int , int , int]
v2 = [(89),(2.12),("abc")] # [int , float , str]
v3 = [(1,),(2,),(3,)] # [tuple,tuple,tuple,tuple]
res = () # 表达空元组
 

5.用一行代码实现数值交换。

# python 特有
a = 1
b = 2
a,b = b,a 
​
# 通用
tmp = a
a = b
b = tmp
​
6.Python中单引号、双引号、三引号的区别?

单双引号没有区别,三引号可以支持跨行
在互相嵌套时需注意:里外不能使用相同的引号
 

7.is和==的区别

is 是判断两边内存地址是否相同
== 判断两边的值是否相同
 

8.python里如何实现tuple和list的转化?

# int float complex bool str list tuple set dict 
list(数据) 
tuple(数据)
 

9.如何实现字符串 name='老男孩'的反转?

# name[start:end:step] 语法
name[::-1]
​
 

10.两个set如何获取交集、并集、差集?

交集 &
intersection
差集 -
difference
并集 |
union
对称差集 ^
symmetric_difference
 

11.那些情况下, y != x - (x-y)会成立?

"""
非空集合且不为子父关系的两个集合
"""
y != x-(x-y)
x = {"a","b","c"}
y = {"b","d"}

if y != x-(x-y):
    print("ok")
 

12.Python中如何拷贝一个对象?

# import copy
# 针对于列表的拷贝,还可以使用[:] , [::],浅拷贝的一种方式

lst1 = [1,2,3]
lst2 = lst1[:]
lst1.append(4)
print(lst2)
 

13.简述 赋值、浅拷贝、深拷贝的区别?

#赋值   : 将变量和值在内存中形成映射指向关系
#浅拷贝 : 只拷贝第一级里所有的元素 copy.copy
#深拷贝 : 为所有层级的元素都单独开辟新空间 copy.deepcopy() (地址:原不可变数据只是暂时的指向,可变的数据独立开辟新空间)
"""
可变数据: list set dict
不可变数据: int float bool complex str tuple
"""

import copy
lst1 = [1,2,3,[4,5,6]]
lst2 = copy.deepcopy(lst1)
lst1[0] = 100
print(lst1)
print(lst2)

print(id(lst1[0]))
print(id(lst2[0]))
 

14.pass的作用?

占位
 

15.阅读代码写结果。

import copy
a = [1,2,4,5,['b','c']]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
a.append(5)
a[4].append('d')

# print(a) [1,2,4,5,['b','c',"d"],5]
# print(b) [1,2,4,5,['b','c',"d"],5]
# print(c) [1,2,4,5,['b','c',"d"]]
 

16.实现9*9乘法表

"""
有行又有列,脑海里瞬间想到两层循环
一层循环控制行
一层循环控制列
"""
i = 1
while i<=9:
    # 这个位置写代码
    j = 1
    while j<= i:
        # "谁"*"谁"="谁"
        print("%d*%d=%2d" % (i,j,i*j),end=" ")
        j+=1
        
    # 打印换行
    print()    
    i+=1


for i in range(1,10):
    for j in range(1,i+1):
        print("%d*%d=%2d" % (i,j,i*j),end=" ")
    print()    
    
    
    
 for i in range(1, 10):
    for j in range(1, i+1):
        print(f'{j}x{i}={i*j}',end=' ')
    print()
    
1x1=1 
1x2=2 2x2=4 
1x3=3 2x3=6 3x3=9 
1x4=4 2x4=8 3x4=12 4x4=16 
1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 
1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 
1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 
1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 
1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81    
 

17.用Python显示一个斐波那契数列。

# 1 1 2 3 5 8 13 21 ...
# 方法一
lst = [1,1]
for i in range(10):
    lst.append(lst[-1] + lst[-2])
print(lst)

# 方法二
a,b = 0,1
for i in range(10):
    print(b)
    a,b = b,a+b

# 方法三
def fib(n):
    if n <= 2:
        return 1
    # 上一个值 + 上上个值
    return fib(n-1) + fib(n-2)

print(fib(6))
 

18.如何删除列表中重复的值?

list(set(lst))
 

19.一个大小为100G的文件etl_log.txt, 要读取文件中的内容, 写出具体过程代码?

fp = open("文件名","模式","编码集")
"""
fp 是迭代器
from collections import Iterator,Iterable

# 在遍历fp时,文件按照一行一行进行读取;
for i in fp:
    code ... 

"""
 

20.a = dict(zip(("a","b","c","d","e"),(1,2,3,4,5))) 请问a是什么?

强转字典的条件:等长的二级容器,配合强转字典的两个函数 zip , enumerate
# zip  拉链
a = dict( zip( ("a","b") , [1,2] ) )
print(a)
# enumerate  枚举
a = dict( enumerate( ["a","b"] ))
a = dict( enumerate( ["a","b"] ,start = 10 ))
print(a)
 

21.lambda关键字的作用?

lambda 匿名函数 : 用一句话表达只有返回值的无名函数
lambda 参数 : 返回值
 

22.*arg和**kwarg`作用?

*arg **kwarg

# *arg   普通收集参数 :   收集多余的没人要的普通实参
# **kwarg 关键字收集参数: 收集多余的没人要的关键字实参
 

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

"""
global  有该全局变量,修改当前变量,没有改全局变量,定义一个全局变量;
"""
def func():
    global a
    a = 90
func()
print(a)
 

24.filter、map、reduce的作用?

"""
三目(元)运算符  True   if 条件表达式 else False

filter => 过滤数据
iterable : 可迭代对象(range ,容器类型数据 , 迭代器)
filter(func,iterable)  => 返回迭代器

lst = [1,2,3,4,5]
it = filter(lambda x : True   if x % 2 == 0 else False , lst )
print(list(it))

"""


# map -> 处理(映射)数据
map(func,iterable) => 返回迭代器

lst = [1,2,3]
it = map(lambda x : x*3 , lst)
print(list(it))

# reduce -> 计算数据
from functools import reduce
# reduce(func,iterable) => 最后计算的值
# [5,4,8,8] => 5488
lst = [5,4,8,8]
res = reduce(lambda x,y : x*10 + y ,lst )
print(res , type(res))

 

25.什么是匿名函数?匿名函数有什么作用?

lambda 匿名函数 : 用一句话表达只有返回值的无名函数
lambda 参数 : 返回值
 

26.Python递归的最大层数?

官方说法1000 , 实际测试 994 ~ 1000
import sys
sys.setrecursionlimit(999999) # 修改递归的最大深度
 

27.什么是迭代器?什么是可迭代对象?

# 具有__iter__() 和 __next__()这两个方法的是迭代器
# 具有__iter__()方法就是可迭代对象
# dir(数据) 可以查看该数据的内部系统成员
# 可迭代对象 => 迭代器  把不能直接被next获取 => 可直接获取到该数据的一个过程
 

28.什么是生成器?

生成器的本质就是迭代器,可以自定义迭代的逻辑
创建方式两种:
    (1)生成器表达式 (推导式)  (i for i in range(3))
    (2)生成器函数   (含有yield关键字)
 

29.什么是装饰器及应用场景?

# 装饰器的本质就是闭包
# 在不修改原有代码的前提下,额外增加新功能就是装饰器
# 应用:登录认证,property类,框架(django,flask,@app.route("/",methdos=["GET","POST"]))
 

30.什么是反射及应用场景?

# 通过字符串去操作类对象 或者 模块中的属性方法
hasattr getattr setattr delattr
应用: 可以配合用户的操作或者输入,调用其中的成员,api接口中
 

31.写一个普通的装饰器。

闭包:内函数使用了外函数的局部变量,外函数把内函数返回出来的过程叫做闭包
这个内函数叫做闭包函数;
特点:如果内函数使用了外函数的局部变量,那么该变量于内函数发生绑定,延长该变量的生命周期
def wrapper(func):
    def inner(*args,**kwargs):
        res = func(*args,**kwargs)
        print("and you")
        return res
        
    return inner

@wrapper
def func():
    print("i am fine 3q")

func()
 

 

32.写一个带参数的装饰器。

def outer(n):
    def wrapper(func):
        def inner1(*args,**kwargs):
            res = func(*args,**kwargs)
            print("我是大王")
            return res
            
        def inner2(*args,**kwargs):
            res = func(*args,**kwargs)
            print("大王叫我来巡山")
            return res
        
        if n == "alex":
            return inner1
        else:
            return inner2
            
    return wrapper


@outer("alex123") # outer("alex123") => wrapper =>@wrapper
def func():
    print("i am fine 3q")
    
func()
 

33.求结果

def num():
    return [lambda x:i*x for i in range(4)]
print([m(2) for m in num()])

"""
def出现的位置是函数的定义处
函数() 出现的位置是函数的调用处
(1)调用的时候,才会把函数中的代码,从上到下执行一遍,否则不执行
(2)里面的func是一个闭包函数,延长了当前变量i的生命周期,最后一次i的值3,所以再去调用时候拿的3
"""


def num():
    lst = []
    for i in range(4):
        def func(x):
            return i*x
        lst.append(func)
    return lst
    
lst = num()
print(lst)


lst = [ m(2)   for m in num()  ]
lst = [ m(2)   for m in lst  ]
lst = [ i*x   for m in lst  ]
lst = [3 * 2 for m in lst]
lst = [6,6,6,6]


"""
[
<function func at 0x000001A02CA642F0>, 
<function func at 0x000001A02CA64378>, 
<function func at 0x000001A02CA646A8>, 
<function func at 0x000001A02CA64730>
]
def func():
    print(1)

for i in range(3):
    print(i)
    
"""
 

 

34.def(a, b=[])这种写法有什么陷阱?

b身上的默认值是列表,如果使用原来默认的参数,调用func函数
会把几次调用的值都存放在同一个默认列表里
"""
默认参数: 
    如果调用时,用户给实参了,那么使用用户的
    如果调用时,用户没给实参,那么使用默认的(早已存在内存中的这个列表)
    
默认值会提前在内存中驻留,在使用时,才能调取,在定义函数的时候就提前开辟了空间
"""
 

35.看代码写结果

def func(a,b=[]):
    b.append(a)
    return b
v1 = func(1)
v2 = func(2,[10,20])
v3 = func(3)
print(v1,v2,v3) # [1,3]  [10, 20, 2]  [1, 3]
36.看代码写结果

def func(a,b=[]):
    b.append(a)
    return b
v1 = func(1)
print(v1) # [1]
v2 = func(2,[10,20])
print(v2) # [10, 20, 2] 
v3 = func(3)
print(v3) # [1, 3]
37.请编写一个函数实现将IP地址转换成一个整数。

# ljust   原字符串居左,填充符号
# rjust   原字符串居右,填充符号
# 方法一
ip = "10.3.9.12"
strvar = ""
for i in ip.split("."):
    bin_str = str(bin(int(i)))[2:]
    # 总长度是8 原字符串居右
    strvar += bin_str.rjust(8,"0")
print(strvar)
    
# 把二进制字符串转换成十进制,默认转换时,是十进制
print(int(strvar,2))

# 方法二
ip = "10.3.9.12"
strvar = ""
for i in ip.split("."):
    # format 将整型转化成二进制,不够8位的拿0补位
    strvar += format(int(i) , "08b")
print(int(strvar,2))



38.请查找一个目录下的所有文件(可能存在文件嵌套)。

# 方法一 (递归写法)
import os
def getallsize(pathvar):
size = 0
lst = os.listdir(pathvar)
print(lst)
for i in lst:
pathvar2 = os.path.join(pathvar,i)
print(pathvar2)

# 判断是否是文件
if os.path.isfile(pathvar2):
size += os.path.getsize(pathvar2)
# 判断是否是文件夹
elif os.path.isdir(pathvar2):
size += getallsize(pathvar2)

print(size)

return size

# "E:\串讲基础\day2\test\1.txt"
pathvar = r"E:\串讲基础\day2\test"
res = getallsize(pathvar)
# print(res)

# 方法二
import os
# os.walk() => 生成器
pathvar = r"E:\串讲基础\day2\test"
gen = os.walk(pathvar)

for root,dirs,files in gen:
for name in files:
pathvar = os.path.join(root,name)
print(pathvar)

39.求结果

import math
print (math.floor(5.5))

math.floor就相当于是round,在.前面是奇数就进一位,偶数就不进位
# floor ceil round
import math
print(math.floor(5.5))

# round n.5 奇进偶不进
print(round(4.5))
print(round(5.5))
print(round(4.52))

40.是否使用过functools中的函数?其作用是什么?

from functools import reduce
# 在装饰器中使用,如果想要保留原来函数的属性,加上wraps
from functools import wraps

def wrapper(func):
@wraps(func)
def inner(*args,**kwargs):
res = func(*args,**kwargs)
print("and you")
return res

return inner

@wrapper
def func():
print("i am fine 3q")

func()
print(func)

# def abc():
# pass
# print(abc)

 

41.re的match和search区别?

"""
match : 必须从字符串的开头进行匹配
search: 从任意位置开始匹配,匹配到就返回
只匹配一个
"""

 

42.用Python匹配HTML tag的时候,<.>和<.?>有什么区别?

. 除了\n的任意字符
* 量词,代表匹配0次或者多次,任意个
.*  贪婪匹配
.*? 非贪婪匹配

 

43.如何生成一个随机数?

import random
random.random    随机获取 0<= x < 1
random.randrange 随机获取指定范围中的整数,用法上同range
random.uniform   随机获取指定范围中的小数

 

44.super的作用?

# 用来解决多继承之间复杂的调用关系使用super
在多继承中,如果出现了多个同名方法
super在调用的时候,会按照mro列表的继承顺序依次调用
类.mro() = > lst

 

45.双下划线和单下划线的区别?

class MyClass():
__abc = 90
_ppp = 100
"""
封装: 公有public 私有private 受保护的protected
私有: 只能在当前这个类里面使用,不能再子类或者在类外使用
受保护的: 可以在当前这个类或者子类里使用,不能再类外使用
约定俗成在该变量前面加上一个下划线_ , 就表示受保护了
"""

 

46.@staticmethod和@classmethod的区别?

一个静态方法,一个类方法
一个静态方法:(无论是对象还是类,都可以调用,不会默认传递任何参数)
一个类方法 :(无论是对象还是类,都可以调用,会默认传递类这个参数)

 

47.实现一个单例模式(加锁)。

# 单态模式:这个类无论实例化多少次,都有且只有一个对象
from threading import Lock
class MyClass(object):
__obj = None
lock = Lock()
def __new__(cls,*args,**kwargs):
with cls.lock:
if not cls.__obj:
cls.__obj = object.__new__(cls)
return cls.__obj
obj1 = MyClass()
obj2 = MyClass()
print(obj1,obj2)

 

48.栈和队列的区别?

栈 :  先进后出,或者 后进先出
队列: 先进先出

 

49.以下代码输出是什么? 请给出答案并解释。

class Parent(object):
x = 1
class Child1(Parent):
pass
class Child2(Parent):
pass
# 1 1 1
print(Parent.x, Child1.x, Child2.x)
Child1.x = 2
# 1 2 1
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
# 3 2 3
print(Parent.x, Child1.x, Child2.x)

50.参考下面代码片段

#代码片段:
   class Context:
   pass

with Content() as ctx:
   ctx.do_something()
请在Context类下添加代码完成该类的实现


# 面向对象的上下文管理是with语法的具体实现
class Context():
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# 相当于在最后,执行了文件的关闭操作,fp.close()
print("abc123")
def do_something(self):
print(1111)

with Context() as ctx:
ctx.do_something()
print(ctx)

# 自动实现了关闭操作
# with open("文件") as fp:
# res = fp.read()


 

 

第二部分 可选题

  1. 如何获取列表中第二大的值?

    # (1) 所有的容器类型数据都可以通过 sorted (sort只局限于列表进行排序)
    # 去重
    lst = set([98,1,100,3,-100,50,100,100])
    res = sorted(lst)
    res_new = res[-2]
    print(res_new)

     

  2. 简述Python内存管理机制。

    # (2) 内存管理机制
    计数器,垃圾回收,内存池
    # 一.计数器
    特点:引用技术如果是0,把这个值从内存中释放掉
    a = 100
    b = a
    print(b)
    del b
    缺点:在维护引用计数时,又可能数据产生循环引用,造成数据不能删除,造成内存泄漏
    lst1 = [1,2]
    lst2 = [5,6]
    lst1.append(lst2)
    lst2.append(lst1)
    del lst1
    print(lst1)
    print(lst2)
    # print(lst1)
    # print(lst2)

     

  3. 简述Python的垃圾回收机制。

    # 二.垃圾回收:引用计数为主,标记清除和分带回收为辅
    标记清除 : 检测标记该对象,避免出现循环引用不能删除的现象
    分带回收 :
    把内存中的数据分成三个区域: 新生代0,老年代1,永久代2
    新生代0数据超过700 , 或者老年代1,永久代2数据超过10,自动触发内存中的垃圾回收机制
    新生代0触发将清除所有三代的区域
    老年代1触发会清理1,2
    永久代2触发只会清理自己


    # 三.内存池
    # 在同一个文件当中 (python3.6)
    # -->Number 部分
    1.对于整型而言,-5~正无穷范围内的相同值 id一致
    2.对于浮点数而言,非负数范围内的相同值 id一致
    3.布尔值而言,值相同情况下,id一致
    4.复数在 实数+虚数 这样的结构中永不相同(只有虚数的情况例外)
    # -->容器类型部分
    5.字符串 空元组 相同的情况下,地址相同
    6.列表,元组,字典,集合无论什么情况 id标识都不同 [空元组例外]
    # 在不同的文件当中
    小数据池 ; 比如整型默认开辟 -5~256 这么多数据提前在内存中驻留
  4. 请用两个队列来实现一个栈。

    """
    栈   : 先进后出,后进先出
    队列 : 先进先出,后进后出
    """
    from queue import Queue

    class Stack():
    def __init__(self):
    self.master_queue = Queue()
    self.minor_queue = Queue()

    def push(self,val):
    # 入栈
    self.master_queue.put(val)

    def pop(self):
    # 出栈
    # 如果队列中没有任何值,直接返回None
    if self.master_queue.qsize() == 0 :
    return None

    while True:
    # 当队列总长度为1的时候,循环终止,把最后一个元素拿出来,为了满足栈后进先出的特点
    if self.master_queue.qsize() == 1:
    value = self.master_queue.get()
    break

    # 剩下还没有拿出来的元素,暂时放在2号队列中存储
    self.minor_queue.put(self.master_queue.get())
    """
    minor_queue(1)
    master_queue(2 3 4)

    minor_queue(2)
    master_queue(3 4)

    minor_queue(3)
    master_queue(4)
    """
    # 交换队列,重新循环,继续去最后一个值,如法炮制
    self.master_queue,self.minor_queue = self.minor_queue,self.master_queue
    return value

    obj = Stack()
    obj.push("a")
    obj.push("b")
    obj.push("c")

    print(obj.pop()) # c
    print(obj.pop()) # b
    print(obj.pop()) # a
    print(obj.pop()) # a



    [a,b,c]
    [a,b]
    [a]
    []

     

  5. 请用Python实现一个链表。

    # 线性表: 相当于一条直线,没有分支

    # ### (1) 创建链表
    class Node():
    def __init__(self, value, next):
    self.value = value
    self.next = next


    head = Node("头", None)
    last = head

    for i in range(5):  # v0 v1 v2 v3 v4
    node = Node("v%s" % i, None)
    last.next = node
    last = node

    # 查看链表的关系
    print(head.value)
    print(head.next.value)
    print(head.next.next.value)
    print(head.next.next.next.value)
    print(head.next.next.next.next.value)
    print(head.next.next.next.next.next.value)


    # print(head.next)
    print("<========>")

     

  6. 请用Python实现链表的逆转。

# 2.链表的逆转
def reverse_link_list(head):
# 要是空的,或者None,直接返回head
if not head or not head.next:
return head

# 获取上一个节点对象
prev_node = None
# 获取下一个节点对象
next_node = head.next
# 获取当前节点对象
current_node = head

while True:
# 修改next,所指向的对象
current_node.next = prev_node
# 如果下一个阶段对象是None
if not next_node:  # not None
break

# 重新获取上一个对象,即把当前丢向单独存一份,以准备第二次循环时插进next属性中
prev_node = current_node
# 重新获取当前对象 , 即把下一个对象单独存储起来(下个)
current_node = next_node
# 重新获取下一个对象,即把下一个对象单独存储起来,所指向的下个新对象赋值给next_node(下下个)
next_node = current_node.next
return current_node


head = reverse_link_list(head)

print(head.value)
print(head.next.value)
print(head.next.next.value)
print(head.next.next.next.value)
print(head.next.next.next.next.value)
print(head.next.next.next.next.next.value)

 

 

 

 

网络并发

 

Python面试重点(进阶篇)

1.简述 OSI 7层模型及其作用?

应用层(应用层,表示层,会话层)
在应用层中封装实际的消息数据(HTTP,HTTPS,FTP)
传输层:
封装端口 指定传输的协议(TCP/UDP)
网络层:
封装ip   版本ipv4 / ipv6
数据链路层:
封装mac地址 指定链路层协议: arp(通过ip->mac)/rarp(通过mac->ip)
物理层:
打成数据包,变成二进制的字节流通过网络进行传输

 

 

 

 

 

2.简述 TCP三次握手、四次回收的流程。

SYN 创建连接
ACK 确认响应
FIN 断开连接

# 三次握手
客户端发送一个消息,请求建立连接
服务端接受客户端的响应,并且发出与客户端建立连接的请求
客户端接受服务端响应,回应服务端请求
接下来就可以发送数据 ...

# 四次挥手
客户端发送一个消息,请求断开连接
服务端接受客户端响应,回应请求
等到所有数据收发完毕之后
服务端发送断开连接的请求
客户端接受服务端响应,回应请求
等到2msl 最大报文生存时间过后
客户端和服务端彻底断凯连接


 

 

 

 

 

 

3.TCP和UDP的区别?(3分)

tcp 需要建立连接,传输可靠,速度慢,消息面向字节流无边界
udp 不需要建立连接,可靠性差,速度快,消息面向数据报(报文)有边界

4.什么是黏包?(2分)

    # 黏包:
tcp协议数据因为无边界的特点,导致都填分开发送的数据粘合在一起变成了一条数据

# 现象:
# 情况1:
在发送端,数据小,时间间隔短,容易几个数据粘合在一起
# 情况2:
在接受端,接受数据慢,在缓存区,导致几个数据粘合在了一起

# 解决:
使用struct:
# pack (数据长度在21个亿左右)
"""把任意长度的数字转换成具有4个字节固定长度的字节流"""
res = struct.pack("i",2100000000) #代表当前转化的数据是整型
# unpack
"""把4个字节值恢复成原来的数据,返回的是一个元组"""
tup = struct.unpack("i",res)[0] # 把rev转换成整型int

思路方面:
计算接下来要发送的数据大小是多少
通过pack转化固定4个字节发送给接受段
然后在发送真实数据
接受段需要接受2次,第一次接受转换成的真实数据大小,放recv参数中
第二次在接受真实的数据,才能保证不黏包

场景:
用在及时通讯类中,如果是上传下载不需要.

 

5.什么 B/S 和 C/S 架构?(2分)

网络开发的两大架构:
c/s : client server  软件
b/s : Brower server  网站,小程序

6.请实现一个简单的socket编程(客户端和服务端可以进行收发消息)(3分)

# 一.TCP 服务端
  import socket
  # 1.创建一个socket对象
  sk = socket.socket()
  # 2.绑定ip和端口(注册网络)
  sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  sk.bind( ("127.0.0.1",9000) )
  # 3.开启监听
  sk.listen()
  # 4.建立三次握手
  conn,addr = sk.accept()
  # 5.处理收发数据逻辑
  # 接受数据
  msg = conn.recv(1024)
  msg.decode("utf-8")
  # 发送数据
  conn.send(b"abc")
  conn.send("我好帅哦".encode())
  # 6.四次挥手
  conn.close()
  # 7.退还端口
  sk.close()
     
# 二.TCP 客户端
  # 1.创建一个socket对象
  sk = socket.socket()
  # 2.与服务器进行连接
  sk.connect( ("127.0.0.1",9000) )
  # 3.收发数据的逻辑
  # 发送
  sk.send(b"abc")
  # 接受
  sk.recv(1024)
  # 4.关闭链接
  sk.close()

# 三.TCP / socketserver 支持TCP的并发操作
import socketserver
class MyServer(socketserver.BaseRequestHandler):
  def handle(self):
     conn = self.request

if __name__ == "__main__":
  server = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) , MyServer )
  server.serve_forever()

# 四.UDP服务端
import socket
# 1.创建一个socket对象
sk = socket.socket(type=socket.SOCK_DGRAM)
# 2.绑定地址
sk.bind( ("127.0.0.1",9000) )
# 3.处理收发数据的逻辑(服务器一定第一次是接受数据)
# 接受
msg , cli_addr = sk.recvfrom(1024)
# 发送
sk.sendto(b"abc" , cli_addr)

# 4.关闭udp连接
sk.close()


# 五.UDP客户端
# 1.创建一个socket对象
sk = socket.socket(type=socket.SOCK_DGRAM)
# 2.收发数据
sk.sendto("你好".encode("utf-8") , ("127.0.0.1",9000) )
sk.recvfrom(1024)
# 3.关闭udp连接
sk.close()

"""
  最大的网络传输数据包大小 (MTU 1500Byte)
  一般路由器网络转发数据的数据包大小不超过1500B
  超过这个范围,该数据会进行拆包和打包的过程
"""

7.简述进程、线程、协程的区别?(3分)

进程:资源分配的最小单位,进程之间的数据彼此隔离,可以并发并行
  from multiprocessing import Process
线程:程序调度的最小单位,进程里面包含线程,共享同一份进程资源,只能并发(GIL锁)
  from threading import Thread
协程:实现单线程在多任务之间的自由切换,是线程执行任务的一种方式
  import gevent;from gevent import monkey
  monkey.pathch_all() # 识别所有模块中的阻塞
  g2 = gevent.spawn(play)

8.什么是GIL锁?(2分)

并发:同一时间,一个cpu执行多个任务
并行:同一时间,多个cpu执行多个任务
GIL:全局解释器锁,为了保证数据安全,只让多线程并发,不能并行
在后台一个个的程序都是由一个个的cpython解释器执行的,每个解释器运行的程序都是单独的进程
但是同一时间,程序中的多个线程只能由一个cpu执行

解决办法:
  1.换个jpython等其他解释器,又可能出现兼容性问题
  2.用多进程的方式间接实现多线程,资源开销较大
  历史遗留问题,无法彻底解决

9.进程之间如何进行通信?(2分)

IPC : 
  1.管道Pipe(进程和进程之间只能单向通信)(了解)
  2.Queue(进程和进程之间可以双向通信)
  3.文件 (共享数据)

q = Queue(3)
put get
put_nowait get_nowait (linux有兼容性问题)
empty full qsize(队列长度)

10.Python如何使用线程池、进程池?(2分)

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
# (1)创建进程池/线程池对象 8个
  p = ProcessPoolExecutor() # 参数:cpu的逻辑处理器数量
  p = ThreadPoolExecutor()  # 参数:cpu的逻辑处理器数量 * 5
# (2)提交异步任务submit
  res = p.submit(func,参数1,参数2,...)
# (3)获取返回值 result (里面有阻塞)
  res_new = res.result()
# (4)等待所有子进程执行完毕 shutdown
  p.shutdown()
  print("主进程执行结束 .. ")

11.请通过yield关键字实现一个协程? (2分)

"""
# 创建生成器
(1) 生成器表达式: gen = (i for i in range(10))
(2) 生成器函数 : 函数内含有yield,需要初始化才能使用
"""
def producer():
  for i in range(100):
     n = yield i
     print("结果:%s",n)

def consumer():
  # 生成器函数的初始化
  g = producer()
  # send可以类比next,但是第一次调用时,必须给None,send可以给yield发送数据(上一个yield)
  g.send(None)
  for i in range(10):
     res = g.send(i)
     print(res)
consumer()

12.什么是异步非阻塞? (2分)

同步:
  代码从上到下按照顺序,依次执行
异步:
  无需等待当前程序中的代码是否执行完毕,
  该代码又开启另外一个进程/线程中执行

阻塞   : input , time , sleep , recv ...
非阻塞 : 依次执行,无需等待

异步非阻塞:
  场景发生在多进程/多线程之间
  没有任何io阻塞或者等待,同时执行
  设置setblocking(False) (设置非阻塞 了解)

13.什么是死锁?如何避免?(2分)

from threading import Lock
互斥锁,死锁,递归锁
只上锁不解锁是死锁
例如:
  lock = Lock()
  # 1
  lock.acquire()
  lock.acquire()
  lock.acquire()
 
  # 2
  进程1
  a.acquire()
  b.acquire()
  b.release()
  a.release()
 
  进程2
  b.acquire()
  a.acquire()
  a.release()
  b.release()
  进程1 拿着A锁抢B锁
  进程2 拿着B锁抢A锁

# 使用递归锁,快速应急,解决服务器死锁问题
  a = b = RLock()
 
  a.acquire()
  a.acquire()
  a.acquire()
 
  a.release()
  a.release()
  a.release()
多次上锁的次数和多次解锁的次数相同,就能达到解锁的目的;
以后使用锁时,尽力不用锁嵌套;

14.程序从flag a执行到falg b的时间大致是多少秒?(2分)

import threading
import time
def _wait():
time.sleep(60)
# flag a
t = threading.Thread(target=_wait)
t.setDeamon(False)
t.start()
# flag b

#答案:0~1秒之间

守护进程: 守护的是主进程
守护线程: 守护的是所有线程;

# 0~1秒

15.程序从flag a执行到falg b的时间大致是多少秒?(2分)

import threading
import time
def _wait():
time.sleep(60)
# flag a
t = threading.Thread(target=_wait)
t.setDeamon(True)
t.start()
# flag b

0~1秒之间

16.程序从flag a执行到falg b的时间大致是多少秒?(2分)

#import threading
import time
def _wait():
time.sleep(60)
# flag a
t = threading.Thread(target=_wait)
t.start()
t.join()
# flag b

#60秒

17.读程序,请确认执行到最后number是否一定为0(2分)

import threading
loop = int(1E7)  #1乘以10的7次幂
def _add(loop:int = 1) --> int: #建议loop这个形参用整型,并不强制,默认是1,--> int是让这个函数的返回值也尽量是整形
global number
for _ in range(loop):
number += 1
def _sub(loop:int = 1):
global number
for _ in range(loop):
number -= 1
number = 0
ta = threading.Thread(target=_add,args=(loop,))
ts = threading.Thread(target=_sub,args=(loop,))
ta.start()
ta.join()
ts.start()
ts.join()
#答案:0

# 类型注解 python3.6版本以上
"""
def add(x:int,y:int) -> int:
return x+y
res = add(10,20)
print(res)
"""

18.读程序,请确认执行到最后number是否一定为0(2分)

import threading
loop = int(1E7)
def _add(loop:int = 1):
global number
for _ in range(loop):
number += 1
def _sub(loop:int = 1):
global number
for _ in range(loop):
number -= 1
number = 0
ta = threading.Thread(target=_add,args=(loop,))
ts = threading.Thread(target=_sub,args=(loop,))
ta.start()
ts.start()
ta.join()
ts.join()

#任何值都可能,俩并行同时抢数

19..MySQL常见数据库引擎及区别?(3分)

myisam : 5.5之前的默认存储引擎 , 只支持表级锁(读写互相阻塞)
innodb : 5.5版本之后,默认的存储引擎,支持事务,行级锁,外键,能够抗住更大的并发量(全表扫描,存在表级锁)
memory : 把数据存储在内存里,一般做缓存
blackhole : 黑洞,用来同步数据,应该在主从数据库当中

20..简述事务及其特性? (3分)

A.原子性:
  同一个事务当中可能执行多条sql语句,要么全部成功,要么直接回滚,这个过程看成一个整体,一个不能再分割的最小个体
C.一致性:
  a,i,d 都是为了保证数据的一致性提出来的
  比如必须按照约束要求插入数据,保证每跳数据类型的一致性
  事务角度上,防止脏读,幻读,不可重读,最终决定当前客户端和当前的数据库状态一致
I.隔离性:
  lock + isolation锁,来处理事务的隔离级别;
  一个事务和另外一个事务在工作过程中彼此隔离独立
  如果同时更改同一个数据,因为锁机制的存在,先执行的先改,其他事务需要等待,保证数据安全
D.持久性:
  把数据写在磁盘上,保证数据的持久化存储;

21.事务的隔离级别?(2分)

脏读: 没提交的数据读出来的 (查)
不可重读: 前后多次读取,数据内容不一样(同一个会话中,在不进行修改或者删除的时候,永远看到的是同一套数据)
幻读:前后多次读取,数据内容不一样(从添加的角度上说的)

# 开始事务
begin:
# 处理sql
# commit 提交数据
# rollback 回滚数据

# 数据的隔离级别

RU(READ_UNCOMMITTED) : 读未提交 : 脏读,不可重读,幻读
RC(READ_COMMITTED)   : 读已提交 : 防止脏读,会出现不可重复还有幻读
RR(REPEATABLE_READ) : 可重复读 : 防止脏读,不可重复读,可能会出现幻读(默认隔离级别)
SR(SERLALIZABLE)     : 可序列化 : 什么都能防止(多个窗口同步,不能并发,性能差)


# 查看默认的隔离级别
select @@tx_isolation
# 查询是否自动提交数据
select @@autocommit
# 找到my.ini 配置文件
autocommit=0 # 关闭自动提交数据
transaction_isolation = READ_UNCOMMITTED # 设置隔离级别

# 打开窗口1
begin;
update t1 set name = "abc" where id = 1
# commit;

# 打开窗口2
select * from t1;

22.char和varchar的区别?(2分)

char    定长,速度快
varchar 变长,速度慢,节省空间(内容的开头会有1~2个字节存储数据长度)

23.mysql中varchar与char的区别以及varchar(50)中的50代表的含义。(2分)

varchar(50) 最多存50个字符
(字符长度如果小于255个,前头用1个字节存长度
字符长度如果大于255个,前头用2个字节存长度
1111 1111 => 255
1111 1111 1111 1111 => 65535
255 ~ 65535 字符长度
)

24.MySQL中delete和truncate的区别?(2分)

delete 删除数据
truncate 重置表(删除数据+重置自增id)

25.where子句中有a,b,c三个查询条件, 创建一个组合索引abc(a,b,c),以下哪种会命中索引(3分)

#最左前缀,因为a在最左边,所以有a就命中
(a)     命中
(b)     不行
(c)     不行
(a,b)   命中
(b,c)   不行
(a,c)   命中
(a,b,c) 命中

26.组合索引遵循什么原则才能命中索引?(2分)

最左前缀原则,条件不能使用范围,可以使用and
# where a>1 and b=1 and c = 100 不能命中
# where b=1 and c = 100 or a = 10 不能命中

27.列举MySQL常见的函数? (3分)

count 
avg
sum
max
min
now()
concat
concat_ws
user => select user()
databases => select databases()
group_concat
year(),month,day(),hour,minute,second week...
password

28.MySQL数据库 导入、导出命令有哪些? (2分)

# 导出 (\q退出数据库)
mysqldump -uroot -p123 db1 > db1.sql
mysqldump -uroot -p123 db1 表1 表2 表3 > ceshi100.sql
# 导入 (进入到mysql,选好数据库)
source /home/wangwen/work/abc.sql

29.什么是SQL注入?(2分)

sql注入:通过注入一些特殊的字符,绕开sql的判断机制
# 使用预处理机制,可以尽量避免sql注入
execute 默认参数是一条sql语句,如果加入参数元组,就等于开启预处理
语法:execute(sql,(参数1,参数2,参数3......))


import pymysql
user = input("user>>>:").strip()
pwd = input("password>>>:").strip()

conn = pymysql.connect(host="127.0.0.1",user="root",password="",database="db2")
# 创建游标对象
cursor = conn.cursor()
# 方法一
"""
user>> sdfsd
password>> sdfsdf' or 10=10 -- sdfsdfsf
sql = "select * from usr_pwd where username = '%s' and password='%s' " % (user,pwd)
res = cursor.execute(sql)
print(res) #返回条数
"""

# 方法二
sql = "select * from usr_pwd where username = %s and password=%s"
res = cursor.execute(sql,(user,pwd))

if res:
  print("登录成功")
else:
  print("登录失败")

30.简述left join和inner join的区别?(2分)

left join  : 左联 以左表为主,右表为辅,完整查询左表所有数据,右表不存在的数据拿null来补 
inner join : 内联 查询左表右表共同存在的数据 select * from a,b where a.cid = b.id

31.SQL语句中having的作用?(2分)

一般和group by 配合使用,将分组之后的数据进行二次过滤用having

32.MySQL数据库中varchar和text最多能存储多少个字符?(2分)

varchar 存的是字符 21845 最大字节数 65535 
text    存的是字符 65535 最大字节数 65535 * 3

33..MySQL的索引方式有几种?(3分)

主键primary key 唯一索引 unique 普通索引 index
联合主键primary key(字段1,字段2,...)
联合唯一索引 unique(字段1,字段2,..)
联合普通索引 index(a,b,c)

innodb(聚集索引) : 一个表只有一个聚集索引,和多个辅助索引,排序速度比较快
myisam(辅助索引) : 只能有多个辅助索引,没有聚集索引

myisam 和innodb 使用索引数据结构都是b+树,只是叶子节点存储的数据不同
innodb文件结构中只有.frm 和 .ibd, 直接把数据塞到叶子节点上
myisam文件结构中只有.frm .myd .myi 叶子节点存储的该数据的地址(映射关系)

34.什么时候索引会失效?(有索引但无法命中索引)(3分)

1.如果查询的是一个大范围内的数据(like in > < ....) 不能命中索引(
2.索引字段参与运算,不能命中,select * from s1 where id*3 = 600
3.如果有or相连,索引字段的判断条件在or的后面,不能命中索引
4.类型不匹配,不能命中 select * from s1 where first_name = 1000
5.联合索引中,不符合最左前缀原则的,不能命中索引
6.like以%开头

35.数据库优化方案?(3分)

1.读写分离(主从数据库,主数据库查询,从数据库负责增删改)
2.分库分表(将字段数量过多的表进行拆分)
3.合理优化数据类型,尽量少的占用空间以合理改善聚集索引b+树的高度(追求矮胖结构)

36.什么是MySQL慢日志?(2分)

设定一个时间阀值,执行sql的时间超过该阈值,把该sql记录在日志文件里,就是慢查询日志
# 查看日志开启状态
show variables like 'slow_query_log';
# 开启慢查询日志
set global slow_query_log = "ON";
# 查看时间阈值
show variables like "long_query_time"
# 设置时间的阈值
set global long_query_time = 5
....
# 参考: https://www.cnblogs.com/Yang-Sen/p/11384440.html

37.设计表,关系如下: 教师, 班级, 学生, 科室。(4分) 科室与教师为一对多关系, 教师与班级为多对多关系, 班级与学生为一对多关系, 科室中需体现层级关系。

1.  写出各张表的逻辑字段
2.  根据上述关系表
   a.查询教师id=1的学生数
   b.查询科室id=3的下级部门数
   c.查询所带学生最多的教师的id

teacher 老师
id name    post_id
1  王老师   1
2  张老师   1
3  金角大王 2

class 班级
id name
1  python1班
2  python2班
3  python3班

t_c_relation 多对多关系
id tid cid
1  1   1
2  1   2
3  3   1
4  3   2

student 学生
id name class_id
1  李四  1
2  张三  2

post 部门
id name    parent_id
1  教务部   0
2  python部 1
3  linux部  1

# a
select
count(*)
from
t_c_relation as tc,student as s
where
tc.cid = s.class_id
and
tc.tid = 1

# b
select
count(*)
from
post
where
parent_id = 3

# c
select
tc.tid,count(*) as c
from
t_c_relation as tc,student as s
where
tc.cid = s.class_id
and
tc.tid = 1
group by
tc.tid
order by
c desc
limit 1


38.有staff表,字段为主键Sid,姓名Sname,性别Sex(值为"男"或"女"),课程表Course,字段为主键Cid,课程名称Cname,关系表SC_Relation,字段为Student表主键Sid和Course表主键Cid,组成联合主键,请用SQL查询语句写出查询所有选"计算机"课程的男士的姓名。(3分)

 

staff
sid sname sex
1  张三  
2  李四  

course
cid  cname
1    计算机
2    美术

sc_relation
sid  cid
1     1

# as 起别名
select
  s.sname
from
  staff as s,
  course as c,
  sc_relation as sc
where
  sc.sid = s.sid
  and c.cid = sc.cid
  and c.cname = "计算机"
  and s.sex = "男"

# as 可以省略
select
  s.sname
from
  staff s,
  course c,
  sc_relation  sc
where
  sc.sid = s.sid
  and c.cid = sc.cid
  and c.cname = "计算机"
  and s.sex = "男"

39.根据表关系写SQL语句(10分)

 

  • 查询所有同学的学号、姓名、选课数、总成绩;

  • 查询姓“李”的老师的个数;

  • 查询平均成绩大于60分的同学的学号和平均成绩;

  • 查询有课程成绩小于60分的同学的学号、姓名

  • 删除学习“叶平”老师课的score表记录;

  • 查询各科成绩最高和最低的分:以如下形式显示:课程ID,最高分,最低分;

  • 查询每门课程被选修的学生数;

  • 查询出只选修了一门课程的全部学生的学号和姓名;

  • 查询选修“杨艳”老师所授课程的学生中,成绩最高的学生姓名及其成绩;

  • 查询两门以上不及格课程的同学的学号及其平均成绩;

 

第二部分 补充题

  1. 什么是IO多路复用?

内部的实现是异步非阻塞,通过单个线程管理多个socket连接,而不是创建大量的多进程/多线程,节省资源,提升效率
这些网络io操作都会被selector(内部使用linux的epoll多路复用接口实现的)暂时挂起,推入内存队列
此时服务端可以任意处理调度里面的网络io,
当连接的socket有数据的时候,自然会把对应的socket告诉你然后进行读写,而不至于一直阻塞等待

2.async/await关键字的作用?

asyncio 是在io密集型任务中,处理协程异步并发的工具模块,目的是加快通信的速度,减少阻塞等待
async def 关键字定义异步的协程函数
await 关键字加载需要等待的操作前,控制一个可能发生io阻塞任务的切入和切出

3.MySQL的执行计划的作用?

执行计划 在一条sql执行之前,制定执行的方案
"""desc/emplain + sql"""
desc select * from t1;
把执行计划的类型,优化级别从低->
all > index > range > ref > eq_ref > const > system
目标: 至少达到range , ref;
range 索引范围扫描(注意点:如果范围太大,不能命中索引)
ref   普通索引查询(非唯一)

4.简述MySQL触发器、函数、视图、存储过程?

https://www.cnblogs.com/Eva-J/articles/10435035.html

5.数据库中有表:t_tade_date

id      tade_date
1 2018-1-2
2 2018-1-26
3 2018-2-8
4 2018-5-6
...
输出每个月最后一天的ID


select
id,max(tade_date)
from
t_tade_date
group by
month(tade_date)

 

 

posted @ 2020-02-26 11:45  圣君灬七夜  阅读(697)  评论(0)    收藏  举报