Python企业面试题2 —— 基础篇

1. re 的match和search区别?
re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
re.search 扫描整个字符串并返回第一个匹配成功的值。
2. 什么是正则的贪婪匹配?
匹配一个字符串没有节制,能匹配多少就去匹配多少,直到没有匹配的为止。
3. 求结果:
a. [i % 2 for i in range(10)]
print([i % 2 for i in range(10)])	# [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
print([i for i in range(10)])	# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print([10 % 2])		# [0]
# % 是个运算符。
b. (i % 2 for i in range(10))
print((i % 2 for i in range(10)))
# <generator object <genexpr> at 0x00000233D5D45EB0> 生成器
# 在py中,有一种自定义迭代器的方式,称为生成器(Generator)。
# 定义生成器的两种方式:
# 1. 创建一个generator,只要把一个列表生成式的[]改成(),就创建了一个generator:generator保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素。没有更多的元素时,抛出StopIteration的错误。
# 2. 定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通的函数,而是一个generator。
4. 求结果
a. 1 or 2	# 1
b. 1 and 2	# 2
c. 1 < (2 == 2)		# False
d. 1 < 2 == 2	# True
5. 如何实现 "1,2,3" 变成 ['1', '2', '3'], 反过来呢?
list("1, 2, 3".split(','))
[int(x) for x in ['1', '2', '3']]
6. 一行代码实现删除列表中重复的值?
list(set([1, 2, 3, 4, 1, 2, 5, 6, 4]))
7. 如何在函数中设置一个全局变量?

py中的global语句是被用来声明全局变量的。

python 复制代码
x = 2
def func():
	global x
	x = 1
	return x
func()
print(x)	# 1
8. logging 模块的作用以及应用场景?
logging 模块定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统。
作用:可以了解程序运行情况,是否正常,在程序的出现故障快速定位出错地方及故障分析。
9. 请用代码简答实现 stack?
  • Stack() 创建一个新的空栈
  • push(item) 添加一个新的元素item到栈顶
  • pop() 弹出栈顶元素
  • peek() 返回栈顶元素
  • is_empty() 判断栈是否为空
  • size() 返回栈的元素个数
python 复制代码
# 实现一个栈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
10. 常用字符串格式化有哪几种?
  • 占位符 %
    %d 表示那个位置是整数;%f 表示浮点数;%s表示字符串。
python 复制代码
print('Hello, %s' % 'Python')   # Hello, Python
print('Hello, %d %s %2f' % (666, 'Python', 9.99))
#   Hello, 666 Python 9.990000
  • format
python 复制代码
print('{k} is {v}'.format(k = 'python', v = 'easy'))    # python is easy
print('{0} is {1}'.format('python', 'easy'))    # python is easy
11. 简述生成器、迭代器、可迭代对象、装饰器?
  1. 生成器:

    包括含有yield关键字,生成器也是迭代器,调动next把函数变成迭代器。

  2. 迭代器:

    含有 __iter____next__方法(包含__iter__方法的可迭代对象就是迭代器)

  3. 可迭代对象:

    一个类内部实现__iter__方法且返回一个迭代器。

  4. 装饰器:

    能够在不修改原函数代码的基础上,在执行前后进行定制操作,闭包函数的一种应用。

    场景:

  • flask 路由系统
  • flask before_request
  • csrf
  • django 内置认证
  • django 缓存
python 复制代码
import functools

def wrapper(func):
    @functools.wraps(func)      # 不改变原函数属性
    def inner(*args, **kwargs):
        # 执行函数前
        return func(*args, **kwargs)
        # 执行函数后
    return inner
# 1. 执行wrapper函数,并将被装饰的函数当做参数。  wrapper(index)
# 2. 将第一步的返回值,重新赋值给新index = wrapper(老index)
@wrapper    # index=wrapper(index)
def index(x):
    return x+100

调用装饰器其实是一个闭包函数,为其他函数添加附加功能,不修改被修改的源代码和不修改被修饰的方式,装饰器的返回值也是一个函数对象。

比如:插入日志、性能测试、事物处理、缓存、权限验证等,有了装饰器,就可以抽出大量与函数功能本身无关的雷同代码并继续重用。

12. def func(a, b=[]) 这种写法有什么坑?
python 复制代码
def func(a, b=[]):
    b.append(1)
    print(a, b)

func(a=2)   # 2 [1]
func(2)     # 2 [1, 1]
func(2)     # 2 [1, 1, 1]
想每次执行只输出[1],默认参数应该设置为None
13. 列举常见的内置函数
  • abs() 返回数字的绝对值
  • map() 根据函数对指定序列做映射
    map() 函数接受两个参数,一个是函数,一个是可迭代对象,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。

返回值:

py2:返回列表

py3:返回迭代器

例子1:

python 复制代码
def mul(x):
    return x*x
n = [1, 2, 3, 4, 5]
res = list(map(mul, n))
print(res)  # [1, 4, 9, 16, 25]

例子2:

python 复制代码
ret = map(abs, [-1, -5, 6, -7])
print(list(ret))    # [1, 5, 6, 7]
  • filter
    filter()函数接收一个函数 f(函数)和一个list(可迭代对象),这个函数 f的作用是对每个元素进行判断,返回True或False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。
python 复制代码
def is_odd(x):
    return x % 2 == 1
v = list(filter(is_odd, [1, 4, 6, 7, 9, 12, 17]))
print(v)    # [1, 7, 9, 17]

map 与 filter 总结

参数:都是一个函数名 + 可迭代对象
返回值:都是返回可迭代对象
区别:filter是做筛选的,结果还是原来就在可迭代对象中的项。map是对可迭代对象中每一项做操作的,结果不一定是原来就在可迭代对象中的项
  • isinstance/type
    isinstance()函数来判断一个对象是否是一个已知的类型,类似type()。
    isinstance() 与 type() 区别:
    type() 不会认为子类是一种父类类型,不考虑继承关系。
    isinstance() 会认为子类类型是一种父类类型,考虑继承关系。
    如果要判断两个类型是否相同推荐使用 isinstance()。
python 复制代码
a = 2
print(isinstance(a, int))   # True
print(isinstance(a, str))   # False
class A:
    pass
class B(A):
    pass
print("isinstance", isinstance(A(), A))     # isinstance True
print("type",type(A()) == A)     # type True
print("isinstance", isinstance(B(), A))     # isinstance True
print("type",type(B()) == A)     # type False
  • 拉链函数
    zip 拉链函数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表迭代器。如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同。
python 复制代码
print(list(zip([0, 1, 3], [5, 6, 7], ['a', 'b'])))  # [(0, 5, 'a'), (1, 6, 'b')]

zip() 函数用于将可迭代对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。

python 复制代码
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = [4, 5, 6, 7, 8]
>>> zipped = zip(a, b)	# 打包为元组的列表 [(1, 4), (2, 5), (3, 6)]
>>> zip(a, c)	# 元素个数与最短的列表一致 [(1, 4), (2, 5), (3, 6)]
>>> zip(*zipped)	# 与zip相反,可理解为解压,返回二维矩阵式	[(1, 2, 3), (4, 5, 6)]
  • reduce
    reduce() 函数会对参数序列中元素进行累积。函数将一个数据集合(链表、元组等)中的所有数据进行下列操作
    注意:py3已经将reduce()函数从全局名字空间里移除了,它现在被放置在functools模块里,如果想要使用它,则需要用过引用引入functools模块来调用reduce()函数。
python 复制代码
from functools import reduce
def add(x, y):
    return x + y
print(reduce(add, [1, 2, 3, 4, 5]))     # 15
print(reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]))    # 15
print(reduce(add, range(1, 101)))   # 5050
14. filter、map、reduce 的作用?

内置函数:map、reduce、filter 的用法和区别

  • map:根据函数对指定序列做映射

    map
    参数:接收两个参数一个是函数,一个是序列(可迭代对象)
    返回值:py2返回列表,py3返回迭代器
    例子:
    abs() 函数返回数字的绝对值
    新的内容的个数等于原内容的个数
    ret = map(abs, [-1, -5, 6, -7])
    print(list(ret)) # [1, 5, 6, 7]

  • filter:过滤函数 新的内容少于等于原内容的时候。才能使用filter,filter()函数用于过滤序列,过滤不符合条件的元素,返回由符合条件元素组成的新列表。

参数:

function 函数

iterable 可迭代对象

返回值:返回列表

python 复制代码
# 筛选大于 10 的数
def is_odd(x):
    if x > 10:
        return True
ret = filter(is_odd, [1, 4, 5, 7, 8, 9, 76])    # 为迭代器
print(list(ret))    # 76
  • reduce:对于序列内所有元素进行累计操作
    reduce() 函数会对参数序列中元素进行累积,函数将一个数据集合(链表、元组等)中的所有数据进行下列操作。
python 复制代码
from functools import reduce
def add(x, y):
    return x + y
print(reduce(add, [1, 2, 3, 4, 5]))     # 15
print(reduce(lambda x, y: x + y, [1, 2, 3, 4, 5]))  # 15
print(reduce(add, range(1, 101)))   # 5050
15. 用py实现一个二分查找的函数?

二分查找算法:简单地说,就是将一个列表先排序好,比如按照从小到大的顺序排列,当给定一个数据,比如3,查找3在列表中的位置时,可以先找到列表中间的数li[middle]和3进行比较,当它比3小时,那么3一定是在列表的右边,反之则3在列表的左边。比如它比3小,则下次就可以只比较[middle+1, end]的数,继续使用二分法,将它一分为二,直到找到3这个数返回或者列表全部遍历完成(3不在列表中)

优点:效率高,时间复杂度为O(logN);

缺点:数据要是有序的,顺序存储。

python 复制代码
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
16. 谈谈你对闭包的理解?
python 复制代码
def foo():
    m = 3
    n = 5
    def bar():
        a = 4
        return m+n+a
    return bar
bar = foo()
print(bar())   # 12

说明:bar在foo函数的代码块中定义。我们称bar是foo的内部函数。在bar的局部作用域中可以直接访问foo局部作用域中定义的m、n变量。简单地说,这种内部函数可以使用外部函数变量的行为,就叫闭包。

闭包的意义与应用

17. 如何生成一个随机数?
python 复制代码
import random
# 生成一个0-1的随机浮点数:0 <= n < 1.0
print(random.random())
# 生成一个指定范围内的整数
print(random.randint(1, 10))
18. 如何使用py删除一个文件?
python 复制代码
import os
file = r'E:/test.txt'
if os.path.exists(file):
    os.remove(file)
    print('delete success')
else:
    print('no such file: %s' % file)
19. 谈谈你对面向对象的理解(三大特性及解释)?

面向对象是一种编程思想,以类的眼光来看待事物的一种方式。将共有的属性和方法的事物封装到同一个类下面。

封装:将共同的属性和方法封装到同一个类下面

  • 第一层面:创建类和对象会分别创建二者的名称空间,我们只能用类名. 或者obj. 的方式去访问里面的名字,这本身就是一种封装
  • 第二层面:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

继承:将多个类的共同属性和方法封装到一个父类下面,然后再用这些类来继承这个类的属性和方法

多态:python天生是支持多态的。指的是基类的同一个方法在不同的派生类中有着不同的功能。

20.面向对象中深度优先和广度优先是什么?

Python的类可以继承多个类,如果继承了多个类,那么其寻找方法的方式有两种:

当类是经典类时,多继承情况下,会按照深度优先方式查找。 py3

当类是新式类时,多继承情况下,会按照广度优先方式查找。 py2

简单点说就是:经典类是纵向查找,新式类是横向查找。

经典类和新式类的区别就是,在声明类的时候,新式类需要加上object关键字。在py3中默认全是新式类。