51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1459|回复: 5
打印 上一主题 下一主题

scrapy items源码分析

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2019-6-11 12:22:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
item和field

class Field(dict): # field没什么好说的,就是一个dict
    """Container of field metadata"""  

@six.add_metaclass(ItemMeta)
# item继承自DictItem,又经过ItemMeta这个元类改造
#DictItem是什么呢,就是通过MutableMapping实现的一个字典
#而ItemMeta继承自python中的abc(抽象基类),实现的是对field字段的限定,只有我们在items定义的字段才能允许使用。
class Item(DictItem):
    pass


ItemMeta:

#在Item初始化的时候执行一次,之后每import一个Item都会再执行一次
class ItemMeta(ABCMeta):

    def __new__(mcs, class_name, bases, attrs):
        classcell = attrs.pop('__classcell__', None)
         # 这行是一个cpython3.5以上引入的特性:双下class属性进入元类后自动转变成双下classcell属性
         # 实际代码运行中没发现有什么用

        new_bases = tuple(base._class for base in bases if hasattr(base, '_class'))
        # 每个item都会生成一个_class属性,和下一行生成的对象绑定

        _class = super(ItemMeta, mcs).__new__(mcs, 'x_' + class_name, new_bases, attrs)
                # 将attr加入_class的属性中
                # 加入item继承自其它有独立字段的item,也会一并进入_class的属性中
               
        fields = getattr(_class, 'fields', {})
        new_attrs = {}
        for n in dir(_class):
            v = getattr(_class, n)  # 依次判断每个属性的值是不是dict,如果是的话就把字段名加入fields这个字典
            if isinstance(v, Field):  # 默认的字段的值是个{}
                fields[n] = v
            elif n in attrs:  # 处理_class的一些其他属性
                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) # 生成最终的item对象


DictItem:

import item时,先创建DictItem对象,但不执行任何方法
class DictItem(MutableMapping, BaseItem):

    fields = {}  # 用来存放允许的字段值

        # item的初始化,在新建一个item对象时运行,可以给_values字典一些值,一般用不到。
        # 也就是说初始化时其实可以传参,给一些限制外的字段。。
    def __init__(self, *args, **kwargs): #common init with no para
        self._values = {} # only create _values dict
        if args or kwargs:  # avoid creating dict for most common case
            for k, v in six.iteritems(dict(*args, **kwargs)):
                self[k] = v

    def __getitem__(self, key):
        return self._values[key]

  # 如果值不在field中,就抛出异常
    def __setitem__(self, key, value):
        if key in self.fields:
            self._values[key] = value
        else:
            raise KeyError("%s does not support field: %s" %
                (self.__class__.__name__, key))

    def __delitem__(self, key):
        del self._values[key]

  # 本来字典类型不会有双下getattr,双下setattr这些方法,但是因为继承了BaseItem的缘故,需要覆盖这两个方法
  # 赋值操作直接引起异常
    def __getattr__(self, name):
        if name in self.fields:
            raise AttributeError("Use item[%r] to get field value" % name)
        raise AttributeError(name)

    def __setattr__(self, name, value):
        if not name.startswith('_'):
            raise AttributeError("Use item[%r] = %r to set field value" %
                (name, value))
        super(DictItem, self).__setattr__(name, value)

    def __len__(self):
        return len(self._values)

    def __iter__(self):
        return iter(self._values)

    __hash__ = BaseItem.__hash__

    def keys(self):
        return self._values.keys()

    def __repr__(self):
        return pformat(dict(self))

    def copy(self):
        return self.__class__(self)


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-11-18 10:27 , Processed in 0.062934 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表