pre:
sqlmap在处理数据的时候大量使用了自定义的AttribDict
属性字典这个数据类型.
1 | # sqlmap paths |
所以有必要看看AttribDict
这个类.
顺便总结一下自己不熟悉的相关知识点.
AttribDict
组成:
这个类通过override了几个super method
.
修改原生的dict
定制成了自己项目需要的属性字典.
定义:
1 | """ |
原来的字典的用法:dict1["key"]
现在的自定义字典的用法:dict1.key
__init__
初始化
1 | def __init__(self, indict=None, attribute=None): |
__setattr__
对一个属性赋值
1 | def __setattr__(self, item, value): |
例如执行以下代码
1 | kb = AttribDict() |
kb = AttribDict()
执行完,也就初始化完kb
这个实例了,对象的属性储存在对象的__dict__
属性中,这个时候它的__dict__
为
1 | {'_AttribDict__initialised': True, 'attribute': None} |
执行kb.a=1
即对实例的属性赋值的时候,就会隐式调用到__setattr__
这个super method.
首先会在__dict__
搜索是否初始化过的标志属性
-
如果未初始化过的话,返回一个
dict
. -
如果初始化过的实例,会根据
key
在__dict__
搜索- 如果这个属性已经设置过的话,会调用
1
2dict.__setattr__(self, item, value)
# 相当于执行`self.itme = value`,对一个属性赋值(覆盖已有属性)-
如果这个属性没设置过的话,会调用
1
2self.__setitem__(item, value)
# 相当于执行`self[key] = val`,对新索引值赋值
__getattr__
访问一个不存在的属性
1 | def __getattr__(self, item): |
1 | kb = AttribDict() |
执行print kb.a
的时候,访问一个不存在的属性的时候,会隐式调用__getattr__
这个super method.
当属性不存在的时候,__getattr__
会raise an AttributeError exception.
-
默认的
__getattr__
的报错:1
AttributeError: 'AttribDict' object has no attribute 'a'
-
修改过的
__getattr__
的报错:1
AttributeError: unable to access item 'a'
额,效果只是让错误输出更友好而已…
__getstate__
序列化
1 | def __getstate__(self): |
代替对象的__dict__属性被保存。
当对象pickled,你可返回一个自定义的状态被保存。当对象unpickled时,这个状态将会被__setstate__
使用。
__setstate__
序列化
1 | def __setstate__(self, dict): |
对象unpickled时,如果__setstate__
定义对象状态会传递来代替用对象的__dict__属性。
这正好跟__getstate__
手牵手:当二者都被定义了,你可以描述对象的pickled状态,任何你想要的。
__deepcopy__
深复制
1 | def __deepcopy__(self, memo): |
对于简单的 object,用 shallow copy 和 deep copy 没区别
复杂的 object, 如 list 中套着 list 的情况,shallow copy 中的 子list,并未从原 object 真的「独立」出来。也就是说,如果你改变原 object 的子 list 中的一个元素,你的 copy 就会跟着一起变。这跟我们直觉上对「复制」的理解不同。
嵌套的复杂object,为了保证数据的独立性,要尽量的使用deepcopy.
好处:
kb作为一个全局的字典,保留了相关的配置信息,很多代码里都会用到这个全局的字典,暂时能想到这样的好处就是
-
大篇幅代码都用到的话,
dict1.key
比dict1["key"]
看起来更简洁.更pythonic. -
可以自定义更加友好的错误输出提示信息.
-
扫描中断需要继续的话,可以自定义序列化的内容,便于自己保存对象.
相关知识点:
special method:
特殊之处在哪呢?
它的特殊之处在于:
如果把Python当成一个framework
的话,这些预留的特殊方法相当于接口interface
.
你可以通过这些接口,使得your-object
就跟built-in object
高度一致,也能复用到built in-object
原有的强大的功能.
有哪些类型的接口呢?
可以在以下这些方面定制你自己的类:
-
构造和初始化
-
控制属性访问
-
创建自定义容器
-
反射
-
可调用的对象
-
上下文管理
-
创建对象描述器
-
复制
什么时候会用到特殊方法呢?
原有的数据类型的功能不能满足你的要求.
例子:
-
你可以像sqlmap这里的
AttribDict
在原生字典的基础上构造自己符合自己项目的数据类型. -
自带的
dict
是无序的,如果你想用有序的字典.你可以用collections
模块的OrderedDict
.而这个OrderedDict
就是在dict
的基础上拓展来的.
__dict__
对象的属性系统:
-
对象的属性储存在对象的
__dict__
属性中 -
__dict__
为一个词典,键为属性名,对应的值为属性本身.
来源:
-
类属性(class attribute)
: 类定义 or 根据类定义继承来的 -
对象属性(object attribute)
: 对象实例定义的
例子:
1 | class bird(object): |
1 | # bird对象属性 比如feather |
有一些属性,比如__doc__
,并不是由我们定义的,而是由Python自动生成。
此外,bird类也有父类,是object类(正如我们的bird定义,class bird(object)
)。
这个object类是Python中所有类的父类。
可以看到,Python中的属性是分层定义的,比如这里分为object/bird/chicken/summer
这四层。
当我们需要调用某个属性的时候,Python会一层层向上遍历,直到找到那个属性。(某个属性可能出现再不同的层被重复定义,Python向上的过程中,会选取先遇到的那一个,也就是比较低层的属性定义)。
当我们有一个summer对象的时候,分别查询summer对象、chicken类、bird类以及object类的属性,就可以知道summer对象所有的__dict__
,就可以找到通过对象summer可以调用和修改的所有属性了.
refs: Python深入03 对象的属性
deep copy和shadow copy:
我们寻常意义的复制就是深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。
而浅复制并不会产生一个独立的对象单独存在,他只是将原有的数据块打上一个新标签,所以当其中一个标签被改变的时候,数据块就会发生变化,另一个标签也会随之改变。这就和我们寻常意义上的复制有所不同了。
对于简单的 object,用 shallow copy 和 deep copy 没区别
复杂的 object, 如 list 中套着 list 的情况,shallow copy 中的 子list,并未从原 object 真的「独立」出来。也就是说,如果你改变原 object 的子 list 中的一个元素,你的 copy 就会跟着一起变。这跟我们直觉上对「复制」的理解不同。
refs: Python-copy()与deepcopy()区别