高级特性面试题

7. 高级特性面试题

7.1 简述with方法打开处理文件帮我们做了什么?

难度指数: ★★★
重要指数: ★★★★

使用with open()的方式打开文件进行操作,如果在读或写的过程中发生了异常,with 会帮我们close文件。如果用try ... except ... finally 方式可以实现同样的效果,在finally中关闭文件。但这样写太麻烦了,不如with open 方便。

提到with ,你必须理解上下文管理器,一个对象实例实现了__enter__ 和 __exit__ 方法,那么它就是一个上下文管理器。__enter__ 通常返回实例本身,__exit__方法里可以做一些清理工作,比如文件的关闭,with 语句块里的代码就在这两个函数中间被执行。

关于with 关键字,参见文章python with 语句

7.2 列表推导式编程---结合map

难度指数: ★★
重要指数: ★★★

题目要求:

列表[1,2,3,4,5],请使用map()函数输出[1,4,9,16,25],并使用列表推导式提取出大于10的数,最终输出[16,25]

编程题目,考察的是你的概念理解和动手能力,只有多练习,才能在面试时从容应对

lst = [1, 2, 3, 4, 5]

for item in map(lambda x: x**2, lst):
    print(item)

result = [item for item in map(lambda x: x**2, lst) if item > 10]
print(result)   # [16, 25]

关于列表推导式,参见文章列表生成式, 字典生成式, 集合生成式

7.3 列表推导式---构造奇数序列

难度指数: ★★
重要指数: ★★★

题目要求:

列表推导式求列表所有奇数并构造新列表,a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = [item for item in a if item % 2 == 1]
print(result)       # [1, 3, 5, 7, 9]

7.4 列表推导式---拍平列表

难度指数: ★★★
重要指数: ★★★

题目要求:

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

lst = [[1, 2], [3, 4], [5, 6]]

result = [value for item in lst for value in item]
print(result)    # [1, 2, 3, 4, 5, 6]

# 等价于
reslut = []
for item in lst:
    for value in item:
        result.append(item)

7.5 举例说明异常模块中try except else finally的相关意义

难度指数: ★★★
重要指数: ★★★

try ... except 是捕获异常,处理异常的基本形式,except 可以有多个,以应对不同的Exception。

如果try 与 except 之间的代码没有发生异常,则进入else语句块进行处理,这种语法免去了对是否发生异常的判断。

不论是否发生异常,finally都会被执行,这是非常容易搞错的一点,哪怕前面的语句执行了return, finally也会执行,因此finally语句块里通常放善后处理的代码,因为一定会被执行。

try:
    0/0
except ValueError:
    print("ValueError")
except AttributeError:
    print("AttributeError")
else:
    print('没有异常')
finally:
    print("一定被执行")

7.6 不借助第三个变量交换两个变量的值

难度指数: ★★★
重要指数: ★★★

这是一个非常经典的面试题,第一种解法

a = 3
b = 4

a = a + b       # a = 7 b = 4
b = a - b       # b = 7 - 4 = 3   a = 7
a = a - b       # a = 7 - 3 = 4

print(a, b)     # 4 3

不借助第三个变量,实现了两个变量值的交换。

对于python而言,完全不需要这样复杂

a = 3
b = 4
a, b = b, a
print(a, b)

7.7 python中copy和deepcopy区别

难度指数: ★★★★
重要指数: ★★★★★

copy 是浅拷贝,deepcopy 是深拷贝,python中有一个copy模块,提供了这两个方法。

关于深拷贝和浅拷贝,我们主要从是否生成新的对象这个层面来进行比较。

浅拷贝

  1. 如果被拷贝对象是不可变对象,则不会生成新的对象
  2. 如果被拷贝对象是可变对象,则会生成新的对象,但是只会对可变对象最外层进行拷贝

深拷贝

  1. 如果被拷贝对象是不可变对象,深拷贝不会生成新对象,因为被拷贝对象是不可变的,继续用原来的那个,不会产生什么坏的影响
  2. 如果被拷贝对象是可变对象,那么会彻底的创建出一个和被拷贝对象一模一样的新对象

关于深拷贝与浅拷贝,想要深入理解,参见文章深拷贝与浅拷贝

7.8 什么是python迭代器

难度指数: ★★★★
重要指数: ★★★★★

可迭代对象概念

想要理解迭代器,首先要理解什么是可迭代对象,平时比较常用的数据类型,例如列表,元组,字符串,字典,集合都是可迭代对象,一个对象实现了__iter__ 方法,那么它就是一个可迭代对象。

iter函数的作用

iter函数作用于可迭代对象时,返回一个迭代器

from collections import Iterable, Iterator


lst = [1, 2, 3]
print(isinstance(lst, Iterable), isinstance(lst, Iterator))     # True, False
_iter = iter(lst)       # iter函数返回迭代器

print(isinstance(_iter, Iterator))  # True

next函数的作用
next函数可以从迭代器中取出数据,这个过程是不可逆的,已经取出的数据不能再被取出,执行next的过程就是迭代的过程,游标从头迭代到尾部

lst = [1, 2, 3]
_iter = iter(lst)

print(next(_iter))      # 1
print(next(_iter))      # 2
print(next(_iter))      # 3
print(next(_iter))      # 引发异常 StopIteration

当没有数据可以迭代时,引发StopIteration异常。

for循环与迭代器

lst = [1, 2, 3]
for item in lst:
    print(item)

上面这种for循环的写法,其内部是这样执行的

  1. iter函数走用于列表,返回迭代器
  2. 使用next方法从迭代器中取数据
  3. 重复第二步骤,直到引发StopIteration异常,对异常进行补货,for循环结束

这就是for循环的本质,想要解释清楚迭代器,就必须把上面这4点答出来

  1. 可迭代对象,迭代器
  2. iter函数
  3. next函数
  4. for循环与迭代器

7.9 python中的生成器是什么

难度指数: ★★★★
重要指数: ★★★★★

关键点:

  1. 生成器是一种迭代器
  2. 生成器生成器对延迟操作提供了支持,更加高效的利用内存
  3. python有两种方式提供生成器, 一个是生成器函数,一个是生成器表达式

生成器函数

如果一个函数内部出现了yield这个关键字,那么该函数就是一个生成器函数,调用生成器函数将得到一个生成器

def my_generator(n):
    index = 0
    while index < n:
        yield index
        index += 1

generate = my_generator(5)
print(type(generate))
for i in generate:
    print(i)

生成器表达式

squares = (x**2 for x in range(5))
print type(squares)

关于生成器更详细的介绍,参见文章python 生成器

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

难度指数: ★★
重要指数: ★★

[i for i in range(3)] 是一个列表推导式,改成生成式也非常简单

_iter = (i for i in range(3))
print(type(_iter))      # <class 'generator'>

7.11 如何在python中使用三元运算符

难度指数: ★
重要指数: ★

python中并没有三元运算符,但可以通过if 语句来伪装出一个三元运算表达式

a = 4
b = 3

result = True if a > 3 else False
print(result)       # True

7.12 如何编写上下文管理器

难度指数: ★★★
重要指数: ★★★

实现了__enter__() 和__exit__()的对象就是上下文管理器

下面是一个上下文管理器的例子

import time


class ProTime(object):
    def __init__(self, tag=''):
        self.tag = tag

    def __enter__(self):
        self.start_time = time.time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end_time = time.time()
        time_diff = self.end_time - self.start_time
        msg = "代码段{tag}运行时长{time_diff}".format(tag=self.tag, time_diff=time_diff)
        print(msg)


with ProTime('first') as pt:
    # 这里是你要统计运行时长的代码块
    time.sleep(1)

with ProTime('second') as pt:
    # 这里是你要统计运行时长的代码块
    time.sleep(2)

运行到with 语句时,先执行__enter__()方法,通常这个方法会返回self对象,也就是as 后面的pt, 之后执行with语句块里的代码,从with语句块中退出时,则来执行__exit__()方法。

上面的上下文管理器,在进入with语句时记录时间,在退出with语句时再次记录时间,这样就可以计算出with语句块中的代码执行时长。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案