Skip to content

Python八股文--面试题

业精于勤荒于嬉,行成于思毁于随。

Author: 李东阳

Day01

注:这里只记了个人觉得需要记的面试题

1.求两集合的交集、并集、差集

set1 = {2, 2.2, True, 4+3j}
set2 = {1, 2.2, False, 4+3j}
# 交集
print(set1 & set2)
# 输出:
# {1, 2.2, (4+3j)}
# 原因:set1 去 set2 里面找相同的元素,找到set2里面的 1 时 ,发现 set1 里面没有1 那么将 True自动转换为 1
print(set2 & set1)
# 输出:
# {True, 2.2, (4+3j)}
# 原因:set2 去 set1 里面找相同的元素,找到set1 里面的 True 时, 发现 set2 里面没有True 那么将 1自动转换为 True
# 优先级: 后面的高于前面的


# 并集
print(set1 | set2)
# 输出:
# {False, True, 2, 2.2, (4+3j)}
# 原因: 先展示 set1 里面的元素, 因为开始展示了 set1 里面的 True , 所以 set2 里面的 1 会被认为是相同的元素,故而不展示
print(set2 | set1)
# 输出:
# {False, 1, 2.2, 2, (4+3j)}
# 同理可得
# 优先级: 前面的高于后面的

# 差集
print(set1 - set2)
# 输出:
# {2}
# 原因: set1 减去set1 与 set2 的交集, 然后输出 set1中剩余的元素
print(set2 - set1)
# 输出:
# {False}
# 同理可得

2.对于函数来说 return是什么?

2.1、 函数的结束标志,一个函数可以有多个return,但是return只执行一次。

​ 2.2 、函数的返回传

​ 2.3 、函数可以返回任意数据类型的数据

​ 2.4 、函数可以返回任意个数的返回值

​ 2.4.1、返回0个: 返回none,不写return,系统默认return none

​ 2.4.2 、 返回1个: 返回值就是返回该值的本身

​ 2.4.3、 返回多个: 返回值是一个元组,我们需要拆包获取

3.求一下字典中对应工资最高的人名

salaries = {
    'aaa': 11500,
    'bbb': 32000,
    'ccc': 25000
}
# 直接求字典的最大值是根据键来判断的
print(max(salaries))

# 输出:
# ccc

# 按照value去比较
print(max(salaries, key=lambda key: salaries[key])) 
# 输出:
# bbb

4.写一个用来遍历某目录下所有内容的函数。(使用os模块)

# 步骤一: 遍历该目录下的所有文件以及文件夹

# 步骤二: 判断是文件还是文件夹

# 步骤三: 如果是文件那么直接打印, 如果是文件夹就再次判断,直到没有文件夹为止(递归)


import os


def fun(path):
    path_list = os.listdir(path)  # 获取该目录下的所有文件和文件夹
    for i in path_list:
        absolute_path = os.path.join(path, i)  # 获取绝对路径
        if os.path.isdir(absolute_path):  # 判断他是否为文件夹
            fun(absolute_path)  # 递归
        else:  # 如果是文件
            print(absolute_path)


path = r'C:\Users\86195\Desktop\点我打开\python面试题'
fun(path)

5.写一个登录的装饰器对以下函数进行装

要求:输入密码和账号才能运行该函数

def func(fun):
    def login(user_account, user_pwd):
        if user_account == 'root' and user_pwd == '123':
            print('登录成功')
            return fun()
        else:
            print('登录失败,账号或者密码错误')
            return

    print('111111')
    return login


@func
def run():
    print('开始执行函数')


run('root', '321')

6.生成验证码函数,验证码包含整形数字和字母组成,且可以指定长度

def create_code(code_lenth):
    res = ''
    for i in range(code_lenth):
        num = str(random.randint(0, 9))
        chr_big = chr(random.randint(65, 90))  # 获取A-Z之间的所有字母
        chr_low = chr(random.randint(65, 90)).lower()
        res += random.choice([num, chr_big, chr_low])
    return res


print(create_code(4))

7.类的实例化对象怎么调用

# __call__: 在对象被调用的时候会自动触发该方法

class A:
    def __init__(self):
        print('初始化')

    def __call__(self, *args, **kwargs):
        print(self)


a = A()
a()

Day02

8.什么是单例模式,怎么用

注:这里只举一个单例模式的使用例子,具体解释看设计模式之单例模式

class Bar(object):
    __instance = None  # 可以理解为一个这个类是否创建过对象的标记位
    # 重写__new__ 方法
    def __new__(cls, *args, **kwargs):  # 创建一个类的实例对象就是在调用object的__new__方法
        if cls.__instance == None:  # 判断是否已经创建过实例对象
            cls.__instance == object.__new__(cls)  # 没有就返回一个新创建的对象
            return cls.__instance
        else:  # 否则返回之前已经创建好的实例对象
            return cls.__instance

a = Bar()     # 这里实例化了两个类对象,但是可以看到内存地址是相同的,说明其实这两个对象是同一个
b = Bar()
print(id(a))        # 1659937936
print(id(b))        # 1659937936

9.怎样标识全世界范围内独一无二的应用软件(基于网络通信)

IP + mac + port

IP: 找到局域网

mac: 找到具体那一台计算机

port: 找到计算机上的具体软件

10.守护线程和守护进程的区别

想要知道它们两之间的区别,那么我们就要了解什么是守护线程和守护进程。

一、守护进程

​ 1.守护进程本身就是一个子进程

​ 2.守护进程会在主进程代码运行结束的情况下,立即挂掉。(注意理解这个代码运行结束不是说主进程结束)

那么我们现在写一个进程的例子:

# 守护进程

from multiprocessing import Process
import time


def fun():
    print('守护进程开始')
    time.sleep(2)
    print('守护进程结束')


def fun2():
    print('进程开始')
    time.sleep(4)
    print('进程结束')


if __name__ == '__main__':
    print('主进程开始')
    p1 = Process(target=fun)
    p2 = Process(target=fun2)
    p1.daemon = True
    p1.start()
    p2.start()
    print('主进程结束')


# 输出:
# 主进程开始
# 主进程结束
# 进程开始
# 进程结束
# 可以看到守护进程还没创建完成主进程代码就结束了,所以,守护进程都没有执行
# 注意: 进程的创建所消耗的资源是很大的

从上面的例子可以得到这样的结论:

主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束。

二、守护线程

主线程与进程的关系:

​ 1、主线程的生命周期就是一个进程的生命周期

​ 2、主进程等待子进程是因为主进程要负责回收子进程的系统资源,主线程等待子线程是因为主线程要等待子线程运行完毕(子线程运行完毕后,这个进程才算运行完毕)后,主线程才结束。

线程的例子:

from threading import Thread


def run():
    print('守护线程开启')
    time.sleep(2)
    print('守护线程结束')


def run2():
    print('非守护线程开启')
    time.sleep(4)
    print('非守护线程结束')


if __name__ == '__main__':
    print('主线程开启')
    t1 = Thread(target=run)
    t2 = Thread(target=run2)
    t1.daemon = True
    t1.start()
    t2.start()
    print('主线程运行完毕')

# 输出:
# 主线程开启
# 守护线程开启
# 非守护线程开启
# 主线程运行完毕   
# 守护线程结束
# 非守护线程结束

# 可以看到当主线程执行结束时,为守护线程的t1应该结束,但是却没有结束仍然在继续执行

从上面的例子中可以得出结论:

1、守护线程会在"该进程内所有非守护线程全部都运行完毕后,守护线程才会挂掉"。并不是主线程运行完毕后守护线程挂掉。

2、守护线程守护的是:当前进程内所有的子线程!

3、主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。

打一个比方:

守护线程相当于普通员工,他们要看老板下班他们才下班。但是,老板是看那些加班的员工什么时候下班才下班。就是说普通员工(守护线程)他被迫加班了啊!

守护进程则是相当于00后的普通员工,他们才不看老板什么时候下班呢,到点下班。

综上所述:

守护线程只有当当前进程中的所有非守护线程结束才会结束,而守护进程则是当主进程代码执行完毕后,立马就结束销毁了。

这一点是守护线程和守护进程的区别之处!

11.MySQL的varchar类型和char类型的区别

1、长度是否可变

varchar 类型的长度是可变的,而 char 类型的长度是固定的

char 类型是一个定长的字段,以 char(10) 为例,不管真实的存储内容多大或者是占了多少空间,都会消耗掉 10 个字符的空间

坦通俗来讲,当定义为 char(10) 时,即使插入的内容是 'abc' 3 个字符,它依然会占用 10 个字节,其中包含了 7 个空字节

举个例子:

char(5) 、varchar(5)存储一个字符串'abc'

char: 'abc '

varchar: 'abc'

2、存储长度

char 长度最大为 255 个字符,varchar 长度最大为 65535 个字符

3、检索效率方面

varchar 类型的查找效率比较低,而 char 类型的查找效率比较高

12、如何防止sql注入

什么是sql注入:

SQL注入是指WEB应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据服务器数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

关于什么是sql注入,我就不过多讲解了,可以自己去了解。

我们要如何防止sql注入呢?

  1. 在前端限制一些非法字符的输入

  2. 使用参数化,尽量不要使用拼接

例子:

sql = "SELECT * FROM table WHERE username = '%s' AND userpwd = '%s'" % (username, userpwd)
cursor.execute(sql)
换成下面这种写法
sql = "SELECT * FROM table WHERE username = %s AND userpwd = %s"
cursor.execute(sql, (username, userpwd))