0%

Sqlmap源码-AttribDict属性字典

pre:

sqlmap在处理数据的时候大量使用了自定义的AttribDict属性字典这个数据类型.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# sqlmap paths
paths = AttribDict()

# object to store original command line options
cmdLineOptions = AttribDict()

# object to store merged options (command line, configuration file and default options)
mergedOptions = AttribDict()

# object to share within function and classes command
# line options and settings
conf = AttribDict()

# object to share within function and classes results
kb = AttribDict()

所以有必要看看AttribDict这个类.

顺便总结一下自己不熟悉的相关知识点.


AttribDict组成:

这个类通过override了几个super method.

修改原生的dict定制成了自己项目需要的属性字典.


定义:

1
2
3
4
5
6
7
8
9
"""
This class defines the sqlmap object, inheriting from Python data
type dictionary.

>>> foo = AttribDict()
>>> foo.bar = 1
>>> foo.bar
1
"""

原来的字典的用法:dict1["key"]
现在的自定义字典的用法:dict1.key


__init__ 初始化

1
2
3
4
5
6
7
8
9
10
11
12
def __init__(self, indict=None, attribute=None):
if indict is None:
indict = {}

# Set any attributes here - before initialisation
# these remain as normal attributes 在初始化之前设置通用属性
self.attribute = attribute
dict.__init__(self, indict)
self.__initialised = True # 初始化标志

# After initialisation, setting attributes
# is the same as setting an item 初始化之后

__setattr__ 对一个属性赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def __setattr__(self, item, value):
"""
Maps attributes to values
Only if we are initialised 只对初始化过的实例赋值
"""

# This test allows attributes to be set in the __init__ method
# 允许在初始化的时候设置属性
if "_AttribDict__initialised" not in self.__dict__:
return dict.__setattr__(self, item, value)


# Any normal attributes are handled normally
elif item in self.__dict__: # 如果已经设置过了
dict.__setattr__(self, item, value)

else: # 没设置过的
self.__setitem__(item, value)

例如执行以下代码

1
2
kb = AttribDict()
kb.a = 1

kb = AttribDict()执行完,也就初始化完kb这个实例了,对象的属性储存在对象的__dict__属性中,这个时候它的__dict__

1
{'_AttribDict__initialised': True, 'attribute': None}

执行kb.a=1即对实例的属性赋值的时候,就会隐式调用到__setattr__这个super method.

首先会在__dict__搜索是否初始化过的标志属性

  • 如果未初始化过的话,返回一个dict.

  • 如果初始化过的实例,会根据key__dict__搜索

    • 如果这个属性已经设置过的话,会调用
    1
    2
    dict.__setattr__(self, item, value)
    # 相当于执行`self.itme = value`,对一个属性赋值(覆盖已有属性)
    • 如果这个属性没设置过的话,会调用

    1
    2
    self.__setitem__(item, value)
    # 相当于执行`self[key] = val`,对新索引值赋值

__getattr__ 访问一个不存在的属性

1
2
3
4
5
6
7
8
9
10
def __getattr__(self, item):
"""
Maps values to attributes
Only called if there *is NOT* an attribute with this name
"""

try:
return self.__getitem__(item) # 通过key获取value
except KeyError:
raise AttributeError("unable to access item '%s'" % item)
1
2
kb = AttribDict()
print kb.a

执行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
2
def __getstate__(self):
return self.__dict__

代替对象的__dict__属性被保存。

当对象pickled,你可返回一个自定义的状态被保存。当对象unpickled时,这个状态将会被__setstate__使用。


__setstate__ 序列化

1
2
def __setstate__(self, dict):
self.__dict__ = dict

对象unpickled时,如果__setstate__定义对象状态会传递来代替用对象的__dict__属性。

这正好跟__getstate__手牵手:当二者都被定义了,你可以描述对象的pickled状态,任何你想要的。


__deepcopy__ 深复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def __deepcopy__(self, memo):
retVal = self.__class__()
memo[id(self)] = retVal

for attr in dir(self):
if not attr.startswith('_'):
value = getattr(self, attr)
if not isinstance(value, (types.BuiltinFunctionType, types.FunctionType, types.MethodType)):
setattr(retVal, attr, copy.deepcopy(value, memo))

for key, value in self.items():
retVal.__setitem__(key, copy.deepcopy(value, memo))

return retVal

对于简单的 object,用 shallow copy 和 deep copy 没区别

复杂的 object, 如 list 中套着 list 的情况,shallow copy 中的 子list,并未从原 object 真的「独立」出来。也就是说,如果你改变原 object 的子 list 中的一个元素,你的 copy 就会跟着一起变。这跟我们直觉上对「复制」的理解不同。

嵌套的复杂object,为了保证数据的独立性,要尽量的使用deepcopy.


好处:

kb作为一个全局的字典,保留了相关的配置信息,很多代码里都会用到这个全局的字典,暂时能想到这样的好处就是

  • 大篇幅代码都用到的话,dict1.keydict1["key"]看起来更简洁.更pythonic.

  • 可以自定义更加友好的错误输出提示信息.

  • 扫描中断需要继续的话,可以自定义序列化的内容,便于自己保存对象.


相关知识点:

special method:

特殊之处在哪呢?

它的特殊之处在于:

如果把Python当成一个framework的话,这些预留的特殊方法相当于接口interface.

你可以通过这些接口,使得your-object就跟built-in object高度一致,也能复用到built in-object原有的强大的功能.


有哪些类型的接口呢?

可以在以下这些方面定制你自己的类:

  1. 构造和初始化

  2. 控制属性访问

  3. 创建自定义容器

  4. 反射

  5. 可调用的对象

  6. 上下文管理

  7. 创建对象描述器

  8. 复制


什么时候会用到特殊方法呢?

原有的数据类型的功能不能满足你的要求.

例子:

  1. 你可以像sqlmap这里的AttribDict在原生字典的基础上构造自己符合自己项目的数据类型.

  2. 自带的dict是无序的,如果你想用有序的字典.你可以用collections模块的OrderedDict.而这个OrderedDict就是在dict的基础上拓展来的.


__dict__ 对象的属性系统:

  • 对象的属性储存在对象的__dict__属性中

  • __dict__为一个词典,键为属性名,对应的值为属性本身.

来源:

  • 类属性(class attribute): 类定义 or 根据类定义继承来的

  • 对象属性(object attribute): 对象实例定义的

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
class bird(object):
feather = True

class chicken(bird):
fly = False
def __init__(self, age):
self.age = age

summer = chicken(2)

print(bird.__dict__)
print(chicken.__dict__)
print(summer.__dict__)
1
2
3
4
5
6
7
8
# bird对象属性 比如feather
{'__dict__': <attribute '__dict__' of 'bird' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'bird' objects>, 'feather': True, '__doc__': None}

# chicken对象属性 比如fly和__init__方法
{'fly': False, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x2b91db476d70>}

# summer对象属性 比如age
{'age': 2}

有一些属性,比如__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()区别


refs: