1. 编程范式考察
1.1 面向对象基础及常考题
什么是面向对象
- 把对象作为基本单元,把对象抽象成类,包含成员和方法
- 数据封装、继承(代码复用)、多肽
- 成员,操作成员的方法
1.2 组合与继承
- 组合是使用其他类示例作为自己的类属性(has a 关系)
- 子类继承父类的属性和方法(is a 关系)
- 优先使用组合保持代码简单
1.3 类变量和实例变量区别
- 类变量:所有实例共享
- 实例变量:实例单独享有,不同实例之间不影响
- 当我们需要在一个类的不同实例之间共享变量时使用类变量
1.4 classmethod 和staticmethod 区别
- 都可以通过
class.method()
方式调用 -
classmethod
第一个参数是cls
,可以引用类变量 -
staicmethod
:使用起来和普通函数一样,只不过放在类里面去组织
1.5 元类 Meta Class
创建类的类
- 允许控制类的生成,比如修改类的属性
- 使用 type 来定义元类
- 最常用的一个使用场景就是 ORM 框架
class LowercaseMeta(type):
"""修改类的属性名称为小写的元类"""
def __new__(msc, name, bases, attrs):
lower_attrs = {}
for k, v in attrs.items():
if not k.startswith('__'):
lower_attrs[k.lower()] = v
else:
lower_attrs[k] = v
return type.__new__(msc, name, bases, lower_attrs)
class LowercaseClass(metaclass=LowercaseMeta):
BAR = True
def HELLO(self):
print('hello')
print(dir(LowercaseClass))
运行结果:
# BAR、 HELLO 都变为了小写
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'hello']
1.6 封装、继承和多态
面向对象编程有三大重要特征:封装、继承和多态。
封装
指将数据与具体实现代码封装在某个对象内部,使得这些细节不被外界发现,外界只能通过接口使用该对象,而不能通过任何形式修改对象内部实现。
优点
- 使得代码更易维护
- 因为不能直接调用和修改对象内部私有信息,在一定程度上保证了系统安全性
- 类通过经函数和变量封装在内部,实现了比函数更高一级的封装
class Person:
name = 'rose'
age = 'gender'
def say_hi(self):
return 'Hello'
外部不能直接调用类内部变量或函数,只能通过类的实例对象调用或修改。
继承
继承机制实现了代码的复用,多个类公用的部分可以放在一个类中,其他类要想使用可以直接继承该类即可。
继承最大的好处就是子类获得父类的所有变量和方法的同时,也可以根据自身需要进行修改、拓展
class People:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def speak(self):
return self.name + 'speak say hello'
class Student(People):
def __init__(self, name, age, gender):
# 调用父类的实例方法
People.__init__(self, name, age, gender)
def speak(self):
"""重写父类的方法"""
return 'haha'
Python3 继承查找顺序
- 首先从自己内部查找所需变量或方法,若没有则查找所继承的类
- 按照 深度优先的原则查找,即有多个父类时,会从左至右开始查找,每一个类全部查找完毕,才开始查找下一个类
super()函数
子类中如果有与父类同名的方法或变量,会覆盖父类的。如果想要强制调用父类的方法或变量,可以使用 super()
函数:
super(子类名, self).方法名()
class A:
def __init__(self, name):
self.name = name
print('父类 __init__ 方法被调用')
def show(self):
print('父类 show 方法被调用')
class B(A):
def __init__(self, name):
super(B, self).__init__(name=name)
def show(self):
super(B, self).show()
b = B('rose')
b.name
b.show()
父类 __init__ 方法被调用
父类 show 方法被调用
多肽
一个接口,多种方法, 同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果
多态的三个条件:
- 继承的存在(继承是多态的基础,没有继承就没有多态)
- 子类重写父类的方法(多态下调用子类重写的方法)
- 父类引用变量指向子类对象(子类到父类的类型转换)
重载(overload)和重写(override)是实现多态的两种主要方式。
接口多态性。
继承多态性。
通过抽象类实现的多态性。
class Animal:
def kind(self):
print('I am an animal')
class Dog(Animal):
def kind(self):
print('I am a dog')
class Cat(Animal):
def kind(self):
print('I am a cat')
# 这个函数接收一个animal参数,并调用它的kind方法
def show_kind(animal):
animal.kind()
a = Animal()
d = Dog()
c = Cat()
show_kind(a)
show_kind(d)
show_kind(c)
I am an animal
I am a dog
I am a cat
比较出名的就是鸭子类型,一只鸟走起来像鸭子,叫起来也像鸭子,游泳起来也像鸭子,那就是一只鸭子。
参考:http://www.liujiangblog.com/course/python/44
2. 装饰器
什么是装饰器
一切皆对象,函数也可以当做参数传递,装饰器是接收函数作为参数,添加功能返回一个新函数的类
记录函数耗时的装饰器
import time
def log_time(func):
def _log(*args, **kwargs):
beg = time.time()
res = func(*args, **kwargs)
print('use time:{}'.format(time.time() - beg))
return res
return _log
@log_time
def mysleep():
time.sleep(1)
使用类编写装饰器
import time
class LogTime:
def __call__(self, func):
def _log(*args, **kwargs):
beg = time.time()
res = func(*args, **kwargs)
print('use time:{}'.format(time.time() - beg))
return res
return _log
@LogTime()
def mysleep():
time.sleep(1)
给装饰器增加参数
import time
class LogTimeParams:
def __init__(self, use_int=False):
self.use_int = use_int
def __call__(self, func):
def _log(*args, **kwargs):
beg = time.time()
res = func(*args, **kwargs)
if self.use_int:
print('use time:{}'.format(int(time.time()) - beg))
else:
print('use time:{}'.format(time.time() - beg))
return res
return _log
@LogTime()
def mysleep():
time.sleep(1)
3. 设计模式
设计模式总共分为三类:创建型、行为型、结构型
3.1 创建型模式
- 工厂模式 Factory:解决对象创建问题
- 构造模式 Builder:控制复杂对象的创建
- 原型模式 Prototype:通过原型的克隆创建新的实例
- 单列 Borg/Singleton:一个类只能创建同一个对象
- 对象池模式 Pool:预先分配同一类型的一组实例
- 惰性计算模型 Lazy Evaluation:延迟计算(property)
1、工厂模式
- 解决对象创建问题
- 解耦对象的创建和使用
- 包括工厂方法和抽象工厂
class DogToy:
def speak(self):
print('wang wang')
class CatToy:
def speak(self):
print('miao miao')
def toy_factory(toy_type):
if toy_type == 'dog':
return DogToy()
elif toy_type == 'cat':
return CatToy()
2、 构造模式
- 用来控制复杂对象的构造
- 创建和表示分离,比如你要买电脑, 工厂模式直接给你一个整机电脑
- 构造模式允许你自定义电脑配置,组装后给你
3、原型模式
- 通过克隆原型来创建新的示例
- 可以使用相同的原型,通过修改部分属性来创建新的示例
- 用途:对于一些创建实例开销比较高的地方可以使用原型模式
4、单列模式
实现方式有多重
- 一个类创建出来额对象都是同一个
- 模块其实就是单列的,只会导入一次
- 使用共享同一个实例的方式来创建单列模式
[图片上传失败...(image-a2f03d-1593095673773)]
3.2 结构型模式
- 装饰器模式 Decorator:无需子类话拓展对象功能
- 代理模式 Proxy:把一个对象的操作代理到另一个对象
- 适配器模式Adapter:通过一个间接层适配统一接口
- 外观模型 Facade:简化复杂对象的访问问题
- 享元模式 Flyweight:通过对象复用(池)改善资源利用,比如连接池
- Model_View-Control:MVC 模式 :解耦展示逻辑和业务逻辑
1、代理模式
吧一个对象的操作代理到另一个对象
通常使用 has-a 组合关系
之前实现的 Stack/Queue,把操作代理到 deque 中
2、 适配器模式
想象一个多功能充电头,可以给不同充电器充电,充当了适配器,当要给你不同接口实现统一的接口
class Dog:
def __init__(self):
self.name = 'Dog'
def bark(self):
return 'woof!'
class Cat:
def __init__(self):
self.name = 'cat'
def bark(self):
return 'meow!'
class Adapter:
def __init__(self, oj, **adapted_methods):
self.obj = obj
self.__dict__.update(adapted_methods)
def __getattr__(self, attr):
return getattr(self.obj, attr)
objects = []
dog = Dog()
objects.append(Adapter(dog, make_noise=dog.bark))
cat = Cat()
objects.append(Adapter(cat, make_noise=cat.bark))
for obj in objects:
print('A {0} goes {1}'.format(obj.name, obj.make_noise()))
3.3 行为型模式
- 迭代器模式 Iterator:通过统一的接口迭代对象
- 观察者模式 Observer:对象发生改变时,观察者执行相应动作
- 策略模式 Strategy:针对不同规模输入使用不同策略
1、 迭代器模式
迭代器必须实现 __iter__()/__next__()
方法,可以用 for 循环迭代
2、 观察者模式
- 发布订阅是一种最常用的实现方式
- 发布订阅用于解耦逻辑
- 可以通过回调等方式实现,当发生事件时, 调用相应的函数
3、策略模式
- 根据不同的输入采用不同的策略
- 比如买东西超过 10 个打八折, 超过20个打七折
- 对外暴露统一接口,内部采用不同策略计算
class Order:
def __init__(self, price, discount_strategy=None):
self.price = price
self.discount_strategy = discount_strategy
def price_after_discount(self):
if self.discount_strategy:
discount = self.discount_strategy(self)
else:
discount = 0
return self.price = discount
def __repr__(self):
fmt = "<Price: {}, price after discount: {}>"
return fmt.format(
self.price, self.price_after_discount()
)
def ten_percent_discount(order):
return order.price * 0.10
def on_sale_discount(order):
return order.price * 0.25 + 20
def main():
order0 = Order(100)
order1 = Order(100, discount_strategy=ten_percent_discount)
order2 = Order(1000, discount_strategy=ten_percent_discount)
print(order0)
print(order1)
print(order2)
[图片上传失败...(image-26dbd8-1593095673773)]
4. 函数式编程
- lambda、map、reduce、filter
list(map(lambda x:x*2, range(10)))
什么是闭包
Closure:一个内部函数对外部作用域(而不是全局作用域)的变量进行引用,那么就可以认为这个内部函数时一个闭包
- 绑定了外部作用域的变量的函数
- 即使程序离开外部作用域,如果闭包仍然可见,绑定变量不会销毁
- 每次运行外部函数都会重新创建闭包
def func1():
a = 1
def func2():
# nonlocal a
a = a + 1
return a
return func2
f1 = func1()
print(f1())
如果不指定 nonllocal
关键字,则会报错:
UnboundLocalError: local variable 'a' referenced before assignment
使用场景
- 当闭包执行完后,仍然能够保持住当前的运行环境
- 闭包可以根据外部作用域的局部变量来得到不同的结果, 比如有时我们需要对某些文件的特殊行进行分析,先要提取出这些特殊行
def make_filter(kwd):
def the_filter(file_name):
file = open(file_name)
lines = file.readlines()
file.close()
filter_doc = [i for i in lines if kwd in i]
return filter_doc
return the_filter
filter = make_filter("pass")
filter_result = filter("result.txt")
如果我们需要取得文件 result.txt
中含有 pass
关键字的行
参考文章:https://blog.csdn.net/marty_fu/article/details/7679297
5. 面试题
1、简单介绍项目(主要体现你在项目中充当什么角色)
2、你在项目中遇到最难的部分是什么?怎么解决的?
3、看过 Django admin 源码?看过 flask 源码?如何理解开源?
写过类似 Django admin 的一个组件,叫做 kingadmin
Django 启动时,它会遍历 setting 中 install_app 中所有 app,再通过 __import__
导入所有 app 中的 admin.py 模块。
admin.py 中将所有要注册的模型注册到 site.register 中。
4、MVC / MTV
1、MVC 模式
将 Web 应用分为:模型(M)、控制器(C)以及视图(V)三部分
- 模型:负责业务对象和数据库对象
- 视图:负责与用户的交互(页面)
- 控制器:接收用户的输入调用模型和视图完成用户请求
2、MTV
Django 特有的,也是 MVC 的一种
- 模型(M):负责业务对象与数据库对象
- 模板(T):负责将页面展示给用户
- 视图(V):负责业务逻辑,并调用 M 和 T
还有一个 url 控制器,将一个个 URL 请求分发不同的 view 处理,view 再调用 M 和 T。
[图片上传失败...(image-cbf0af-1593095673773)]
5、缓存怎么用?
数据库、文件、内存、memcached、redis,缓存仅适合页面实时性要求不高的页面
6、中间件是用来干嘛的?
中间件是一个用来处理Django的请求和响应的框架级别的钩子,可以用来处理一些全局性的事情。比如在请求之前干嘛、处理视图函数之前做 csrf 验证等等。
Django 的生命周期是:前端请求--->nginx--->uwsgi.--->中间件--->url路由---->view试图--->orm---->拿到数据返回给view---->试图将数据渲染到模版中拿到字符串---->中间件--->uwsgi---->nginx---->前端渲染。
中间件的作用非常大,可以处理所有的请求内容,中间件其实就是一个类,这个类中一共有5个方法,分别是 process_request, process_response,process_view, process_exception,process_render_template
- process_request:处理请求前,request 对象产生之后,URL 匹配之前调用,返回 None 或 HttpResponse
- process_reponse:处理响应后,视图函数调用之后,所有响应返回客户端之前调用,返回 HttpResponse 对象
- process_view:处理视图前,每个请求上,URL 匹配之后,视图函数调用之前,返回 None 或 HTTPResponse
- process_exception:异常处理,当视图抛出异常时调用,在每个请求上调用,返回一个 HTTPResponse 对象
- process_render_template:在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法,用的比较少。
- 返回 None,则继续调用下一个方法,否则返回给用户
参考:
7、CSRF 是什么?Django 是如何避免的?XSS 呢?
CSRF:跨站请求伪造,当客户端向服务端发起 post 、delete、put 请求时,必须携带相关证明,才能通过。
分为全局和局部避免:
全局:
# 通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成验证
# 中间件中的process_view 方法中验证csrf,首先检查该函数是否设置了@csrf_exempt如果设置了,就免除验证,否则在这里进行csrf_token的验证
局部:
# 装饰器
# @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
# @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
模板中添加:
# 当要向后台发送 post 请求时
# form 表单,添加 {% csrf_token %} 即可
# ajax
# 添加 {% csrf_token %},它会生成一个隐藏的 input 标签,获取其 value 值,然后一起提交到后台
2、XSS是什么?它的全名是:Cross-site scripting,为了和CSS层叠样式表区分所以取名XSS。是一种网站应用程序的安全漏洞攻击,是代码注入的一种。如果允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。
恶意攻击者往Web页面里插入恶意script代码,当用户浏览该页之时,嵌入其中Web里面的script代码会被执行,从而达到恶意攻击用户的目的。
防御措施:
- 当恶意代码值被作为某一标签的内容显示:在不需要html输入的地方对html 标签及一些特殊字符( ” < > & 等等 )做过滤,将其转化为不被浏览器解释执行的字符。django 默认就是过滤HTML标签,
如果我们真的需要有个script显示到前端就用mark_safe
参考文章:https://www.cnblogs.com/sticker0726/articles/10565286.html
8、如果你来设计 login,简单说一下思路
9、session 和 cookie 的联系和区别?为什么说 session 是安全的?
cookie和session都是用来跟踪浏览器用户身份的会话方式
- cookie:存储在客户端浏览器上
- session:存储在服务端上,session 以键值对形式存储,其中键就是 cookie(随机字符串)
因为如果 session 存储在客户端上,可能被伪造,拿来攻击服务器
10、uWSGI 和 Nginx 的作用
- uWSGI: uwsgi是对内的服务器,主要用来处理动态请求
- Nginx:处理静态文件加载,反向代理、负载均衡等,nginx是对外的服务器,外部浏览器通过url访问nginx。nginx接收到浏览器发送过来的http请求,将包进行解析,分析url, a.如果是静态文件请求就直接访问用户给nginx配置的静态文件目录,直接返回用户请求的静态文件, b.如果不是静态文件,而是一个动态的请求,那么nginx就将请求转发给uwsgi。
11、Django 的生命周期
前端请求--->nginx--->uwsgi.--->中间件--->url路由---->view试图--->orm---->拿到数据返回给view---->视图将数据渲染到模版中拿到字符串---->中间件--->uwsgi---->nginx---->前端渲染
**12、__new__()
与__init__()
的区别****
-
__new__()
:产生实例对象,静态方法 -
__init__()
:初始类实例对象,实例方法
class Foo:
def __new__(cls):
return object.__new__(cls)
def __init__(self):
pass
13、 Tornado、Flask 和 Django
Tornado
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
# 请求方式(get、post、delete...)
def get(self):
self.write("Hello, world")
# 路由映射
application = tornado.web.Application([
(r"/index", MainHandler),
])
if __name__ == "__main__":
application.listen(8888) # 监听 8888 端口
tornado.ioloop.IOLoop.instance().start()
非阻塞:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
from tornado import gen
from tornado.concurrent import Future
class AsyncHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
future = Future()
future.add_done_callback(self.doing) # 请求成功,回调函数
yield future
# 或
# tornado.ioloop.IOLoop.current().add_future(future,self.doing)
# yield future
def doing(self, *args, **kwargs):
self.write('async')
self.finish()
application = tornado.web.Application([
(r"/index", AsyncHandler),
])
if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()
Flask
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Index Page"
if __name__ == "__main__":
app.run()
14、谈谈对面向对象的理解?
面向对象是向现实世界模型的自然延伸,是一种 万物皆对象 的编程思想。在现实生活中任务物体都可以归为一类事物,而每个个体都是一类事物的实例。面向对象编程是以对象为中心,以消息为驱动,所以程序=对象+消息。
面向对象编程(OOP):是一种解决软件复用的设计和编程方法,它把软件系统中相似近似的操作逻辑和操作应用数据、状态,以类的形式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。
15、Python 面向对象继承特点?
在调用方法时,总是首先查找对应类型的方法,若不能在派生类中找到对应的方法,才开始到基类中逐个查找。
16、Python 面向对象深度优先和广度优先是什么?
Python
面向对象多继承查找类方法的方式(算法):
-
Python2
经典类:深度优先,纵向查找到最深 -
Python3
新式类:广度优先,横向查找
class A(object):
def test(self):
print('A')
class B(A):
def test(self):
print('B')
class C(A):
def test(self):
print('C')
class D(B):
def test(self):
print('E')
class E(C):
def test(self):
print('E')
class F(D, E):
def test(self):
print('F')
f1 = F()
f1.test()
print(F.__mro__)
# 新式类
F --> D --> B --> E --> C --> A --> object
# 经典类
# F --> D --> B --> A --> E --> C
image
17、类中 __new__、__init__
的区别?
-
__new__
:构造方式,创建类对象,实例化时第一个执行,返回一个创建好的对象及__init__(self)
的self
-
__init__
:初始化方法,实例化类
18、静态方法和类方法区别?
- 静态方法:既不是类中的属性又不使用对象中的属性,由类或者对象调用的方法,依赖装饰器
@staticmethod
来实现 - 类方法:只使用类中的静态变量,一般由类调用:
cls.func()
,依赖装饰器:@classmethod
来实现
class Foo:
name = 'rose'
@staticmethod
def func1(gender):
print(gender)
@classmethod
def func2(cls):
print(cls.name)
f = Foo()
# Foo.func1("female")
f.func1("male")
Foo.func2()
# f.func2()
19、用尽量多方法实现单例模式
基于new() 方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __new__(cls, *args, **kwargs):
if not hasattr(cls, 'instance'):
cls.instance = super().__new__(cls)
return cls.instance
a = Person('p1', 21)
b = Person('p2', 22)
装饰器形式
def sigleton(cls, *args, **kwargs):
instance_dic = {}
def inner(*args, **kwargs):
if cls not in instance_dic:
instance_dic[cls] = cls(*args, **kwargs)
return instance_dic[cls]
return inner
@sigleton
class Person:
pass
20、装饰器的写法及应用场景
def wrapper(func, *args, **kwargs):
def inner(*args, **kwargs):
result = func(*args, **kwargs)
return result
return inner
应用场景:登录测试、插入日志、性能测试、事务处理、缓存等
网友评论