Python面试基础(二)

2019-09-14  本文已影响0人  VienFu

1. 装饰器之functools.wraps

装饰器很好用极大地复用了代码,但是不可否认在给原函数使用装饰器后原函数的一些元数据信息比如函数名、注释、参数列表不见了,比如swagger api文档函数注释功能不可见,比如django rest_framework视图某个接口下@detail_route()再加上一个其他功能的装饰器甚至没法自动生成路由等。好在有functools.wraps解决了这个问题,wraps也是一个装饰器,它能把原函数的信息拷贝到装饰器里边的函数中,具体用法可参照如下,也建议写装饰器都这么写:

from functools import wraps

def test_dec00(func):
  @wraps(func)
  def wrapper(*args, **kwargs):
    ...
    func(*args, **kwargs)
    ...
  return wrapper

2. Django之日志处理

Django采用python内置的logging模块来处理系统日志,而一个python logging配置至少包括以下三部分:

其实日志系统还包含一个组件filters,提供更细粒度的日志过滤功能,不过实际采用中一般不多(因为上述三个组件基本上可以满足我对日志的所有需求),这里就不再赘述。

下面举例来说明一下Django配置logging日志处理的常规做法:

# log_conf.py
logging_conf = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'simple': {
            'format': '%(levelname)s - %(asctime)s - %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S'
        },
        'console': {
            'format':'%(levelname)s: %(asctime)s -- %(pathname)s:%(lineno)d - %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S'
        }
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'console'
        },
        'fileHandler': {
            'level': 'INFO',
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'formatter': 'simple',
            'filename': '/Users/vienfu/test_logs/test_error.log'
        }
    },
    'loggers': {
        'mdjango': {
            'handlers': ['console', 'fileHandler'],
            'level': 'INFO',
            'propagate': False
        }

    },
    'root': {
        'level': 'WARNING',
        'handlers': ['console', 'fileHandler'],
    }
}
# settings.py
# Django Logging Settings
from log_conf import logging_conf
LOGGING = logging_conf
import logging
from rest_framework.viewsets import ViewSet

logger = logging.getLogger(__name__)
# logger = logging.getLogger('mdjango')  一般这两种来实例化日志记录器,前者呢常会对应到root logger,而后则指明具体的logger

class TestViewSet(ViewSet):
  def list(self, request):
    ...
    logger.error('this is a demo logging error test')
    ...

如此,Django下日志配置及处理就基本上完成了。

3. Django之自定义错误码

4. Python中下划线命名含义

5. 鸭子类型

先引用一句关于python鸭子模式最经典的话:如果一只鸟走起路来像鸭子,叫声也跟鸭子一样,然后游泳也跟鸭子一样,那么这只鸟就是鸭子。

6. Python新式类与旧式类

# 新、旧式类的定义
class A(object):  # 新式类的定义
  pass
class A:  # 旧式类的定义
  pass

# 多重继承下的MRO(Method Resolution Order)的问题,举个栗子:
class E(object):  # 新式类
# class E:  # 旧式类
    def __init__(self):
        print('welcome to class E')

    def extra(self):
        print('welcome to extral E')

class F(E):
    def __init__(self):
        print('welcome to class F')

    def extra(self):
        print('welcome to extral F')

class G(E):
    def __init__(self):
        print('welcome to class G')

class H(G, F):
    def __init__(self):
        print('welcome to class H')

if __name__ == '__main__':
    class_h = H()
    class_h.extra()

# 输出结果:
welcome to class H
welcome to extral F

但如果把例子中新式类换成旧式类,结果就会变成:
welcome to class H
welcome to extral E
这是由各自的MRO导致,新式类MRO是H->G->F->A(先左后右,广度优先),而旧式类则是H->G->E->F(先左后右,深度优先)。

7. __new__和__init__的区别

举一个通过__new__构建单例模式的栗子:

class Singleton(object):
  _instance = None
  def __new__(cls, *args, **kwargs):
    if not cls._instance:
      cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
    return cls._instance

class myclass(Singleton):
  pass

扩展

这里顺便说一下__call__的用法,如果在一个类中实现了__call__方法,则该类的实例变成可调用的,举个简单的例子:

# -*- coding: utf-8 -*-

class TestClass(object):
    def __init__(self):
        print('Just test __call__')

    def __call__(self):
        print('testing __call__ func')

if __name__ == '__main__':
    class_A = TestClass()
    class_A()  #  实例可以直接是可调用的

通过这种方式可以实现基于类的装饰器,在类里面记录状态实现获取函数的状态等。

8. MetaClass(元类)

python中一切皆为对象,可以这样理解:类的实例对象是类的实例化的结果,而类则是元类的实例化,可以通过自定义元类来创建类并改变类的行为。python默认的元类是type,所有对象归根结底都是有它创建出来的,又被叫做类工场,当然也可以通过继承type自定义一个元类,下面是一个通过元类来实现单例模式的例子:

# -*- coding: utf-8 -*-
# test_metaclass01.py
# 通过元类来实现单例模式

class Singleton(type):
    def __init__(self, *args, **kwargs):
        self._instance = None
        super(Singleton, self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        if self._instance is None:
            self._instance = super(Singleton, self).__call__(*args, **kwargs)
        return self._instance

class TestSingleton(object):
    __metaclass__ = Singleton

    def __init__(self):
        print('just a test')

9. 猴子补丁

属性(类、模块、方法等)在运行状态下的动态替换,就叫猴子补丁,一般在动态编程语言中使用,比如python中类,下面举一个简单的例子:

# -*- coding: utf-8 -*-

class OneClass(object):

    def __init__(self):
        print('Just a Monkey_Patch test')

    def echo_something(self, value):
        print('welcome {}'.format(value))

def show_something(ins, value):
    print('Start testing Monkey_Patch ...')
    print('welcome {}'.format(value))
    print('Test Success.')

if __name__ == '__main__':
    class_A = OneClass()
    class_A.echo_something(666)
    OneClass.echo_something = show_something
    class_A.echo_something(666)

运行结果如下:

总之,猴子补丁的功能就是运行时动态地增加或替换模块的属性,实际使用中,比如想引用公司通用库的同时要做一些功能的拓展,除了继承实现外还可以考虑猴子补丁。

10. 抽象基类

ABC(abstract bases class),定义了一组行为,不必实现,不能够直接实例化,一般基于它的子类需要重写函数实现具体的接口,它一般通过如下两种途径来使用:

Python中实现抽象基类的模块:abc模块,它里面定义的一个叫ABCMeta的元类以及上文提到抽象方法、抽象属性等装饰器,如果自定义一个抽象基类可参考如下方法:

# python 3.x
class MyAbstractClass(object, metaclass=abc.ABCMeta):
  pass

# python 2.x
class MyAbstractClass(object):
  __metaclass__ abc.ABCMeta
  pass

# 其实跟前文使用元类的方法完全一样!!
# 还有一个兼容python 2.x和3.x方法,使用six模块
@six.add_metaclass(abc.ABCMeta)
class MyAbstractClass(object):
  pass

11. 协程

11.1 yield VS yield from

直接看个实例来理解吧:

# -*- coding: utf-8 -*-

# 斐波那契数列
def fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n += 1

def warpper(func):
    print('start')
    # for item in func(5):
    #    yield item
    yield from func(5)  # 这一行作用相当于上面注释的两行语句
    print('end')

if __name__ == '__main__':
    test_gen = warpper(fab)
    for item in test_gen:
        print(item)

11.2 Python协程的发展

11.3 asyncio VS gevent

上一篇下一篇

猜你喜欢

热点阅读