self參數 - __ init__ ()方法 super(Net, self).__init__()


self參數

self指的是實例Instance本身,在Python類中規定,函數的第一個參數是實例對象本身,並且約定俗成,把其名字寫為self,

也就是說,類中的方法的第一個參數一定要是self,而且不能省略。關於self有三點是很重要的:

self指的是實例本身,而不是類
self可以用this替代,但是不要這么去寫
類的方法中的self不可以省略
class Person():
def eat(self):
print(self)
Alex = Person()
Alex.eat()
print(Person)

 

 

__ init__ ()方法

在python中創建類后,通常會創建一個 __ init__ ()方法,這個方法會在創建類的實例的時候自動執行

__ init__ ()方法必須包含一個self參數,而且要是第一個參數

 
        
class Person():
def __init__(self):
print("是一個人")
#self.name = name
def eat(self):
print("吃飯")
Alex = Person()

是一個人
__ init__ ()方法就已經自動執行了,但是如果不是 __ init__ ()方法,比如說eat()方法,那肯定就只有調用才執行

再比如說下面的代碼,如果 __ init__ ()方法中還需要傳入另一個參數name,但是
我們在創建Alex的實例的時候沒有傳入name,那么程序就會報錯,

class Person():
def __init__(self,name):
print("是一個人")
self.name = name
def eat(self):
print("吃飯")
Alex = Person()

 

 我們其實就比較清晰的知道什么東西需要在__ init__ ()方法中定義了,就是希望有一些操作是在創建實例的時候就有,

我們寫神經網絡的代碼的時候,一些網絡結構的設置,也最好放在__ init__ ()方法中。

super(Net, self).init()

python中的super(Net, self).init()是指首先找到Net的父類(比如是類NNet),然后把類Net的對象self轉換為類NNet的對象,然后“被轉換”的類NNet對象調用自己的init函數,

其實簡單理解就是子類把父類的__init__()放到自己的__init__()當中,這樣子類就有了父類的__init__()的那些東西。

Net類繼承nn.Module,super(Net, self).init()就是對繼承自父類nn.Module的屬性進行初始化。

而且是用nn.Module的初始化方法來初始化繼承的屬性。

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        # 輸入圖像channel:1;輸出channel:6;5x5卷積核
        self.conv1 = nn.Conv2d(1, 6, 5)

也就是說,子類繼承了父類的所有屬性和方法,父類屬性自然會用父類方法來進行初始化。
當然,如果初始化的邏輯與父類的不同,不使用父類的方法,自己重新初始化也是可以的。比如:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
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,'Middle school',87)
print (s.school)
print (s.name)
print (s.age) #報錯,因為沒有初始化
Middle school
ALICE

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, 'Middle school', 87)
print(s.school)
print(s.name)
Middle school
Alice

class Person(object):
    def __init__(self, name, gender, age):
        self.name = name
        self.gender = gender
        self.age = age +2


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, 'Middle school', 87)
print (s.school)
print (s.age)
Middle school
20 #(18+2)

class Person(object):
    def __init__(self, name, gender, age):
        self.name = name
        self.gender = gender
        self.age = age +2


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, 'Middle school', 87)
print (s.name)
print (s.age)

ALICE

20

def __init__(self, name, gender, age, school, score):
super(Student,self).__init__(name,age,gender)#報錯,順序要匹配


def __init__(self, name, gender, age, school, score):
super(Student,self).__init__(name,gender)#報錯,不可缺少父類屬性


下面解釋Python的super()函數

1.python子類調用父類成員有2種方法,分別是普通方法和super方法

假設Base是基類

class Base(object):
  def __init__(self):
    print “Base init”

則普通方法如下

class Leaf(Base):
     def __init__(self):
         Base.__init__(self)
        print “Leaf init”

super方法如下

class Leaf(Base):
     def __init__(self):
         super(Leaf, self).__init__()
         print “Leaf init”

兩種方法的效果一樣

我們就遇到了一個難題:

 

 

 

