学习笔记:Python 面试100讲(基于Python3.x)05-python综合题

01-read、readline、readlines区别

一 如何打开和读取文本内容
二 使用open函数打开文件,并返回一个IO对象,该对象有3个用于读取文件的方法,
分别为read、readline、readlines。请用代码描述它们的区别

答:
一
使用open()函数,例如:
#随便创建一个txt文件
f = open('heihei.txt', 'r')  # r表示可读
print(type(f))  # 输出
print(f.read())  # 输出文本内容
print(f.read())  # 这个不会输出任何东西,因为文件指针到最后了

二
1.read:读取文件的全部内容
f = open('heihei.txt', 'r')  # r表示可读
print(type(f))  # 输出
print(f.read(4))    #表示读取前四个字符
f.seek(3)   #表示文件指针指到第4个字节,就是下次read会从第4个字节开始
#注意:上面的seek里的参数是偏移量,也就是代表需要移动偏移的字节数,注意是按照字节算的,字符编码存每个字符所占的字节长度不一样。
#如果是英文字符则是一个字节一个字符,如果是中文,若指针刚好到这个表示这个中文字符的字节中间时,会报错
print(f.read(4))    #这里会输出第4个字符到第7个字符的共四个字符(包括第7个)(假设是一个字节表示的一个英文字符)
f.close()   #关闭文件

2.readline:按行读取
f = open('heihei.txt', 'r')  # r表示可读
print('-------------------')
print(f.readline()) #读取第一行,文件指针到第二行了
print(f.readline()) #由于文件指针到第二行了,所以读取第二行
#现在文件指针来到第三行了
#readline里面的参数代表读取当前行的前几个字符(不是字节了),如果该数大于当前行的字符数,则只会输出该行
print(f.readline(5))
f.close()

3.readlines:返回一个列表,每一项元素都是文件里的一行
f = open('heihei.txt','r')
print('---------------')
print(f.readlines())    #返回一个列表,文件里每一行都是列表的元素
f.seek(0)   #让文件指针回到开始
print(f.readlines(25))  #里面的参数25表示只要返回的列表里所有元素的字符刚好大于25个就可以了
#   返回的列表里每个元素都是一整行,最后那个元素不是说刚好到25就截断了
f.close()

02-在JSON序列化时日期类型如何处理

一 在JSON序列化时,可以自动处理哪些数据类型
二 在JSON序列化时,如何处理日期类型

答:
一
可以处理的数据类型:
str、int、list、tuple、dict、bool、None
但是datetime类型不支持JSON序列化

二
这里不是很懂,所以注释很少,就是知道json序列化不支持datetime类型,实际应用遇到再查找资料好了
貌似是先用strftime方法搞成字符串在json序列化
视频原文的总结是:
JSON序列化并不能自动处理日期类型的值,所以需要在转换类的default方法中手工完成对日期类型值的处理

import json
from datetime import date, datetime


class DateToJson(json.JSONEncoder):
    def default(self, obj):
        # isinstance判断两个数据类型是否一样
        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": "Bill", "datetime": datetime.now()}
print(json.dumps(d, cls=DateToJson, ensure_ascii=False))

03-with语句用法

一 with语句有什么作用,用代码解释一下
二 如何将with语句用于一个自定义类

答:
总结:with语句可以确保不管是否抛出,都会释放资源。如果将with语句用于自定义类中,
需要实现__enter__和__exit__方法,否则会抛出异常

一
with语句用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”工作,
即释放相关资源
例:
f = open("heihei.txt", "r")
data = f.read()
print(data)
'''
上面代码有不合理的地方:
1.没有关闭文件
2.即使有关闭文件的语句,但是如果在关闭之前抛出异常,仍然无法关闭

这样就造成了资源浪费
'''

f = open("heihei.txt", "r")
try:
    data = f.read()
except:
    print("抛出异常")
# finally表示无论是否抛出异常,都会执行的操作
finally:
    f.close()
'''
上面这段代码解决了第一段代码中的第2个不合理之处,但没有解决第一个,
就是如果忘记写关闭的语句了怎么办?
这个时候就需要with语句了
'''
#下面的with里面的语句执行完后,会自动调用close()函数,即自动关闭文件
with open('heihei.txt', 'r') as f:
    data = f.read()
    print(data)

二
'''
类中有两个魔术方法
__enter__:使用类时最先调用
__exit__:使用类时最后调用
with语句用于类时类必须要有这两个方法
'''


class myClass:
    def __enter__(self):
        print("这是__enter__方法")
        return self

    def process1(self):
        print("process1")

    def process2(self):
        # 模拟抛出异常
        a = 1 / 0
        print('process2')

    def process3(self):
        print('process3')

    # exit方法后面那几个参数在类执行抛出异常时会有相应的值来表示是什么异常
    # 当没异常的时候后面参数返回的是None
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("这是__exit__方法")
        print(f"type:{exc_type}", f"value:{exc_val}", f"traceback:{exc_tb}", sep="\n")

