转载https://blog.csdn.net/zyh19980527/article/details/107206483/
相信大家在很多场合特别是写神经网络的代码的时候都看到过下面的这种代码:

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. class Net(nn.Module):
  5. def __init__(self):
  6. super(Net, self).__init__()
  7. # 输入图像channel:1;输出channel:6;5x5卷积核
  8. self.conv1 = nn.Conv2d(1, 6, 5)
  9. self.conv2 = nn.Conv2d(6, 16, 5)
  10. # an affine operation: y = Wx + b
  11. self.fc1 = nn.Linear(16 * 5 * 5, 120)
  12. self.fc2 = nn.Linear(120, 84)
  13. self.fc3 = nn.Linear(84, 10)
  14. def forward(self, x):
  15. # 2x2 Max pooling
  16. x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
  17. # 如果是方阵,则可以只使用一个数字进行定义
  18. x = F.max_pool2d(F.relu(self.conv2(x)), 2)
  19. x = x.view(-1, self.num_flat_features(x))
  20. x = F.relu(self.fc1(x))
  21. x = F.relu(self.fc2(x))
  22. x = self.fc3(x)
  23. return x

大家可能刚开始会有点疑惑的是下面的这三行代码是干什么用的,要看懂这三行代码需要了解三个东西:

  • self参数
  • __ init__ ()方法
  • super(Net, self).init()

接下来就为大家逐一讲解一下。

  1. def __init__(self):
  2. super(Net, self).__init__()
  3. def forward(self, x):

一、self参数

self指的是实例Instance本身,在Python类中规定,函数的第一个参数是实例对象本身,并且约定俗成,把其名字写为self,也就是说,类中的方法的第一个参数一定要是self,而且不能省略。
我觉得关于self有三点是很重要的:

  1. self指的是实例本身,而不是类
  2. self可以用this替代,但是不要这么去写
  3. 类的方法中的self不可以省略

首先第一点self指的是实例本身,而不是类

  1. class Person():
  2. def eat(self):
  3. print(self)
  4. Bob=Person()
  5. Bob.eat()
  6. print(Person)

看输出的结果我们可以看到,self指的是实例
image.png
第二点self可以用this替代,但是不要这么去写,其实我理解self就相当于Java中的this,我们试着换一下

  1. class Person():
  2. def eat(this):
  3. print(this)
  4. Bob=Person()
  5. Bob.eat()
  6. print(Person)

image.png
是没有报错的,但是大家还是按照规范用self

第三点类的方法中的self不可以省略,看下面的代码,pycharm自动提示需要参数self。

二、 init ()方法

在python中创建类后,通常会创建一个__ init__ ()方法,这个方法会在创建类的实例的时候自动执行。__ init__ ()方法必须包含一个self参数,而且要是第一个参数。

比如下面例子中的代码,我们在实例化Bob这个对象的时候,__ init__ ()方法就已经自动执行了,但是如果不是 __ init__ ()方法,比如说eat()方法,那肯定就只有调用才执行

  1. class Person():
  2. def __init__(self):
  3. print("是一个人")
  4. def eat(self):
  5. print("要吃饭" )
  6. Bob=Person()

image.png
再比如说下面的代码,如果 __ init__ ()方法中还需要传入另一个参数name,但是我们在创建Bob的实例的时候没有传入name,那么程序就会报错, 说我们少了一个__ init__ ()方法的参数,因为__ init__ ()方法是会在创建实例的过程中自动执行的,这个时候发现没有name参数,肯定就报错了

  1. class Person():
  2. def __init__(self,name):
  3. print("是一个人")
  4. self.name=name
  5. def eat(self):
  6. print("%s要吃饭" %self.name)
  7. Bob=Person()
  8. Bob.eat()

image.png
传入了Bob之后就不会了,而且eat方法也可以使用name这个参数。

  1. class Person():
  2. def __init__(self,name):
  3. print("是一个人")
  4. self.name=name
  5. def eat(self):
  6. print("%s要吃饭" %self.name)
  7. Bob=Person('Bob')
  8. Bob.eat()

image.png
这样我们其实就比较清晰的知道什么东西需要在__ init__ ()方法中定义了,就是希望有一些操作是在创建实例的时候就有的时候,比如说下面的这个代码,其实就应该把money这个量定义在__ init__ ()方法中,这样就不需要在执行eat()方法后再执行qian()方法。或者说我们写神经网络的代码的时候,一些网络结构的设置,也最好放在__ init__ ()方法中。

  1. class Person():
  2. def __init__(self,name):
  3. print("是一个人")
  4. self.name=name
  5. def eat(self,money):
  6. print("%s要吃饭" %self.name)
  7. self.money=money
  8. def qian(self):
  9. print("花了%s元" %self.money)
  10. Bob=Person('Bob')
  11. Bob.eat(12)
  12. Bob.qian()

三、super(Net, self).init()

python中的super(Net, self).init()是指首先找到Net的父类(比如是类NNet),然后把类Net的对象self转换为类NNet的对象,然后“被转换”的类NNet对象调用自己的init函数,其实简单理解就是子类把父类的__init__()放到自己的__init__()当中,这样子类就有了父类的__init__()的那些东西。

回过头来看看我们的我们最上面的代码,Net类继承nn.Modulesuper(Net, self).init()就是对继承自父类nn.Module的属性进行初始化。而且是用nn.Module的初始化方法来初始化继承的属性。

  1. class Net(nn.Module):
  2. def __init__(self):
  3. super(Net, self).__init__()
  4. # 输入图像channel:1;输出channel:6;5x5卷积核
  5. self.conv1 = nn.Conv2d(1, 6, 5)

也就是说,子类继承了父类的所有属性和方法,父类属性自然会用父类方法来进行初始化。

举个例子帮助大家理解:

  1. class Person:
  2. def __init__(self,name,gender):
  3. self.name = name
  4. self.gender = gender
  5. def printinfo(self):
  6. print(self.name,self.gender)
  7. class Stu(Person):
  8. def __init__(self,name,gender,school):
  9. super(Stu, self).__init__(name,gender) # 使用父类的初始化方法来初始化子类
  10. self.school = school
  11. def printinfo(self): # 对父类的printinfo方法进行重写
  12. print(self.name,self.gender,self.school)
  13. if __name__ == '__main__':
  14. stu = Stu('djk','man','nwnu')
  15. stu.printinfo()

image.png
当然,如果初始化的逻辑与父类的不同,不使用父类的方法,自己重新初始化也是可以的。比如:

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. class Person(object):
  4. def __init__(self,name,gender,age):
  5. self.name = name
  6. self.gender = gender
  7. self.age = age
  8. class Student(Person):
  9. def __init__(self,name,gender,age,school,score):
  10. #super(Student,self).__init__(name,gender,age)
  11. self.name = name.upper()
  12. self.gender = gender.upper()
  13. self.school = school
  14. self.score = score
  15. s = Student('Alice','female',18,'Middle school',87)
  16. print s.school
  17. print s.name