class Base(object):
       def __init__(self):
              print “Base init”
 
class Medium1(Base):
       def __init__(self):
              Base.__init__(self)
              print “Medium1 init”
 
class Medium2(Base):
       def __init__(self):
              Base.__init__(self)
              print “Medium2 init”
 
class Leaf(Medium1, Medium2):
       def __init__(self):
              Medium1.__init__(self)
              Medium2.__init__(self)
              print “Leaf init”    

leaf  =Leaf()

 

 

 Base被初始化了兩次!這是由於Medium1和Medium2各自調用了Base的初始化函數導致的。

解決方法:

Python使用的是super重寫一下,看一下輸出結果

class Base(object):
       def __init__(self):
              print “Base init”
 
class Medium1(Base):
       def __init__(self):
              super(Medium1, self).__init__()
              print “Medium1 init”
 
class Medium2(Base):
       def __init__(self):
              super(Medium2, self).__init__()
              print “Medium2 init”
 
class Leaf(Medium1, Medium2):
       def __init__(self):
              super(Leaf, self).__init__()
              print “Leaf init”        
leaf  =Leaf()

Base init

Medium2 init

Medium1 init

Leaf init

 

super的內核:mro

要理解super的原理,就要先了解mro。mro是method resolution order的縮寫,表示了類繼承體系中的成員解析順序

在python中,每個類都有一個mro的類方法。我們來看一下鑽石繼承中,Leaf類的mro是什么樣子的:

>>> Leaf.mro()

[<class '__main__.Leaf'>, <class '__main__.Medium1'>, <class '__main__.Medium2'>, <class '__main__.Base'>, <type 'object'>]

可以看到mro方法返回的是一個祖先類的列表。Leaf的每個祖先都在其中出現一次,這也是super在父類中查找成員的順序。 

通過mro,python巧妙地將多繼承的圖結構,轉變為list的順序結構。super在繼承體系中向上的查找過程,變成了在mro中向右的線性查找過程,任何類都只會被處理一次。

通過這個方法,python解決了多繼承中的2大難題:

1. 查找順序問題。從Leaf的mro順序可以看出,如果Leaf類通過super來訪問父類成員,那么Medium1的成員會在Medium2之前被首先訪問到。

    如果Medium1和Medium2都沒有找到,最后再到Base中查找。

2. 鑽石繼承的多次初始化問題。在mro的list中,Base類只出現了一次。事實上任何類都只會在mro list中出現一次

    這就確保了super向上調用的過程中,任何祖先類的方法都只會被執行一次。

 

super的具體用法

(1) super(type, obj)

class Leaf(Medium1, Medium2):
       def __init__(self):
              super(Leaf, self).__init__()
              print “Leaf init”

super(Leaf, self).__init__()的意思是說:

  1. 獲取self所屬類的mro, 也就是[Leaf, Medium1, Medium2, Base]
  2. 從mro中Leaf右邊的一個類開始,依次尋找__init__函數。這里是從Medium1開始尋找
  3. 一旦找到,就把找到的__init__函數綁定到self對象,並返回

 

從這個執行流程可以看到,如果我們不想調用Medium1的__init__,而想要調用Medium2的__init__,那么super應該寫成:super(Medium1, self)__init__() 

 

 

(2) super(type, type2)

class Leaf(Medium1, Medium2):
       def __new__(cls):
              obj = super(Leaf, cls).__new__(cls)
              print “Leaf new”
              return obj

super(Leaf, cls).__new__(cls)的意思是說:

  1. 獲取cls這個類的mro,這里也是[Leaf, Medium1, Medium2, Base]
  2. 從mro中Leaf右邊的一個類開始,依次尋找__new__函數
  3. 一旦找到,就返回“非綁定”的__new__函數

 

由於返回的是非綁定的函數對象,因此調用時不能省略函數的第一個參數。這也是這里調用__new__時,需要傳入參數cls的原因

同樣的,如果我們想從某個mro的某個位置開始查找,只需要修改super的第一個參數就行

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM