Python常用框架源码学习笔记

简介: 起因:公司的移动APPsaas后台项目基本稳定,但是总感觉不够精炼,和一些成熟的开源python框架比感觉缺乏美感,总想着重构后台代码,但是做的时候一团乱麻,不知道从何处下手;由于缺乏框架实现的经验,所以打算从使用的几个Python框架入手,先学习别人的框架设计思路;以此为为记,2017年3月31日。

起因:公司的移动APPsaas后台项目基本稳定,但是总感觉不够精炼,和一些成熟的开源python框架比感觉缺乏美感,总想着重构后台代码,但是做的时候一团乱麻,不知道从何处下手;

由于缺乏框架实现的经验,所以打算从使用的几个Python框架入手,先学习别人的框架设计思路;

以此为为记,2017年3月31日。

 

  • pony,一个ORM的mode实现(ORM中M的实现)

pony的mode有点特殊,需要继承Database中的成员类,直接撸关键代码:

class Database(object):

    @cut_traceback
    def __init__(self, *args, **kwargs):
        # argument 'self' cannot be named 'database', because 'database' can be in kwargs
        self.priority = 0
        self._insert_cache = {}

        # ER-diagram related stuff:
        self._translator_cache = {}
        self._constructed_sql_cache = {}
        self.entities = {}
        self.schema = None
        self.Entity = type.__new__(EntityMeta, 'Entity', (Entity,), {})
        self.Entity._database_ = self

        # Statistics-related stuff:
        self._global_stats = {}
        self._global_stats_lock = RLock()
        self._dblocal = DbLocal()

        self.provider = None
        if args or kwargs: self._bind(*args, **kwargs)

 

用户自定义的mode是Database中的Entity变量,这个变量是一个类,实现用户自定义变量的获取的转化处理;这样实现和Database偶合在一起了,即mode实例不能单独存在,必须依附于Database实例。

self.Entity = type.__new__(EntityMeta, 'Entity', (Entity,), {})

 

自己实现的mode继承方式:

class Customer(db.Entity):
    id = PrimaryKey(int, auto=True)
    name = Required(str)
    email = Required(str, unique=True)
    orders = Set("Order")

既然是一个db实例的的db.Entity成员,只不过这个成员比较特殊,是一个类:

继续查看EntityMeta、Entity是何方神圣:

代码太长只摘取关键部分:

class EntityMeta(type):
    def __new__(meta, name, bases, cls_dict):
        if 'Entity' in globals():
            if '__slots__' in cls_dict: throw(TypeError, 'Entity classes cannot contain __slots__ variable')
            cls_dict['__slots__'] = ()
        return super(EntityMeta, meta).__new__(meta, name, bases, cls_dict)
    @cut_traceback
    def __init__(entity, name, bases, cls_dict):
        super(EntityMeta, entity).__init__(name, bases, cls_dict)
        .......
     # 查找mode中用户自定义属性,并根据属性类型做转化从而适配数据库,具体看Attribute类; direct_bases
= [ c for c in entity.__bases__ if issubclass(c, Entity) and c.__name__ != 'Entity' ] entity._direct_bases_ = direct_bases base_attrs = [] for base in direct_bases: for a in base._attrs_: prev = base_attrs_dict.get(a.name) if prev is None: base_attrs_dict[a.name] = a base_attrs.append(a) entity._base_attrs_ = base_attrs new_attrs = [] for name, attr in items_list(entity.__dict__): if name in base_attrs_dict: throw(ERDiagramError, "Name '%s' hides base attribute %s" % (name,base_attrs_dict[name])) if not isinstance(attr, Attribute): continue if name.startswith('_') and name.endswith('_'): throw(ERDiagramError, 'Attribute name cannot both start and end with underscore. Got: %s' % name) if attr.entity is not None: throw(ERDiagramError, 'Duplicate use of attribute %s in entity %s' % (attr, entity.__name__)) attr._init_(entity, name) new_attrs.append(attr)
     # 按照定义的顺序排序
     new_attrs.sort(key=attrgetter('id'))
        # 完成属性的收集
     entity._new_attrs_ = new_attrs
entity._attrs_ = base_attrs + new_attrs
      entity._adict_ = {attr.name: attr for attr in entity._attrs_}
 

 

用户调用接口:

    @cut_traceback
    def __getitem__(entity, key):
        if type(key) is not tuple: key = (key,)
        if len(key) != len(entity._pk_attrs_):
            throw(TypeError, 'Invalid count of attrs in %s primary key (%s instead of %s)'
                             % (entity.__name__, len(key), len(entity._pk_attrs_)))
        kwargs = {attr.name: value for attr, value in izip(entity._pk_attrs_, key)}
        return entity._find_one_(kwargs)
Entity是以EntityMeta为元类的一个类,主要处理数据库中的复杂关系:
class Entity(with_metaclass(EntityMeta)):
    .......
上面的定义和下面等价:
class Entity(object):
    __metaclass__ = EntityMeta

这样写是为了兼容py2和py3的差异:

py3中的语法为:

class MyClass(metaclass=Meta):
    pass

 

由于牵涉到元类的使用,实现难度:4颗星

关键:捕获用户自定义变量,实现底层存储和转化的封装,常用户ORM的M层实现。

总结:要实现子类成员的收集分以下3步,

1、需要实现自己的元类;

2、对子类类型进行判断,同类型属性合并

4、对外实现接口,如:__getitem__,__setter__

元类的使用可以参考:http://blog.jobbole.com/21351/

 

下面对比infi.clickhouse_orm中M的实现方式: 

第一步:创建自己的元类

class ModelBase(type):
    '''
    A metaclass for ORM models. It adds the _fields list to model classes.
    '''

    ad_hoc_model_cache = {}

    def __new__(cls, name, bases, attrs):
        new_cls = super(ModelBase, cls).__new__(cls, name, bases, attrs)
        # Collect fields from parent classes
        base_fields = []
        for base in bases:
            if isinstance(base, ModelBase):
                base_fields += base._fields
        # Build a list of fields, in the order they were listed in the class
        fields = base_fields + [item for item in attrs.items() if isinstance(item[1], Field)]
        fields.sort(key=lambda item: item[1].creation_counter)
        setattr(new_cls, '_fields', fields)
        return new_cls

 

其中,_fields存放用户自定义(类)属性:

第二步:实现M的基类,提供对外调用的接口

class Model(with_metaclass(ModelBase)):
    '''
    A base class for ORM models.
    '''

    engine = None
    readonly = False

    def __init__(self, **kwargs):
        '''
        Creates a model instance, using keyword arguments as field values.
        Since values are immediately converted to their Pythonic type,
        invalid values will cause a ValueError to be raised.
        Unrecognized field names will cause an AttributeError.
        '''
        super(Model, self).__init__()

        self._database = None

        # Assign field values from keyword arguments
        for name, value in kwargs.items():
            field = self.get_field(name)
            if field:
                setattr(self, name, value)
            else:
                raise AttributeError('%s does not have a field called %s' % (self.__class__.__name__, name))
        # Assign default values for fields not included in the keyword arguments
        for name, field in self._fields:
            if name not in kwargs:
                setattr(self, name, field.default)

    def __setattr__(self, name, value):
        '''
        When setting a field value, converts the value to its Pythonic type and validates it.
        This may raise a ValueError.
        '''
        field = self.get_field(name)
     # 当field没有被覆盖,还是Field类型
if field: value = field.to_python(value, pytz.utc) field.validate(value)
     # 如果已经被覆盖,直接覆盖(此处有bug,初次赋值对类型做检查,再次赋值不会对类型做检查) super(Model, self).
__setattr__(name, value) def get_field(self, name): ''' Get a Field instance given its name, or None if not found. ''' field = getattr(self.__class__, name, None) return field if isinstance(field, Field) else None

其中:

__init__提供类似ModeSome(**kwargs)的构建方式,_fields的作用1、初始化时设置默认值,2、在类级别保存Field,因为ModeSome(**kwargs)及__setattr__会覆盖Field属性。

__setattr__提供类似字典赋值的接口,

 

在抓取界赫赫有名的Scrapy中的用户自定义Item也用到了ORM模型的思想:

大家感受一下scrapy中元类的实现方式:

class ItemMeta(ABCMeta):

    def __new__(mcs, class_name, bases, attrs):
        classcell = attrs.pop('__classcell__', None)
        new_bases = tuple(base._class for base in bases if hasattr(base, '_class'))
        _class = super(ItemMeta, mcs).__new__(mcs, 'x_' + class_name, new_bases, attrs)

        fields = getattr(_class, 'fields', {})
        new_attrs = {}
        for n in dir(_class):
            v = getattr(_class, n)
            if isinstance(v, Field):
                fields[n] = v
            elif n in attrs:
                new_attrs[n] = attrs[n]

        new_attrs['fields'] = fields
        new_attrs['_class'] = _class
        if classcell is not None:
            new_attrs['__classcell__'] = classcell
        return super(ItemMeta, mcs).__new__(mcs, class_name, bases, new_attrs)

元类继承了ABCMeta而来,子类的区分方式是根据是否包含_class变量来区分的

scrapy中Item的父类:

live_refs = defaultdict(weakref.WeakKeyDictionary)


class object_ref(object):
    """Inherit from this class (instead of object) to a keep a record of live
    instances"""

    __slots__ = ()

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        live_refs[cls][obj] = time()
        return obj


class BaseItem(object_ref):
    """Base class for all scraped items."""
    pass

Item的实现:

class DictItem(MutableMapping, BaseItem):

    fields = {}

    def __init__(self, *args, **kwargs):
        self._values = {}
        if args or kwargs:  # avoid creating dict for most common case
            for k, v in six.iteritems(dict(*args, **kwargs)):
                self[k] = v ........... @six.add_metaclass(ItemMeta) class Item(DictItem): pass

 

Item复用了MutableMapping类,其行为更像python原生字典。

  • pony加载不同的provider实现(动态创建实例)

 

