Python基础篇——super(XXX,self).__init__()的学习、理解以及classxxx(object)中object的作用

在很多场合尤其是写神经网络的代码的时候,经常看到过下面代码示例:

TensorFlow框架下 神经网络的定义:

class BertIntentModel(object):
    def __init__(self):
        super(BertIntentModel, self).__init__()
        self.dict_path = '/Users/vocab.txt'
        self.config_path = '/Users/bert_config_rbt3.json'
        self.checkpoint_path = '/Users/bert_model.ckpt'

        self.label_list = [line.strip() for line in open('label','r',encoding='utf8')]
        self.id2label = {idx:label for idx,label in enumerate(self.label_list)}

        self.tokenizer = Tokenizer(self.dict_path)
        self.model = build_bert_model(self.config_path, self.checkpoint_path, 13)

        self.model.load_weights('/Users/checkpoint/best_model.weights')

PyTorch框架下 神经网络的定义:

import torch
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()

        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)

        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):

        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))

        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

代码行 super(BertIntentModel, self).__init__() 的功能说明:

1. self参数——介绍

self指的是 实例Instance本身,在Python类中规定,函数的第一个参数是实例对象本身,并且约定俗成,把其名字写为self,也就是说, 类中的方法的第一个参数一定要是 self ,而且不能省略

关于self有三点是很重要的:

  • self指的是实例本身,而不是类;
  • self可以用其他变量,如this替代,但是一般不这么去写;
  • 类的方法中的self不可以省略。

实例1:

以下实例证明【self指的是实例本身,而不是类】


class Person():
    def eat(self):
        print(self)

Bob = Person()
Bob.eat()
print(Person)

>>>
<__main__.Person object at 0x7fc63d3ba9d0>
<class '__main__.Person'>

看输出的结果我们可以看到,self指的是实例对象,并不是一个类

实例2:

以下实例证明【self可以用其他变量,如this替代,但是一般不这么去写】,我们试着换一下:

class Person():
    def eat(this):
        print(this)

Bob = Person()
Bob.eat()
print(Person)

>>>
<__main__.Person object at 0x7fc63c967520>
<class '__main__.Person'>

看输出的结果我们可以看到,并没有报错,但是推荐按照规范用self。

实例3:

以下实例证明【 &#x7C7B;&#x7684;&#x65B9;&#x6CD5;中的self不可以省略】,看下面的代码:

class Person():
    def eat():
        print(self)

Bob = Person()
Bob.eat()
print(Person)

>>>
      7 print(Person)

TypeError: eat() takes 0 positional arguments but 1 was given

2. init ()方法——介绍

在python中创建类后,通常会创建一个 __ init__ ()&#x65B9;&#x6CD5; ,这个方法会在创建类的实例的时候自动执行。

  • *__ init__ ()&#x65B9;&#x6CD5; 必须包含一个self参数,而且要是第一个参数。

实例1——【实例化Bob这个对象的时候, __ init__ ()&#x65B9;&#x6CD5; 会自动执行】:

在下面的示例中,我们在实例化Bob这个对象的时候, __ init__ ()&#x65B9;&#x6CD5;就已经自动执行了,如果不是__ init__ ()方法,比如说 eat()&#x65B9;&#x6CD5;,那就只有调用时才会执行。

class Person():
    def __init__(self):
        print("是一个人")
    def eat(self):
        print("要吃饭")

Bob = Person()

>>>
是一个人

实例2——【如果 __ init__ ()&#x65B9;&#x6CD5; 中还需要传入另一个参数,但是在创建Bob的实例时未传入该参数,程序报错】:

在下面的示例中,如果 __ init__ ()&#x65B9;&#x6CD5;中还需要传入另一个参数name,但是我们在创建Bob的实例的时候没有传入name,那么程序就会报错, 说我们少了一个 __ init__ ()&#x65B9;&#x6CD5;的参数,因为 __ init__ ()&#x65B9;&#x6CD5;是会在创建实例的过程中自动执行的,这个时候发现没有name参数,就会报错。

class Person():
    def __init__(self, name):
        print("是一个人")
        self.name = name
    def eat(self):
        print("%s要吃饭" %self.name)

Bob = Person()

Bob.eat()
print(Person)
>>>
是一个人
      9 Bob.eat()
     10 print(Person)

TypeError: __init__() missing 1 required positional argument: 'name'

传入了Bob之后就不会了,而且 eat&#x65B9;&#x6CD5;也可以使用name这个参数。

哪些需放入 __ init__ ()&#x65B9;&#x6CD5; 中,哪些不需要???

  • 需要在 init ()方法中定义:希望有一些操作是在创建实例的时候就自动创建的。
  • *需要在 init ()方法中定义:神经网络代码中,一些网络结构的设置,也最好放在 __ init__ ()&#x65B9;&#x6CD5; 中。

在下述代码中,其实就应该把money这个量定义在 __ init__ ()&#x65B9;&#x6CD5;中,这样就不需要在执行eat()方法后再执行qian()方法。

