新版博客SEO优化基本完成,新老博客内容正在整合中,保证每篇文章高质量。 SiteMap RSS Github
Python面试题---长期更新
嘉美伯爵   2019年7月20日 11:47   面试   Python   219  

常见PEP8 规范

  • 不要在行尾加分号, 也不要用分号将两条命令放在同一行
  • 不要使用反斜杠连接行
  • 不要在返回语句或条件语句中使用括号
  • 顶级定义之间空2行, 方法定义之间空1行,顶级定义之间空两行
  • 如果一个类不继承自其它类, 就显式的从object继承

Python语言的运行机制

i = i+1 和 i += 1

  • 对于不可变数据类型(str、int、tuple)

由于本身是不可变数据类型,执行后都会生产新的对象

x = 1
print(id(x))  # 1510566928
x += 1
print(id(x))  # 1510566960
---------------------------
x = 1
print(id(x))  # 1510566954
x = x + 1
print(id(x)) # # 1510566998
  • 可变数据类型情况(list、dict)

可以看到 使用 += 并不会改变对象的内存地址

x = [1, 2]
print(id(x))  # 2701823038387
x = x + [3, 4]
print(id(x))  # 2701823038334
------------------
x = [1, 2]
print(id(x))  # 2701823038344
x += [3, 4]
print(id(x))  # 2701823038344
  • 注意

n = n + n 作用域问题内部为[1, 2, 1, 2], 外部仍为[1, 2]

def num(n):
    n = n + n
x = [1, 2]
num(x)
print(x)  # [1, 2]
--------------------
def num(n):
    n += n
x = [1, 2]
num(x)
print(x)  # [1, 2, 1, 2]

内建函数

zip

key = [key for key in dict(zip(('a','b','c','d','e'),(1,2,3,4,5))) ]
print(key)
--------------------------------------------------------
['a', 'b', 'c', 'd', 'e']
A0 = dict(zip(('a','b','c','d','e'),(1,2,3,4,5)))
A1 = range(10)
A2 = [i for i in A1 if i in A0]
A3 = [A0[s] for s in A0]
A4 = [i for i in A1 if i in A3]
A5 = {i:i*i for i in A1}
A6 = [[i,i*i] for i in A1]
-------------------------------------------------------------
A0 = {'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4}
A1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
A2 = []
A3 = [1, 3, 2, 5, 4]
A4 = [1, 2, 3, 4, 5]
A5 = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
A6 = [[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81]]

闭包

  • 作用——保存局部信息不被销毁。
def num2(n):
    i = 1
    def num_in():
        nonlocal i
        i = i + n
        print(i)
    return num_in

i = 0
start = num2(3)
while i<5:
    start()
    i += 1
  • lambda 与 闭包
lst = [lambda x: x*i for i in range(4)]
res = [m(2) for m in lst]
-----
def func():
    fun_list = []
    for i in range(4):
        def foo(x):
            return x*i
        fun_list.append(foo)
    return fun_list
for m in func():
  print m(2)
-----
[6, 6, 6, 6]

匿名函数(lambda)

与正常写法相比,使用匿名函数相当简洁

  • map() 遍历所有
a = [1, 2, 3]
x = []
for each in a:
    x.append(each+1)

print(x)
### 使用map(func, iterable)
print(list(map(lambda x: x+1, a)))
  • reduce(func, seq) 积累每次计算的值
def num(x, y):
    return x + y
print(reduce(num, [1, 2, 3, 4]))
--------------------------
print(reduce(lambda x, y: x*y, [1, 2, 3, 4]))
  • filter(func, iterable) 过滤满足条件的值
print(list(filter(lambda x: x%2==0,range(10))))

进程和线程的区别

  • 进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。
  • 线程是程序执行时的最小单位,它是进程的一个执行流。
  • 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵
  • 线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多
  • 进程实现
from multiprocessing import Pool
import time
import random
import os

def work(msg):
    start = time.time()
    print("work{}开始执行,id为{}".format(msg, os.getpid()))
    time.sleep(random.random()*2)
    stop = time.time()
    print("work{}耗时{}.".format(msg, stop-start))

p = Pool()
for i in range(10):
    # 非堵塞运行
    p.apply_async(work, args=(i,))
    # 堵塞进行
    # p.apply(work, args=(i,))


print("开始")
p.close()
p.join()
print("结束")
  • 线程实现
import threading
import os
from time import sleep

def sorry(i):
    print('say sorry  {}'.format(i))
    sleep(1)

if __name__ == '__main__':
    for i in range(1,10):
        t = threading.Thread(target=sorry, args=(i,))
        t.start()

协程

协程之前我们明白Python的进程和线程,这里我们来说一下协程

  • 子程序切换不是线程切换,而是由程序自身控制
  • 没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显
  • 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁

  • 协程实现

def custumer():
    r = ''
    while True:
        n = yield r  # 接受send的值  返出yield的值
        if not n:
            return
        print('custer {}'.format(n))
        r = 'done'

def produce(c):
    c.send(None)  # 启动
    n = 0
    while n < 5:
        n += 1
        print('custer {}'.format(n))
        r = c.send(n)
        print('custer return {}'.format(r))
    c.close()

c = custumer()
produce(c)

GIL

Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意。Python中有一个被称为Global Interpreter Lock(GIL)的东西,它会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常之快,会让你误以为线程是并行执行的,但是实际上都是轮流执行。经过GIL这一道关卡处理,会增加执行的开销。这意味着,如果你想提高代码的运行速度,使用threading包并不是一个很好的方法。 不过还是有很多理由促使我们使用threading包的。如果你想同时执行一些任务,而且不考虑效率问题,那么使用这个包是完全没问题的,而且也很方便。但是大部分情况下,并不是这么一回事,你会希望把多线程的部分外包给操作系统完成(通过开启多个进程),或者是某些调用你的Python代码的外部程序(例如Spark或Hadoop),又或者是你的Python代码调用的其他代码(例如,你可以在Python中调用C函数,用于处理开销较大的多线程工作)。

copy

Python拷贝分为深拷贝和浅拷贝

  • 浅拷贝对子对象不拷贝,深拷贝全部拷贝
l1 = [1, 2, [3, 4]]
l2 = copy.copy(l1)
l1.append(5)
l1[2].append(5)  # 子对象 改变
print(l1)
print(l2)
--------------
[1, 2, [3, 4, 5], 5]
[1, 2, [3, 4, 5]]
  • 深拷贝完是两个完全不相干的对象
l1 = [1, 2, [3, 4]]
l2 = copy.deepcopy(l1)
l1.append(5)
l1[2].append(5)
print(l1)
print(l2)
--------------
[1, 2, [3, 4, 5], 5]
[1, 2, [3, 4]]

垃圾回收机制

  • 引用计数
import sys
# 请在Python解释器下运行  为 2  创建一次 调用一次
str1 = 'hello world'
print(sys.getrefcount(str1))
  • 分代技术
Python默认定义了三代对象集合,索引数越大,对象存活时间越长
Python中使用了某些启发式算法(heuristics)来加速垃圾回收。例如,越晚创建的对象更有可能被回收。对象被创建之后,垃圾回收器会分配它们所属的代(generation)。每个对象都会被分配一个代,而被分配更年轻代的对象是优先被处理的。
  • 引用循环
垃圾回收器会定时寻找这个循环,并将其回收。举个例子,假设有两个对象o1和o2,而且符合o1.x == o2和o2.x == o1这两个条件。如果o1和o2没有其他代码引用,那么它们就不应该继续存在。但它们的引用计数都是1。

is和==

# is 比较的是内存地址   == 比较内容和数据类型
a = [1, 2, 3]
b = a
print(a is b)
print(a == b)

c = copy.deepcopy(a)
print(a is c)
print(a == c)
-------------
True
True
False
True

文件操作

read,readline和readlines

read 读取整个文件
readline 读取下一行,使用生成器方法
readlines 读取整个文件到一个迭代器以供我们遍历

递归输出文件

import os

def print_directory_contents(sPath):
    for sChild in os.listdir(sPath):
        sChildPath = os.path.join(sPath, sChild)
        if os.path.isdir(sChildPath):
            print_directory_contents(sChildPath)
        else:
            print(sChildPath)
print_directory_contents('F:\\GZ\\mxgController')

Fibonacci数列

def fab(n):
    a, b = 0, 1
    while n:
        yield b
        a, b = b, a+b
        n -= 1

内存管理

  • 小整数缓存池
a = 1
b = 1
print(a is b) # True
  • 短字符串
# True
a = "good"
b = "good"
print(a is b)

# False
a = "very good morning"
b = "very good morning"
print(a is b)

# False
a = []
b = []
print(a is b)

函数调用

函数赋值会开辟新空间,即[] 和 [3, 2, 1]内存地址不一样,前者引用,后者覆盖

def f(x, l=[]):
    for i in range(x):
        l.append(i*i)
    print(l)

f(2) # [0, 1]
f(3,[3,2,1]) # [3, 2, 1, 0, 1, 4]  # 自己单独开辟
f(3) # [0, 1, 0, 1, 4]  # 和f(2)共用

三目

if/else

# 若果 a>b 成立  就输出  a-b  否则 a+b
h = a-b if a>b else a+b

and/or

### and 所有值都为真,返回最后一个真;若有一个假,返回第一个假
print(2 and 1 and 3)  # 3
print(1 and 3 and 0 and 4) # 0
print(1 and  0 and 3/0) # 0
### or  所有值都为假,返回最后一个假;若有一个真,返回第一个真
print(0 or 1 or 1/0)
print(0 or '')
### 如果 a<b 返回True 否则返回a+b
print((a-b) and (a<b) or (a+b))

设计模式

单例---类方法方式

class Single():
    def __init__(self, name):
        self.name = name
    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Single, "_instance"):
            Single._instance = Single(*args, **kwargs)
        return Single._instance
s1 = Single.instance('Gage')
s2 = Single.instance()
print(s1)
print(s2)

单例---new方式

class Single(object):
    __isstance = None
    __first_init = False
    def __new__(cls, *args, **kwargs):
        if not cls.__isstance:
            cls.__isstance = object.__new__(cls)
        return cls.__isstance
    def __init__(self, name):
        if not self.__first_init:
            self.name = name
            Singleton.__first_init = True
a = Single('a')
b = Single('b')
print(id(a))
print(id(b))

工厂模式

# 首先定义一个抽象基类
class CarStore(object):

    # 定义生产汽车的方法
    def createcar(self, name):
        pass

    # 根据类型去生产车
    def order(self, name):
        self.car = self.createcar(name)
        self.car.move()


# 定义4s店  实现抽象类
class AoDiCarStore(CarStore):

    def createcar(self, name):
        self.factory = CarFactory()
        return self.factory.createcar(name)


# 创建一个车
class AoDi():
    def move(self):
        print('移动')
# 定义一个工厂
class CarFactory():
    def createcar(self, name):
        self.name = name
        if self.name == 'AoDi':
            self.car = AoDi()
        return self.car
aodi = AoDiCarStore()
aodi.order('AoDi')

正则匹配优先级

Python 面对高并发有哪些解决方法?

  • CDN加速:解决服务器延迟带来的问题,使用户就近取得所需内容
  • 后台数据库使用MySQL+Redis,用户先访问Redis缓存,若为命中,则查询MySQL数据库,随后将其缓存
  • MySQL数据库优化:添加必要的索引、分库分表、读写分离
  • 集群化部署:多台服务器提供相同的服务
  • Nginx负载均衡:降低单台机器的访问负载,降低宕机的可能性

什么是并发和并行?

需要说明的是由于GIL的存在,Python并没有真正的并行,只要并发,这是动态语言所体现的弊端,即Python不支持多进程。

  • 并行:指具有同时处理多个任务的能力,通俗的说就是你可以一边吃饭,一边打电话

  • 并发:指的是在一个时间段可以处理多个任务,通俗的就是说,你正在吃饭,有人打电话,打完电话你又接着吃

Python打乱一个数组

import random
a = [1, 2, 3, 4, 5];
random.shuffle(a)

实现一个Log装饰器

import logging
from functools import wraps

def logged(level, name, message):
    def deactor(func):
        log_name = name if name else func.__name__
        log_message = message if message else ''
        log = logging.getLogger(log_name)
        @wraps(func)
        def wrapper(*args, **kwargs):
            log.log(level, log_message)
            return func(*args, **kwargs)
        return wrapper
    return deactor

@logged(logging.CRITICAL, name='hi', message='log')
def test_log(x, y):
    return x / y

如何反爬虫/爬虫

  • 如何反爬虫
访问频繁封IP
验证码防止爬虫
维护动态IP表
Ajax异步加载
Cookie限制
  • 如何爬虫
使用打码平台
多账号反爬(网站一般对登录用户的请求次数给的多,此时在封之前切换账号)
分布式爬虫
收费动态IP
保存Cookie

参考链接

Python爬虫开发(五):反爬虫措施以及爬虫编写注意事项

简单的并行编程

Python 中的多线程,多进程,并发,并行,同步,通信

数据库读写分离和负载均衡策略

python高并发的解决方案

分布式与集群的区别是什么?