• MMDetection简单教程:Python基础知识之类的继承、函数修饰器@和*args/**kwargs


            为了看懂基于MMDetection/MMDetection3D的目标检测模型代码,有必要先了解一些重要但平时不常用的python基础知识。

    1.类的继承

            参考:Python中的__init__和super() - 知乎

            python定义类的语句如下:

    1. class ClassName:

             也可在类名后加括号,括号内写上另一个已定义类的名称表示新类继承旧类的属性和方法:

    1. class DerivedClassName(BaseClassName):

            这里DerivedClassName称为子类(派生类),BaseClassName称为父类(基类)。

            假设我们有一个类Fruit,其定义如下:

    1. class Fruit:
    2. def __init__(self, name="Apple"):
    3. self.name = name

            以Fruit作为父类,定义下面的子类:

    1. class Apple(Fruit):
    2. pass

            创建该子类的实例:

    f = Apple()

            使用print函数输出其属性:

    print(f.name)     # 输出Apple

           可以看到,即使在定义Apple类时没有类似name="Apple"的语句,也能获取其name属性的值。这就是因为Apple类继承了其父类Fruit的属性和初始化方法。

            被继承的方法可以重写。例如,新建一个Apple_Init类(仍以Fruit为父类),创建其实例并输出属性:

    1. class Apple_Init(Fruit):
    2. def __init__(self, color):
    3. self.color = color
    4. fi = Apple_Init('red')
    5. print(fi.name) # 该语句会报错说Fruit_Init没有name属性
    6. print(fi.color) # 输出red

            可以看到,由于定义Apple_Init时定义了初始化方法,覆盖了继承自其父类的初始化方法,因此访问属性name失败。

            若要同时继承其父类的初始化方法并添加新的属性,则可以使用super()函数,其语法为

    super(DerivedClassName, self).BaseClassMethodName(*ArgsOfBaseClassMethod)

    表示继承父类BaseClassName的BaseClassMethodName方法(参数为ArgsOfBaseClassMethod)。

            仍以Fruit为父类创建另一Apple_Super类,在继承Fruit类初始化方法的基础上添加新的属性:

    1. class Apple_Super(Fruit):
    2. def __init__(self, name, color):
    3. self.color = color
    4. super(Apple_Super, self).__init__(name)
    5. fs = Apple_Super('Apple','red')
    6. print(fs.name) # 输出Apple
    7. print(fs.color) # 输出red

            可见Apple_Super类的实例同时拥有父类属性name和子类属性color。

    2.函数修饰符@

            参考:mmdetecion 中类注册的实现(@x.register_module())

            假设现在有函数func1,以函数为参数:

    1. def func1(fn):
    2. fn()
    3. print(1)

            假设现在有另一函数func2,功能是在屏幕上打印“2”。我们使用func1修饰func2:

    1. @func1
    2. def func2():
    3. print(2)

    然后执行func2,可观察到依次输出2和1。实际上,经过@的修饰,func2与下面的语句等价:

    1. def func1(fn):
    2. fn()
    3. print(1)
    4. def func2():
    5. print(2)
    6. func1(func2)

            类似的,若func以类为输入和返回值,修饰类my_class:

    1. def func(cls):
    2. print(0)
    3. return cls
    4. @func
    5. class my_class():
    6. def __init__(self):
    7. ...

            直接运行上述程序(注意该程序并没有对类进行实例化),会发现屏幕输出0,说明执行了func中的语句。

            在MMDetection中,自定义模型时用到的@x.register_module()即在运行时调用注册函数(维护一个模块列表,在搭建(build)时从配置文件的type字段取出类别名,然后将剩余字段传入该类中进行初始化)。

    3.*args和**kwargs

            若定义函数时允许函数接收可变数量的参数,可以使用*args或**kwargs。二者区别在于:

            (1)*args会将非键值对参数打包为元组。例如

    1. def func(*args):
    2. print(args)
    3. func("name", "color") # 输出('name', 'color')

            (2)**kwargs会将键值对参数打包为字典。例如

    1. def func(**kwargs):
    2. print(kwargs)
    3. func(name='apple',color='red') # 输出{'name': 'apple', 'color': 'red'}

            注意,在同时使用*args和**kwargs时,*args必须放在**kwargs前面。此外,args和kwargs的名称可以随意修改。

            类似地,在传参时,也可以使用*和**,会自动将传入的列表/元组以及字典分开。例如:

    1. def func(name,color):
    2. print(name,color)
    3. func_inputs = ['apple','red']
    4. func(*func_inputs) # 输出:apple red

    附:其他一些pytorch中可能会遇见的不常用操作

    (1)None作为tensor索引,如

    1. a = torch.zeros(2,3)
    2. print(a[:,None,None].shape) # 输出torch.Size([2,1,1,3])

    即None作为索引时相对于在相应的维度进行一次unsqueeze操作。

    (2)@运算符,即矩阵乘法。

    1. a = torch.zeros(2,3)
    2. b = torch.zeros(3,2)
    3. c = a @ b # 即a和b的矩阵乘积
  • 相关阅读:
    Linux常见问题-获取Vsync信号
    十亿数据如何去重,红黑树到hash再到布隆过滤器
    国家信息安全水平考试NISP证书(一级、二级、三级)
    MySQL数据库:2、MySQL的下载与安装、基本使用、系统服务制作
    HTML5七夕情人节表白网页制作【唯美3D相册】HTML+CSS+JavaScript
    优秀的测试/开发程序员与普通的程序员对比......
    漏洞复现-redis未授权getshell
    HTML CSS JS游戏网页设计作业「响应式高端游戏资讯bootstrap网站」
    使用Nodejs搭建简单的Web网页并实现公网访问
    Spring Cloud Alibaba x AppActive 带来的全新异地活动解决方案
  • 原文地址:https://blog.csdn.net/weixin_45657478/article/details/126624962