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

编程范式(二)

作者: 焰火青春 | 来源:发表于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
    

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

    相关文章

      网友评论

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

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