Python 面试题

一、Python基础

1 迭代器和生成器(***)

  1. 容器
  • 是一系列元素的集合,str、list、set、dict、file、sockets对象都可以看作是容器
  • 容器都可以被迭代(用在for,while等语句中),因此他们被称为可迭代对象
  1. 可迭代对象: 实现了iter方法,该方法返回一个迭代器对象
  2. 迭代器:
  • 持有一个内部状态的字段,用于记录下次迭代返回值,它实现了next和iter方法
  • 迭代器不会一次性把所有元素加载到内存,而是需要的时候才生成返回结果
  • 如果迭代器中没有更多元素了,则抛出StopIteration异常
  1. 生成器
  • 是一种特殊的迭代器,它的返回值不是通过return而是用yield
  • 懒加载,节省内存和CPU

这个是stackoverflow里python排名第一的问题,值得一看: http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python

这是中文版: http://taizilongxu.gitbooks.io/stackoverflow-about-python/content/1/README.html

这里有个关于生成器的创建问题面试官有考:
问: 将列表生成式中[]改成() 之后数据结构是否改变?
答案:是,从列表变为生成器

>>> L = [x*x for x in range(10)]  # 列表推导式
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x*x for x in range(10))  # 生成器表达式
>>> g
<generator object <genexpr> at 0x0000028F8B774200>

通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含百万元素的列表,不仅是占用很大的内存空间,如:我们只需要访问前面的几个元素,后面大部分元素所占的空间都是浪费的。因此,没有必要创建完整的列表(节省大量内存空间)。在Python中,我们可以采用生成器:边循环,边计算的机制—>generator

2 列表推导式,字典推导式(***)

num_list = [i for i in range(100)]

可能你见过列表推导时,却没有见过字典推导式,在2.7中才加入的:

d = {key: value for (key, value) in iterable}

3 装饰器

  1. 装饰器作用
  • 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,装饰器的作用就是为已经存在的对象添加额外的功能
  • 插入日志
  • 身份认证
  • 性能测试
  • 事务处理
  1. 装饰器类型
  • 普通装饰器(无参数,含参数)
  • 嵌套装饰器
  • 高级装饰器(利用functools)
  1. 装饰器例子
import functools
import time


def log_exec_time(func):
    """
    log_exec_time
    :param func
    :return:
    """

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        res = func(*args, **kwargs)
        end = time.perf_counter()
        print('{0} took {1:.3f}s'.format(func.__name__, end - start))
        return res

    return wrapper


@log_exec_time
def hello():
    """
    hello
    """
    print('hello world!')
    time.sleep(0.1)

    return True


if __name__ == '__main__':
    result = hello()
    print(result)

4 单例模式

​ 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

__new__()__init__()之前被调用,用于生成实例对象。利用这个方法和类的属性的特点可以实现设计模式的单例模式。单例模式是指创建唯一对象,单例模式设计的类只能实例

这个绝对常考啊.绝对要记住1~2个方法,当时面试官是让手写的.

1 使用__new__方法

class Singleton(object):
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            orig = super(Singleton, cls)
            cls._instance = orig.__new__(cls, *args, **kw)
        return cls._instance

class MyClass(Singleton):
    a = 1

2 共享属性

创建实例时把所有实例的__dict__指向同一个字典,这样它们具有相同的属性和方法.


class Borg(object):
    _state = {}
    def __new__(cls, *args, **kw):
        ob = super(Borg, cls).__new__(cls, *args, **kw)
        ob.__dict__ = cls._state
        return ob

class MyClass2(Borg):
    a = 1

3 装饰器版本

def singleton(cls, *args, **kw):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...

4 import方法

作为python的模块是天然的单例模式

# mysingleton.py
class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()

# to use
from mysingleton import my_singleton

my_singleton.foo()

5 进程,线程,协程(**)

python 彻底解读多线程与多进程
知乎被问到了,呵呵哒,跪了

简单点说协程是进程和线程的升级版,进程和线程都面临着内核态和用户态的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需要陷入系统的内核态.

Python里最常见的yield就是协程的思想!可以查看第九个问题.

6 GIL线程全局锁

线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.对于io密集型任务,python的多线程起到作用,但对于cpu密集型任务,python的多线程几乎占不到任何优势,还有可能因为争夺资源而变慢。

Python 最难的问题

解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能).

7 Python里的拷贝(**)

引用和copy(),deepcopy()的区别

import copy
a = [1, 2, 3, 4, ['a', 'b']]  #原始对象

b = a  #赋值,传对象的引用
c = copy.copy(a)  #对象拷贝,浅拷贝
d = copy.deepcopy(a)  #对象拷贝,深拷贝

a.append(5)  #修改对象a
a[4].append('c')  #修改对象a中的['a', 'b']数组对象

print 'a = ', a
print 'b = ', b
print 'c = ', c
print 'd = ', d

输出结果:
a =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
b =  [1, 2, 3, 4, ['a', 'b', 'c'], 5]
c =  [1, 2, 3, 4, ['a', 'b', 'c']]
d =  [1, 2, 3, 4, ['a', 'b']]

8 Python垃圾回收机制(**)

Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。

1 引用计数

PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。

优点:

  1. 简单
  2. 实时性

缺点:

  1. 维护引用计数消耗资源
  2. 循环引用

2 标记-清除机制

基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。

3 分代技术

分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。

Python默认定义了三代对象集合,索引数越大,对象存活时间越长。

举例:
当某些内存块M经过了3次垃圾收集的清洗之后还存活时,我们就将内存块M划到一个集合A中去,而新分配的内存都划分到集合B中去。当垃圾收集开始工作时,大多数情况都只对集合B进行垃圾回收,而对集合A进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合B中的某些内存块由于存活时间长而会被转移到集合A中,当然,集合A中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。

9. async和await

10. Python中如何防止SQL注入(**)

  1. pymysql对SQL特殊字符进行转义
  2. 执行SQL的正确方法,不要在SQL中拼接参数,字符转义只会针对参数args
  3. 使用ORM框架,底层使用pymsql,实现了转义,参数与SQL分开
  4. 使用SQL注入检测工具(sqlmap)

二、Web框架

1. Flask

1. Flask 概述

  1. Flask是一个微内核的python web框架,简单易扩展,werkzeug,jinjia是其中最重要的个库
  2. werkzeug是标准的HTTP和WSGI的工具集,负责核心的逻辑模块,比如路由、请求和应答的封装、WSGI 相关的函数
  3. jinja 负责模板的渲染,主要用来渲染返回给用户的 html 文件内容

2. 蓝图用途

  1. 把一个应用分解为一套蓝图。这是针对大型应用的理想方案:一个项目可以实例化 一个应用,初始化多个扩展,并注册许多蓝图
  2. 在一个应用的 URL 前缀和(或)子域上注册一个蓝图。 URL 前缀和(或)子域的 参数成为蓝图中所有视图的通用视图参数(缺省情况下)
  3. 使用不同的 URL 规则在应用中多次注册蓝图
  4. 通过蓝图提供模板过滤器、静态文件、模板和其他工具。蓝图不必执行应用或视图 函数
  5. 当初始化一个 Flask 扩展时,为以上任意一种用途注册一个蓝图

3. 应用启动流程

  1. app.run()调用werkzeug的run_simple启动WSGI Server
  2. WSGI Server监听在指定的端口,收到 HTTP 请求的时候解析为 WSGI 格式,然后调用 app 去执行处理的逻辑
  3. 应用上下文压栈,全局处理请求函数full_dispatch_request找到通过路由找到处理请求函数dispatch_request,加上请求的 hooks 处理和错误处理的内容
  4. 最后finalize_request 会把它转换成 Response 对象

4. 路由

  1. 通过 @app.route或者app.add_url_rule注册应用 url 对应的处理函数
  2. 每次请求过来的时候,会事先调用路由匹配的逻辑,把路由结果保存起来
  3. dispatch_request 根据保存的路由结果,调用对应的视图函数

5. 上下文(application context 和 request context)

  1. application context (current_app,g)
  2. request context(request,session)
  3. LocalStack
  4. LocalProxy
  5. Local 就是实现了类似 threading.local 的效果——多线程或者多协程情况下全局变量的隔离效果,_get_current_object() 方法获取当前线程或者协程对应的对象
  6. Flask处理请求过程
  • 每次有请求过来的时候,Flask 会先创建当前线程或者协程需要处理的两个重要上下文对象把它们保存到隔离的栈里面
  • 通过路由匹配找到对应的视图函数进行处理,通过线程ID或协程的ID就能直接从栈上获取自己对应上下文的信息
  • 为什么要把 request context 和 application context 分开?每个请求不是都同时拥有这两个上下文信息吗?(设计灵活)
  • 为什么 request context 和 application context 都有实现成栈的结构?每个请求难道会出现多个 request context 或者 application context 吗?(Flask支持多应用,以及测试应用)
  • 为什么要使用 LocalProxy?不使用 LocalProxy 直接访问 LocalStack 的对象会有什么问题吗?(因为 Flask 希望在测试或者开发的时候,允许多 app 、多 request 的情况。而 LocalProxy 也是因为这个才引入进来的)

6. 请求

继承了werkzeug的request,使用了 Mixin进行了多继承

7. 响应

  1. finalize_request 进行最后的处理,在这个方法里就包含了 response 对象的生成和处理逻辑
  2. 一般情况下不要直接操作 Response 对象,而是使用 make_response 方法来生成它
  3. 如果需要使用自定义的响应对象,可以覆盖 flask app 对象的 response_class 属性

8. session

  1. 请求过来的时候,Flask 会根据 cookie 信息创建出 session 变量(如果 cookie 不存在,这个变量有可能为空),保存在该请求的上下文中
  2. 视图函数可以获取 session 中的信息,实现自己的逻辑处理
  3. Flask 会在发送 response 的时候,根据 session 的值,把它写回到 cookie 中

2. Django

django面试题(21道)

3. Tornado

4. Celery

  1. Async Task,Celery beat
  2. Broker:RabbitMQ,Redis
  3. Worker
  4. Backend
  5. 异步任务,定时任务同时启动
celery worker -B -A celery_worker.celery --loglevel INFO
  1. 只启动定时任务(-q指定队列)
celery worker -A celery_worker.celery -l INFO &&
celery beat -A celery_worker.celery -l INFO
  1. json,pickle

5. SQLAchemy

三 数据分析

1. Pandas

2. Numpy

3. Maplotlib

四 运维

1. Docker

1. Docker基本概念
  1. Docker Registry(GitLab)
  2. Docker 镜像(GitLab代码)
  3. Docker容器(本地跑Git克隆下来的项目)
2. 如何优雅的关闭docker 容器里的应用程序?
  1. docker kill
    docker kill 直接发出SIGKILL信号关闭容器。但也可以通过-s参数修改发出的信号。
  2. docker stop
    a. 可以使用 docker stop 命令关闭容器。docker 会默认容器中的应用程序有 10 秒的时间用以终止运行。
    b. 但是,运行在 docker 里的应用程序能够优雅的关闭,关键点在于:应用程序能够接收到 docker 容器关闭的消息,并且在收到消息后,自身进行优雅关机。
  3. docker rm(批量删除:docker prune)
3. docker命令
  1. docker copy:复制文件
  2. docker add:复制并解压文件
  3. docker volume:挂载文件
  4. docker network(bridge,overlay):容器互联(–network)

4. docker实现原理

  1. Docker 使用 Google 公司推出的 Go 语言 (opens new window)进行开发实现,基于 Linux 内核的 cgroup (opens new window),namespace (opens new window),以及 OverlayFS (opens new window)类的 Union FS (opens new window)等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术 (opens new window)。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
  2. 最初实现是基于 LXC (opens new window),从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer (opens new window),从 1.11 版本开始,则进一步演进为使用 runC (opens new window)和 containerd (opens new window)。

2. k8s

1. 概念

Kubernetes 是 Google 团队发起并维护的基于 Docker 的开源容器集群管理系统,它不仅支持常见的云平台,而且支持内部数据中心。
建于 Docker 之上的 Kubernetes 可以构建一个容器的调度服务,其目的是让用户透过 Kubernetes 集群来进行云端容器集群的管理,而无需用户进行复杂的设置工作。系统会自动选取合适的工作节点来执行具体的容器集群调度处理工作。其核心概念是 Container Pod。一个 Pod 由一组工作于同一物理工作节点的容器构成。这些组容器拥有相同的网络命名空间、IP以及存储配额,也可以根据实际情况对每一个 Pod 进行端口映射。此外,Kubernetes 工作节点会由主系统进行管理,节点包含了能够运行 Docker 容器所用到的服务。

正文链接:Python面试题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值