def build_model(cfg):
if isinstance(cfg, list):
modules = [
eval(cfg_.pop("type"))(**cfg_) for cfg_ in cfg
]
return Sequential(*modules)
else:
return eval(cfg.pop("type"))(**cfg)
b站up啥都生维护的分类项目
这段代码的功能是完成模型搭建,其中有三个python的巧妙用法,这里是自己的理解,有不对的地方希望各路大佬指出!
- ①eval语句(将参数变成函数返回)
- ②初始化__init__文件的作用(调包列表)
- ③
*
和**
解包语法(*
是将列表解包为单个元素**
是解包字典) - isinstance和sequential
1.eval 语句
这个函数的功能是将参数变为可执行语句。
例子:
2.**
语法
2.1 单个*
- 2.1.1 单个*表示乘和倍数(用在两个变量中间)
print(3*4) # 输出:12
print('nihao '*3) # 输出:nihao nihao nihao
- 2.1.2 单个*表示组合、解包元组(用在单个变量前)
# 组合
# 接受任意多个参数放在一个元组内
def demo(*p):
print(p)
demo((1,2,3)) # 输出:((1, 2, 3),)
demo(1,2,3) # 输出:(1, 2, 3)
# 解包
# 函数串参时将一个参数解包为三个
def demo(a, b, c):
print(a, b, c)
a1 = ['ni', 'hao', 'a']
b1 = ['da', 'tai', 'yang']
c1 = ['shu', 'ya', 'zi']
demo(a1,b1,c1) # 输出: ['ni', 'hao', 'a'] ['da', 'tai', 'yang'] ['shu', 'ya', 'zi']
# 这句中的*语法就是将a数组的三个元素解包,变为三个字符串分别传给形参a,b,c
demo(*a1) # 输出: ni hao a
# 解包为独立的元素输出
a = [1,2,3,4]
print(*a, sep='--') # 输出:1--2--3--4
2.2 双星号**
- 2.2.1 双星号**表示取幂(用在两个变量之间)
print(7**2) # 输出:49
- 2.2.2双星号**表示组合、解包字典(用在单个变量前)
# 组合
def demo(**p):
return p
print(demo(x=1,y=2,z=6,q=9)) #输出:{'x': 1, 'y': 2, 'z': 6, 'q': 9}
# 解包
dic = {'x':'1','y':'2','z':'6'}
for s in dic:
print(dic.get(s))
st = "{z}---{z}---{z} ".format(**dic)
st2 = '{z}---{x}---{x}---{y}'.format(**dic)
print(st, st2) #输出:6---6---6 6---1---1---2
3.__init__初始化文件
导包的时候from某某import *
*号表示导入该文件夹下所有包,那是如何精确导入呢?
通过在文件夹下定义__init__.py
文件:把该文件夹视作一个模块,当某程序调用该文件夹功能是率先访问__init__.py
文件
当我们使用from某某import *
导包时,首先访问的是__init__.py
文件中的__all__
列表,该列表中的元素就是按顺序对应前面导入的模型。
4 在代码中的解析
- 首先判断传入参数是不是列表
- isinstance
作用:判断是否是同一类型,返回true or false
在这段代码中就是判断cfg是不是列表
isinstance(object, classinfo)
object -- 实例对象。
classinfo -- 可以是直接或间接类名、基本类型或者由它们组成的元组
isinstance() 与 type() 区别:
type() 不会认为子类是一种父类类型,不考虑继承关系。
isinstance() 会认为子类是一种父类类型,考虑继承关系。
如果要判断两个类型是否相同推荐使用 isinstance()。
-
如果是列表
代码使用列表推导式遍历cfg列表,将其中的每个字典元素cfg_处理成一个模块实例,并将所有模块组成一个modules列表。列表中的每个字典元素表示一个模块的配置,其中"type"键对应的值是模块的类名(字符串形式),其他键值对是模块的初始化参数。
eval(cfg_.pop(“type”))这一部分是为了根据字符串形式的模块类名创建模块实例。eval()函数将字符串作为表达式进行求值,从而创建相应的模块实例。pop(“type”)用于从cfg_字典中弹出键为"type"的元素,以便接下来的**cfg_
只包含模块的初始化参数。
最后,使用Sequential(*
modules)将所有的模块组成一个Sequential模型,并将其作为函数的返回值 -
如果不是列表
这里的处理方式与之前类似,但是不同之处在于这里的cfg表示一个模块的配置字典,而不是多个模块的配置列表。代码首先使用eval()根据字符串形式的模块类名创建模块实例,然后将配置参数**cfg传递给模块的初始化函数来创建模块实例,并将其作为函数的返回值。
请注意,由于代码使用了eval()函数,这可能会导致潜在的安全风险,特别是如果cfg的内容来自不受信任的来源。如果可能的话,最好使用其他方法来创建模块实例,比如通过模块类名的映射字典,以避免eval()的使用。
总结起来,这段代码是一个根据传入配置cfg构建神经网络模型的函数,如果cfg是列表,则构建一个由多个模块组成的Sequential模型,如果cfg是字典,则构建单个模块。在创建模块时,cfg中的"type"键对应的字符串表示模块类名,其他键值对表示模块的初始化参数。
例如cfg
是model_cfg的实例化对象如下所示
- 每个模型需要的初始化参数是如何传入的?
主要是return eval(cfg.pop("type"))(**cfg)
pop后字典剩下的键值传入给对应的模型也就是类别class,这也解释了为什么不用get,pop仅让我们找到指定的模型,若全部传入则会报错没有定义type匹配不上.