class Person():
    def __init__(self, name):
        print("是一个人")
        self.name = name
    def eat(self,money):
        print("%s要吃饭" %self.name)
        self.money = money
    def qian(self):
        print("花了%s元" %self.money)

Bob = Person("Bob")
Bob.eat(12)
Bob.qian()

>>>
是一个人
Bob要吃饭
花了12元

3. super(Net, self). init ()——对继承自父类的属性进行初始化,且用父类的初始化方法来初始化继承的属性

父类其实更像是子类的子函数,这句话可能比较拗口,简单来说就是 子类中的代码行 super(Net, self).__init__() 实现调用父类

具体过程: Python中的 super(Net, self).__init__()是指首先找到Net的父类(比如是类NNet),然后把类Net的对象self转换为类NNet的对象,然后”被转换”的类NNet对象调用自己的init函数,其实简单理解就是 子类把父类的 __init__() 放到自己的 __init__() 当中,这样子类就继承了父类的 __init__() 的那些东西。 也就是说,子类继承了父类的所有属性和方法,父类属性自然会用父类方法来进行初始化。当然,如果初始化的逻辑与父类的不同,不使用父类的方法,自己重新初始化也是可以的。

在如下代码块中,子类 Net&#x7C7B;继承父类 nn.Module,函数体中 super(Net, self).__init__()就是对继承自 &#x7236;&#x7C7B;nn.Module&#x7684;&#x5C5E;&#x6027;进行初始化。而且是用 nn.Module的初始化方法来初始化继承的属性。

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()

        self.conv1 = nn.Conv2d(1, 6, 5)

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

举个例子帮助大家理解:

class Person():
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def printinfo(self):
        print(self.name, self.gender)

class Stu(Person):
    def __init__(self, name, gender, school):
        super(Stu, self).__init__(name, gender)
        self.school = school
    def printinfo(self):
        print(self.name, self.gender, self.school)

if __name__=='__main__':
    stu = Stu('Bob', 'female', '5th')
    stu.printinfo()
>>>
Bob female 5th

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

class Person(object):
    def __init__(self, name, gender, age):
        self.name = name
        self.gender = gender
        self.age = age
class Student(Person):
    def __init__(self, name, gender, age, school, score):
        super(Student, self).__init__(name,gender, age)
        self.name = name.upper()
        self.gender = gender.upper()
        self.school = school
        self.score = score

s = Student("Alice", "female", "18", "High school", "17")
print(s.name, s.gender, s.school, s.score)

>>>
ALICE FEMALE High school 17

使用 Python 时, 遇到 class A 和 class A(object) 的写法,那么 class A(object) 中 object 的功能 是什么呢?

object&#x7EE7;&#x627F; 的意思,面向对象语言中都有这样一种特性。 &#x7EE7;&#x627F;&#xFF0C;&#x6307;&#x4E00;&#x4E2A;&#x5BF9;&#x8C61;&#x76F4;&#x63A5;&#x4F7F;&#x7528;&#x53E6;&#x4E00;&#x5BF9;&#x8C61;&#x7684;&#x5C5E;&#x6027;&#x548C;&#x65B9;&#x6CD5;

class Person:
"""
    不带object
"""
    name = "zhenhe"

class Animal(object):
"""
    带有object
"""
    name = "dog"

if __name__ == "__main__":
    x = Person()
    print("Person", dir(x))

    y = Animal()
    print("Animal", dir(y))

>>>
Person ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
Animal ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']

上述实例中Animal类 &#x7EE7;&#x627F;&#x4E86;object&#x5BF9;&#x8C61;&#x62E5;&#x6709;&#x4E86;&#x597D;&#x591A;&#x53EF;&#x64CD;&#x4F5C;&#x5BF9;&#x8C61;,这些都是 &#x7C7B;&#x4E2D;&#x7684;&#x9AD8;&#x7EA7;&#x7279;&#x6027;

对于不太了解python类的同学来说,这些高级特性基本上没用处,但是对于那些要着手写框架或者写大型项目的高手来说,这些特性就比较有用了,比如说tornado里面的异常捕获时就有用到 __class__&#x5B9A;&#x4F4D;&#x7C7B;&#x7684;&#x540D;&#x79F0;,还有 &#x9AD8;&#x5EA6;&#x7075;&#x6D3B;&#x4F20;&#x53C2;&#x6570;的时候用到 __dict__来完成。

实际上在 python 3 &#x4E2D;&#x5DF2;&#x7ECF;&#x9ED8;&#x8BA4;&#x5C31;&#x5E2E;&#x4F60;&#x52A0;&#x8F7D;&#x4E86;object (即便你没有写上object)。

Original: https://blog.csdn.net/weixin_42782150/article/details/122223466
Author: Yale曼陀罗
Title: Python基础篇——super(XXX,self).init()的学习、理解以及classxxx(object)中object的作用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/712100/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球