美文网首页
编程范式(二)

编程范式(二)

作者: 焰火青春 | 来源:发表于2020-06-25 22:35 被阅读0次

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

应用场景:登录测试、插入日志、性能测试、事务处理、缓存等

相关文章

  • 编程范式(二)

    1. 编程范式考察 1.1 面向对象基础及常考题 什么是面向对象 把对象作为基本单元,把对象抽象成类,包含成员和方...

  • 从一个加法器看简单的编程范式

    从一个加法器的实现看简单的编程范式 编程范式 编程范式(Programming Paradigm)是某种编程语言典...

  • 兄弟会0805 编程的思维

    编程的思维 1.指令式编程思维 2. 函数式编程思维 编程范式 函数式编程是一种编程范式,我们常见的编程范式有命令...

  • 06 - 面向过程 VS 面向对象

    编程范式中,除了面向对象之外,被大家熟知的编程范式还有另外两种,面向过程编程和函数式编程。面向过程这种编程范式随着...

  • 函数式编程基本概念

    编程范式 函数式编程(Function Programming, FP)是编程范式之一(还包括面向对象编程、面向过...

  • 函数式编程简析

    想要知道什么是函数式编程就需要先理解什么是编程范式 编程范式是什么? 编程范型、编程范式或程序设计法(英语:Pro...

  • 从Swift函数式编程到设计模式(二)

    Swift是一门多范式编程语言。除了支持面向对象编程范式之外,还支持函数式编程范式,和泛型编程。这使得Swift可...

  • 编程范式 (Programming paradigm)

    编程范式 Programming paradigm 范,模范、典范也。范式即模式、方法。常见的编程范式有:函数式编...

  • Swift和OC的区别

    一、编程范式 Swift可以面向协议编程、面向函数编程、面向对象编程。 OC主要是面向对象编程。 二、类型安全 S...

  • 探秘Spring AOP

    编程范式概览 面向过程编程 面向对象编程 函数式编程 事件驱动编程 面向切面编程 AOP是什么 是一种编程范式,不...

网友评论

      本文标题:编程范式(二)

      本文链接:https://www.haomeiwen.com/subject/alvefktx.html