『無為則無心』Python物件導向 — 47、Python中的self詳解

繁華似錦Fighting發表於2022-02-22

1、self的作用

self指的是呼叫該函式的物件(是一個例項)。Python中self等價於Java中的this

首先明確的是self只有在類中的方法中才會有,獨立的函式或方法是不必帶有self的。

例如:

# 定義方法
def showTime(name):
    print(f'大家好我是{name},多多關照!')

# 呼叫方法
showTime('齊天大聖')

"""
輸出結果:
大家好我是齊天大聖,多多關照!
"""

2、self的使用注意事項

(1)self代表類的例項,而非類

# self代表類的例項,而非類
class TestDemo():
    # 可將self理解為例項td
    def testFn(self):
        print(f"誰呼叫我,我就是誰,此時呼叫我的是{self}")
        # 例項呼叫__class__屬性時會指向該例項對應的類
        print(f"我是按照{self.__class__}建立出來的")


# td為TestDemo的例項
td = TestDemo()
# 在類中方法的形參中,self引數一定要定義,但是在呼叫時會自動傳入。
td.testFn()

執行結果如下:

誰呼叫我,我就是誰,此時呼叫我的是<__main__.TestDemo object at 0x00000000028836C8>
我是按照<class '__main__.TestDemo'>建立出來的

說明:

<__main__.TestDemo object at 0x00000000028836C8>表示:
self是一個TestDemo型別的object(物件),物件在記憶體的地址為0x00000000028836C8

為什麼self指的是類的例項物件,而不是類本身。

如果self指向類本身,那麼當一個類有多個例項物件時,self指向哪一個呢?

(2)self不必非寫成self,只是一種規範。

有很多人先學習別的語言,如Java,然後再學習Python的,所以總覺得self怪怪的,想寫成this,可以嗎?
當然可以,換成任何識別符號都可以,把上面的程式碼改寫一下。

# self代表類的例項,而非類
class TestDemo():
    # 可將self理解為例項td
    def testFn(this):
        print(f"誰呼叫我,我就是誰,此時呼叫我的是{this}")
        # 例項呼叫__class__屬性時會指向該例項對應的類
        print(f"我是按照{this.__class__}建立出來的")


# td為TestDemo的例項
td = TestDemo()
# 在類中方法的形參中,self引數一定要定義,但是在呼叫時會自動傳入。
td.testFn()

執行結果如下:

誰呼叫我,我就是誰,此時呼叫我的是<__main__.TestDemo object at 0x00000000028836C8>
我是按照<class '__main__.TestDemo'>建立出來的

改成this後,執行結果完全一樣。
當然,最好還是尊重約定俗成的習慣,使用self。(不是最好,是一定。)

(3)類中方法的形參中一定要寫self,包括內建函式

# 如果類中的方法不寫self形參,
# 則不能使用物件.方法名()來呼叫方法,
# 只能使用類名.方法名()的方式來呼叫該方法,
# 類似與Java中的靜態方法。

class TestDemo():
    # 定義一個方法,不定義self形參
    def testFn():
        print(f"不定義形參self,依舊可以呼叫我")
        print(__class__)

# 建立物件,用物件.方法名()來呼叫方法
td = TestDemo()
# 報錯
# TypeError: testFn() takes 0 positional arguments but 1 was given
td.testFn()

# 只能使用類名.方法名()的方式來呼叫該方法。
TestDemo.testFn()

(4)__init__函式中,要把接收到的引數賦值到self中,提供全類使用

關於__init__函式,可以檢視類的內建函式來了解。

class Student():
    def __init__(self, name, age, addr):
        # self的作用主要表示這個變數是類中的公共變數
        # 定義在self中的屬性,整個類內都可以使用
        # 普通方法同理
        self.name = name
        self.age = age
        # 沒有定義在self中的屬性,只能在當前方法內使用
        addr = addr

    # 標準用法
    def tellMeName(self):
        # 如果去掉此處的self,會提示name 'name' is not defined
        print(f'我不叫孫悟空,我叫{self.name}')

    # 方法形參沒有定義self,則報錯
    # TypeError: tellMeAge() takes 0 positional arguments but 1 was given
    # def tellMeAge():
    def tellMeAge(self):
        # 如果獲取age的值不加self,則獲取不到,會報錯
        # NameError: name 'age' is not defined
        # print(f'我今年{age}啦')
        print(f'我今年{self.age}啦')

    def tellMeAddr(self):
        # 因為__init__函式彙總沒有把addr變數定義在self物件中
        # 所以addr變數的作用域只在__init__函式內,
        # 其他函式無法呼叫。
        # 新增在self物件內的屬性為全域性屬性。
        print(f'我現居住在{self.addr}')


s = Student('美猴王', 18, addr='北京')
s.tellMeName()
s.tellMeAge()
s.tellMeAddr()

(5)同一個類中呼叫其他的方法時需要加self

class Student():
    def __init__(self, name, age, addr):
        # self的作用主要表示這個變數是類中的公共變數
        # 定義在self中的屬性,整個類內都可以使用
        # 普通方法同理
        self.name = name
        self.age = age
        self.addr = addr


    def tellMeName(self):
        print(f'我不叫孫悟空,我叫{self.name}')

    def tellMeAge(self):
        print(f'我今年{self.age}啦')

    def tellMeAddr(self):
        print(f'我現居住在{self.addr}')

    def tellAll(self):
        # 如果呼叫類中的其他函式時,不用self呼叫,則會報錯
        # NameError: name 'tellMeName' is not defined
        # tellMeName()
        self.tellMeName()
        self.tellMeAge()
        self.tellMeAddr()

s = Student('美猴王', 18, addr='北京')
s.tellAll()

"""
輸出結果:
我不叫孫悟空,我叫美猴王
我今年18啦
我現居住在北京
"""

(6)self總是指呼叫時的類的例項,在繼承時中也一樣

# 定義一個父類
class Parent():
    def pFn(self):
        print(self)

# 定義一個子類
class Child(Parent):
    def cFn(self):
        print(self)

# 建立子類物件
child = Child()
# 呼叫子類方法
# <__main__.Child object at 0x00000000025A38C8>
child.cFn()
# 子類呼叫父類方法
# <__main__.Child object at 0x00000000025A38C8>
child.pFn()

# 建立父類
parent = Parent()
# 呼叫自己方法
# <__main__.Parent object at 0x00000000025A3908>
parent.pFn()

(7)self與私有變數的用法

# self的屬性名稱前加上兩個下劃線,就變成了一個私有變數(private)
# 只有類內部可以訪問,外部不能直接訪問
class Student():
    def setname(self, name1, name2):
        self.name1 = name1
        self.__name2 = name2

    def getname(self):
        print(f'我的第一個名字是{self.name1},我的第二個名字是{self.__name2}')


stu = Student()
stu .setname("齊天大聖", "美猴王")
# 結果:我的第一個名字是齊天大聖,我的第二個名字是美猴王
stu .getname()

# 結果:齊天大聖
print(stu.name1)
# 結果報錯:AttributeError: 'Student' object has no attribute 'name2'
# 說明私有變數並不能獲取到
print(stu.name2)

(8)總結

  • self總是指呼叫時的類的例項。
  • self的名字並不是規定死的,但是在Python中self不是關鍵詞,你可以定義成thisabc或其它名字都可以。但是約定成俗,減少程式碼理解難度。
  • 在類中方法的形參中,self引數一定要定義,但是在呼叫時會自動傳入。

(9)綜合練習

一個類可以建立多個物件。

# 需求:洗衣機,功能:能洗衣服

# 1. 定義洗衣機類
class Washer():
    def wash(self):
        print('我會洗衣服')
        print(self)


# 2. 建立物件
haier1 = Washer()
# <__main__.Washer object at 0x0000000002553508>
print(haier1)
# haier1物件呼叫例項方法(物件方法)
haier1.wash()

# 3.建立第二個物件
haier2 = Washer()
# <__main__.Washer object at 0x0000000002553608>
print(haier2)
haier2.wash()
"""
輸出結果:
<__main__.Washer object at 0x00000000025A3688>
我會洗衣服
<__main__.Washer object at 0x00000000025A3688>
<__main__.Washer object at 0x00000000025A3788>
我會洗衣服
<__main__.Washer object at 0x00000000025A3788>
"""


"""
可以看到每建立一個新的物件,都是有獨立空間的物件,
所以每個物件的地址是不同的。
"""

注意:

可以看到每建立一個新的物件,都是有獨立空間的物件,因為每個物件的地址是不同的。

每次列印物件和self得到的結果是一致的,說明self指向了物件的例項。

相關文章