with myClass() as my:
    my.process1()
    my.process2()
    my.process3()
'''
执行程序后的输出为(我自己省略了系统的报错信息):
这是__enter__方法
process1
这是__exit__方法
type:
value:division by zero
traceback:
'''

04-得到文件中出现次数最多的字符

一 现在有一个文本文件,要求得到该文本文件中出现次数最多的字符最后输出该字符和出现的次数,
空白符除外

答:
'''
这次在下的知识面就辐射到了这道题,哈哈
d是一个记录文档里出现过的字符及其出现次数的字典
maxChar是记录出现最多次的字符,初始值为文档里的第一个字符
'''

with open('heihei.txt', 'r') as f:
    data = f.read()
d = {}
maxChar = data[0]
for c in data:
    # 判断c是不是空白符
    if c.isspace():
        continue
    if d.get(c) is None:
        d[c] = 1
    else:
        d[c] += 1
        maxChar = c if d[c] > d[maxChar] else maxChar
print(maxChar, d[maxChar])

05-装饰器的作用

一 请简要地描述装饰器的作用
二 如何定义装饰器

答:
一
装饰器是一个普通的函数,主要用于为函数添加额外功能。如插入日志、性能测试等

b站看到一个解释装饰器很清楚的视频,感觉还可以,网址如下:
https://www.bilibili.com/video/BV1d7411h794
看完还不是很懂的话可以看一下还是这个up主讲解的函数闭包:
https://www.bilibili.com/video/BV1M7411a7M3

二
#反正这里不找找其他博客我是看不懂的了
from functools import wraps


def log(flag):
    def decorate(func):
        @wraps(func)
        def _wrap(*args, **kwargs):
            try:
                if flag:
                    func(*args, **kwargs)
                print("name", func.__name__)
            except Exception as e:
                print(e.args)

        return _wrap

    return decorate


@log(True)
def add(a, b, c):
    print('sum', '=', a + b + c)


add(1, 2, 3)

06-判断调用的是函数还是方法

一 如何区分调用的是函数还是方法

答:
1.
'''
在类里的是方法,独自定义的是函数

好像之前笔记里有些地方把方法和函数弄混淆了
'''
class myClass:
    # 方法
    def process(self):
        pass
# 函数
def process():
    pass

print(type(myClass().process))  # 输出
print(type(process))  # 输出
print(type(myClass().process).__name__ == 'method')  # 输出True
print(type(process).__name__ == 'function') # 输出True

2.用isinstance函数判断
from types import MethodType,FunctionType
print(isinstance(myClass().process,MethodType))
print(isinstance(myClass().process,FunctionType))

07-@staicmethod和@classmethod的用法和区别

一 请解释@staticmethod和@classmethod的用法和区别

答:
'''
都是用来声明静态方法的(静态方法是不需要实例化对象就能使用的,使用时就是“类名.方法名“就行了)

1.@staticmethod不需要表示自身的self对象的self和自身类的cls参数,就像普通函数一样定义
    应该是只调用静态变量时使用
2.@classmethod也不需要self参数,但需要有表示自身的cls参数,避免硬解码。
'''


class myClass:
    bar = 2  # 静态变量,可以直接myClass.bar引用

    def __init__(self):
        self.count = 20  # 成员变量,要实例化对象才能引用

    # 创建一个普通方法,普通方法必须加入self
    def process(self):
        print("process", self.count)
    @staticmethod   #应该是引用静态对象时需要这两个装饰器吧
    def static_process():
        print("static_process")
        print(myClass.bar)
    @classmethod    #要有一个cls参数,随便命名
    def class_process(cls):
        print("class_process")
        # 调用静态变量
        print(cls.bar)
        print(cls) #输出这个类本身,
        #貌似cls后面加个括号就相当与实例化,能调用成员的变量和方法了
        cls().process() #可以实例化调用这个类里的方法
        print(cls().count)

# 啦啦啦
print(myClass.bar)
myClass.static_process()
myClass.class_process()
myClass.bar = 123
myClass.static_process()

08-元类(metaclass)的作用

一 什么是元类,请用代码解释如何使用元类

答:
1.metaclass:元类,类似与创建类的模板,所有的类都是通过他来创建的,可以自由控制类的创建过程。
例如实现单例模式和ORM模式。(一般开发不会用到元类,除非想控制类的创建)

到这里又不是很清楚了,这个学习视频是面试用的,不会讲的很清楚,
我还是去b站找了个元类解释视频(再下面的博客链接解释得更清楚):
https://www.bilibili.com/video/BV1y7411F7cR