def _bind(self, *argv, **kwargs):

        if self.provider is not None:
            throw(TypeError, 'Database object was already bound to %s provider' % self.provider.dialect)
        if args: provider, args = args[0], args[1:]
        elif 'provider' not in kwargs: throw(TypeError, 'Database provider is not specified')
        else: provider = kwargs.pop('provider')
        if isinstance(provider, type) and issubclass(provider, DBAPIProvider):
            provider_cls = provider
        else:
            if not isinstance(provider, basestring): throw(TypeError)
            if provider == 'pygresql': throw(TypeError,
                'Pony no longer supports PyGreSQL module. Please use psycopg2 instead.')
            provider_module = import_module('pony.orm.dbproviders.' + provider)
            provider_cls = provider_module.provider_cls
        self.provider = provider = provider_cls(*args, **kwargs)

关键代码:

provider_module = import_module('pony.orm.dbproviders.' + provider)
provider_cls = provider_module.provider_cls
self.provider = provider = provider_cls(*args, **kwargs)
provider由是用户输入的标识字符串,所有的provider模块对外统一接口名:provider_cls

以SQLite模块为例:

provider_cls = SQLiteProvider

调用举例:

db = Database("sqlite", "demo.sqlite", create_db=True)

 

实现技巧:1颗星

适用场景,通过标识创建所需要的对象,工场模式。

 

目录
相关文章
|
23天前
|
数据采集 存储 JSON
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第27天】本文介绍了Python网络爬虫Scrapy框架的实战应用与技巧。首先讲解了如何创建Scrapy项目、定义爬虫、处理JSON响应、设置User-Agent和代理,以及存储爬取的数据。通过具体示例,帮助读者掌握Scrapy的核心功能和使用方法,提升数据采集效率。
66 6
|
23天前
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
124 45
|
17天前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
63 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
6天前
|
缓存 API 数据库
Python哪个框架合适开发速卖通商品详情api?
在跨境电商平台速卖通的商品详情数据获取与整合中,Python 语言及其多种框架(如 Flask、Django、Tornado 和 FastAPI)提供了高效解决方案。Flask 简洁灵活,适合快速开发;Django 功能全面,适用于大型项目;Tornado 性能卓越,擅长处理高并发;FastAPI 结合类型提示和异步编程,开发体验优秀。选择合适的框架需综合考虑项目规模、性能要求和团队技术栈。
18 2
|
1月前
|
网络协议 Java Linux
PyAV学习笔记(一):PyAV简介、安装、基础操作、python获取RTSP(海康)的各种时间戳(rtp、dts、pts)
本文介绍了PyAV库,它是FFmpeg的Python绑定,提供了底层库的全部功能和控制。文章详细讲解了PyAV的安装过程,包括在Windows、Linux和ARM平台上的安装步骤,以及安装中可能遇到的错误和解决方法。此外,还解释了时间戳的概念,包括RTP、NTP、PTS和DTS,并提供了Python代码示例,展示如何获取RTSP流中的各种时间戳。最后,文章还提供了一些附录,包括Python通过NTP同步获取时间的方法和使用PyAV访问网络视频流的技巧。
248 4
PyAV学习笔记(一):PyAV简介、安装、基础操作、python获取RTSP(海康)的各种时间戳(rtp、dts、pts)
|
1月前
|
Python
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
使用Python的socket库实现客户端到服务器端的图片传输,包括客户端和服务器端的代码实现,以及传输结果的展示。
140 3
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
|
24天前
|
数据采集 前端开发 中间件
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第26天】Python是一种强大的编程语言,在数据抓取和网络爬虫领域应用广泛。Scrapy作为高效灵活的爬虫框架,为开发者提供了强大的工具集。本文通过实战案例,详细解析Scrapy框架的应用与技巧,并附上示例代码。文章介绍了Scrapy的基本概念、创建项目、编写简单爬虫、高级特性和技巧等内容。
52 4
|
1月前
|
JSON 数据格式 Python
Socket学习笔记(一):python通过socket实现客户端到服务器端的文件传输
本文介绍了如何使用Python的socket模块实现客户端到服务器端的文件传输,包括客户端发送文件信息和内容,服务器端接收并保存文件的完整过程。
154 1
Socket学习笔记(一):python通过socket实现客户端到服务器端的文件传输
|
24天前
|
安全 数据库 开发者
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第26天】本文详细介绍了如何在Django框架下进行全栈开发,包括环境安装与配置、创建项目和应用、定义模型类、运行数据库迁移、创建视图和URL映射、编写模板以及启动开发服务器等步骤,并通过示例代码展示了具体实现过程。
35 2
|
24天前
|
网络协议 物联网 API
Python网络编程:Twisted框架的异步IO处理与实战
【10月更文挑战第26天】Python 是一门功能强大且易于学习的编程语言,Twisted 框架以其事件驱动和异步IO处理能力,在网络编程领域独树一帜。本文深入探讨 Twisted 的异步IO机制,并通过实战示例展示其强大功能。示例包括创建简单HTTP服务器,展示如何高效处理大量并发连接。
40 1
下一篇
无影云桌面