一、背景知識
python中兩個屬相相關方法
result = obj.name 會調用builtin函數getattr(obj,'name')查找對應屬性,如果沒有name屬性則調用obj.__getattr__('name')方法,再無則報錯
obj.name = value 會調用builtin函數setattr(obj,'name',value)設置對應屬性,如果設置了__setattr__('name',value)方法則優先調用此方法,而非直接將值存入__dict__並新建屬性
二、nn.Module的__setattr__()方法邏輯
nn.Module中實現了__setattr__()方法,當再class的初始化__init__()中執行module.name=value時,會在其中判斷value是否屬於Parameters或者nn.Module對象,是則將之存儲進入__dict__._parameters和__dict__._modules兩個字典中;如果是其他對象諸如Variable、List、dict等等,則調用默認操作,將值直接存入__dict__中。
示例
nn.Module的新建Parameter屬性,在._parameters中可以查詢到,在.__dict__中沒有,屬於.__dict__._parameters中
import torch as t
import torch.nn as nn
module = nn.Module()
module.param = nn.Parameter(t.ones(2,2))
print(module._parameters)
"""
OrderedDict([('param', Parameter containing:
1 1
1 1
[torch.FloatTensor of size 2x2])])
"""
print(module.__dict__)
"""
{'_backend': <torch.nn.backends.thnn.THNNFunctionBackend at 0x7f5dbcf8c160>,
'_backward_hooks': OrderedDict(),
'_buffers': OrderedDict(),
'_forward_hooks': OrderedDict(),
'_forward_pre_hooks': OrderedDict(),
'_modules': OrderedDict(),
'_parameters': OrderedDict([('param', Parameter containing:
1 1
1 1
[torch.FloatTensor of size 2x2])]),
'training': True}
"""
以通常List的格式傳入的子Module直接從屬於屬於.__dict__,並未被_modules識別
submodule1 = nn.Linear(2,2)
submodule2 = nn.Linear(2,2)
module_list = [submodule1,submodule2]
module.submodules = module_list
print('_modules:',module_list)
# _modules: [Linear (2 -> 2), Linear (2 -> 2)]
print('__dict__[submodules]:',module.__dict__.get('submodules'))
# __dict__[submodules]: [Linear (2 -> 2), Linear (2 -> 2)]
print('__dict__[submodules]:',module.__dict__['submodules'])
# __dict__[submodules]: [Linear (2 -> 2), Linear (2 -> 2)]
以ModuleList格式傳入的子Module可被._modules識別,而不直接從屬於.__dict__
module_list = nn.ModuleList(module_list)
module.submodules = module_list
print(isinstance(module_list,nn.Module))
# True
print(module._modules)
"""
OrderedDict([('submodules', ModuleList (
(0): Linear (2 -> 2)
(1): Linear (2 -> 2)
))])
"""
print(module.__dict__.get('submodules'))
# None
print(module.__dict__['submodules'])
"""
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-19-d4344afabcbf> in <module>()
----> 1 print(module.__dict__['submodules'])
KeyError: 'submodules'
"""
三、屬性查詢函數__getattr__相關特性
nn.Module的.__getattr__()方法會對__dict__._module、__dict__._parameters和__dict__._buffers這三個字典中的key進行查詢。當nn.Module進行屬性查詢時,會先在__dict__進行查詢(僅查詢本級),查詢不到對應屬性值時,就會調用.__getattr__()方法,再無結果就報錯。
示例
對於__dict__中的屬性.training,可以看到.__getattr__('training')查詢時就沒有結果,
print(module.__dict__.get('submodules'))
# None
getattr(module,'training')
# True
module.training
# True
module.__getattr__('training')
"""
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
……
AttributeError: 'Module' object has no attribute 'training'
"""
另外,我們可以看到.__getattr__可以查詢到的結果如下,都是nn.Module自建的屬性,
module.__getattr__
"""
<bound method Module.__getattr__ of Module (
(submodules): ModuleList (
(0): Linear (2 -> 2)
(1): Linear (2 -> 2)
)
)>
"""
對於普通的新建屬性,其實和nn.Module自建的沒什么不同,不同查詢方式輸出相似,
module.attr1 = 2
getattr(module,'attr1')
# 2
module.__getattr__('attr1')
"""
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
……
AttributeError: 'Module' object has no attribute 'attr1'
"""
對於nn.Module的特殊屬性,可以看到,getattr和.__getattr__均可查到,這也是由於getattr一次查找無果后,調用.__getattr__的結果,
getattr(module,'param')
"""
Parameter containing:
1 1
1 1
[torch.FloatTensor of size 2x2]
"""
module.__getattr__('param')
"""
Parameter containing:
1 1
1 1
[torch.FloatTensor of size 2x2]
"""