2.这是看完学习视频都不懂的元类操作,找点博客或者视频来看吧
要是有空可以认真读一下下面这篇博客:
https://segmentfault.com/a/1190000007255412

# 创建一个继承自type的类
class Singleton(type):
    def __init__(self, *args, **kwargs):
        print("in __init__")
        self.__instance = None
        '''
        super() 函数是用于调用父类(超类)的一个方法。super 是用来解决多重继承问题的,
        直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查
        找顺序(MRO)、重复调用(钻石继承)等种种问题。
        '''
        super(Singleton, self).__init__(*args, **kwargs)

    # 创建类的实例时自动调用__call__方法
    def __call__(self, *args, **kwargs):
        print("in __call__")
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
            print(type(self.__instance))
        return self.__instance


class myClass(metaclass=Singleton):
    pass


my1 = myClass()
my2 = myClass()
print(my1 is my2)   #输出True

09-hasattr()、getattr()、setattr()的用法

一 请用代码说明hasattr()、getattr()、setattr()的作用

答:
'''
hasattr:可以判断一个对象是否含有某个属性
getattr:可以获取对象中某一个属性的值
setattr:可以设置对象中某一个属性的值
'''

# 定义一个类
class Person:
    def __init__(self):
        self.name = "lili"
        self.age = 12

    def show(self):
        print(self.name, self.age)


# 前一个参数指定类,后一个参数指定属性(方法和属性没有本质区别)
if hasattr(Person, "show"):
    print("存在show方法")

person1 = Person()
setattr(person1, "sex", "男")
setattr(person1, 'age', '21')   #能修改已有属性,也能添加属性
print(getattr(person1, 'sex'))
print(getattr(person1, 'age'))
print(hasattr(person1, 'address'))  #这里输出False
# setattr不仅可以在类的实例里添加属性,还能直接为类添加属性
setattr(Person, "sex", "女")
person2 = Person()
print(person2.sex)

10-请描述lambda表达式的用法

一 请阐述什么是lambda表达式,并用代码描述lambda表达式的应用场景

答:
1.lambda函数:就是匿名函数,可以作为参数值传给函数或方法

2.
a = [("a", 1), ("b", 2), ("c", 3), ("d", 4)]
# map函数是将第一个参数里的函数作用到第二个可迭代的参数的每一项上
# map如果还有第三、第四个可迭代的参数,那么会把他们的每一项分别代进第一个函数参数的参数
# map会返回一个迭代对象,用list弄成列表输出。map用法另外找,这里就这样了
a_1 = list(map(lambda x: x[0], a))
a_2 = list(map(lambda x: x[1], a))
print(a_1, a_2)
#这里输出['a', 'b', 'c', 'd'] [1, 2, 3, 4]

f = lambda x, y: x + y
print(f(1, 2))  # 这里输出3

11-浅拷贝(copy)和深拷贝(deepcopy)

一 请描述一下浅拷贝(copy)和深拷贝(deepcopy)的区别,
请用代码举例说明如何使用copy和deepcopy

答:
1.
copy:只复制深层对象的引用
deepcopy:复制深层对象本身

2.
import copy

# 列表a里面的["A", "B"]是a的深层的对象
a = [1, 2, 3, ["A", "B"]]
a_copy = copy.copy(a)  # 浅拷贝
a_deepcopy = copy.deepcopy(a)  # 深拷贝
print(a_copy)
print(a_deepcopy)
'''
上面两个输出:
[1, 2, 3, ['A', 'B']]
[1, 2, 3, ['A', 'B']]
'''
a[3].append(123)
print(a)
print("加123后的浅拷贝", a_copy)
print("加123后的深拷贝", a_deepcopy)
'''
上面三个输出:
[1, 2, 3, ['A', 'B', 123]]
加123后的浅拷贝 [1, 2, 3, ['A', 'B', 123]]
加123后的深拷贝 [1, 2, 3, ['A', 'B']]
'''

12-编写一个生成器使得一个二维列表转换为一个一维列表

一 编写一个生成器,将一个二维列表转换为一个一维列表

答:
1.这里主要是介绍yield的使用
我的理解:yield和return差不多,只不过调用了return后函数就停止了,但每次调用yield都相当于一个return,
但是yield完之后会继续接着yield后面开始运行,返回的是一个生成器类型的值
如:
def yield_i():
    a = [1, 2, 3, 4, 5, 6]
    for i in a:
        yield i
def return_i():
    a = [1, 2, 3, 4, 5, 6]
    for i in a:
        return i
print(list(yield_i())) #yield返回的是一个生成器
print(return_i()) #int类型不是可迭代对象,不能用list转换
'''
上面两条输出语句的输出分别为:
[1, 2, 3, 4, 5, 6]
1
'''

2.
# 现在把[[1,2,3],[4,5,6],[7,8]]转换为一个一维列表

def enumList(a):
    for sublist in a:
        for elem in sublist:
            yield elem
a = [[1,2,3],[4,5,6],[7,8]]
print(list(enumList(a)))

13-递归生成器

一 请编写一个生成器,将任意多维的列表转换为一维列表

答:
nestedList = [1, [2, 3, 4,[5, 6, 7,[8], [9, 10]]], [11, 12, [13, 14]]]
def enumList(a):
    try:
        for sublist in a:
            for elem in enumList(sublist):
                yield elem
    # 出现数据类型错误时执行下面代码
    except TypeError:
        # 当遇到不能迭代的数据时,就会把那个数据输出
        yield a

print(list(enumList(nestedList)))
#输出[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

14-获取日期的年月日

一 如何获取当前日期的年月日以及是一年中的第几天

答:
import time

# time.localtime就是把time.time生成的时间戳加工一下
localtime = time.localtime(time.time())
print(type(localtime))
# 输出
print(localtime)
# 输出time.struct_time(tm_year=2020, tm_mon=4, tm_mday=8, tm_hour=3, tm_min=59, tm_sec=14, tm_wday=2, tm_yday=99, tm_isdst=0)
# 想输出年和今天是一年里第几天的话如下,其他如法炮制
print(localtime.tm_year)
print(localtime.tm_yday)
# 可以用列表表示,一样的,比如输出月份
print(localtime[1])
for t in localtime:
    print(t, end=' ')
#这个循环输出:2020 4 8 4 7 31 2 99 0

15-进程间的通信

一 用python创建两个进程,这两个进程间如何通信

答:
'''
multiprocessing是处理进程的模块
Process创建进程
通过Queue在两个进程间通信
我猜Queue应该是一个所有进程都可以用到的东西,然后代进进程创建时的args参数就好了
'''
from multiprocessing import Queue, Process
# 导入time,random模块让进程操作间隔一段时间(我也不懂为什么要多这一步,视频是这么说的)
import time, random

# 创建一个列表充当两个进程通信的数据
list1 = ['一只羊', '两条狗', '三只猫', '四头牛']
def write(queue):
    for value in list1:
        print(f"正在向进程中添加数据->{value}")
        queue.put_nowait(value)
        time.sleep(random.random())

def read(queue):
    while True:
        if not queue.empty():
            value = queue.get_nowait()
            print(f"从队列中取到的数据为->{value}")
            time.sleep(random.random())
        else:
            break

queue = Queue()
#进程定义
write_data = Process(target=write,args=(queue,))
read_data = Process(target=read,args=(queue,))
write_data.start()
write_data.join()
read_data.start()
read_data.join()
print("OK")

 16-为一个线程函数传递参数

一 当运行一个线程函数时,如何为该函数传递参数

答:
和进程差不多,不过少了一个队列,直接在创建时加入args参数就好了
例如:

import threading

#创建一个函数,供线程调用
def func(s,fun):
    print("我爱她轰轰烈烈最疯狂")
    #该函数的第二个参数是一个函数,下面调用了这个函数
    fun(s)

# 定义一个传入func第二个参数的函数
def toolPerson(s):
    print(f"我是工具人函数,输出我的s参数为{s}")
#创建一个线程
# target是这个线程要执行的函数
#args是给target要传入的参数
t1 = threading.Thread(target=func,args=("hello world",toolPerson))
#线程一号,劲爆启动
t1.start()
'''
输出:
我爱她轰轰烈烈最疯狂
我是工具人函数,输出我的s参数为hello world
'''

17-线程中创建和使用全局对象

一 如何创建和使用在线程内部使用的全局对象

答:
'''
使用threading.local方法,
可以创建只在单个线程使用的全局变量
'''
import threading
import time

# 导入time模块是为了使用time.sleep(),我猜是为了不让线程里面的操作太快完成
# 想了想,不加也行吧。话说做这个学习视频的老师也不说为什么要加
a = threading.local()
print(type(a))  # 可以看到a其实是一个类


# 创建一个供线程使用的函数
def worker():
    a.x = 0
    for i in range(20):
        time.sleep(0.01)
        a.x += 1
    # 函数操作的左后一步,打印出当前线程名字和a.x的值
    print(threading.current_thread(), a.x)
    #线程是交替进行的(操作系统知识),所以有些a.x没有跟在线程名称后面


# 循环弄出十个线程来执行,看看a.x的值会不会跨线程叠加
for i in range(10):
    threading.Thread(target=worker).start()

 

未完待续……

你可能感兴趣的:(视频学习记录)