1. 程式人生 > 實用技巧 >第037講:類和物件:面向物件程式設計

第037講:類和物件:面向物件程式設計

課堂小筆記

面向物件最重要的概念就是類(Class)和例項(Instance),必須牢記類是抽象的模板,比如Student類,而例項是根據類創建出來的一個個具體的“物件”,每個物件都擁有相同的方法,但各自的資料可能不同。

self是什麼?

Python的self就相當於C++的this指標。類是圖紙,物件是例項化的東西。由同一個類可以生成無數個物件,這些物件長得都很相似,都是來源於同一個類的屬性和方法,當一個物件的方法被呼叫的時候,物件會將自身作為第一個引數傳給這個self引數,接收到這個self的時候Python就知道你是哪一個物件在呼叫方法了。

為什麼都是呼叫kick方法,為啥結果不一樣。因為在呼叫的時候,a.kick()的這裡他有第一個引數,這個引數是隱藏的,就是把a這個物件的標誌傳進去;那setName()這裡的self就接收到了,self.name就會去找到這個a物件的name屬性,然後kick把他賦值打印出來。是Python默默在工作的。

(類裡面沒有定義的屬性都可以賦值,因為類是共有屬性,物件可以有自己的私有屬性)

你聽說過Python的魔法方法嗎?

被雙下劃線包圍。

__init__(self),稱為構造方法,魔力體現在:只要在例項化一個物件的時候,那麼這個方法就會在物件被建立的時候自動呼叫。(傳說中的構成函式?)

共有和私有

蒼井空是世界的,老婆是自己的!

name mangling 名字改編,名字重整。在Python中定義私有變數只需要在變數名或函式名前加上“__”兩個下劃線,那麼這個函式或者變數就變為私有的了。

可以通過p._Person__name

是偽私有。

課後測試題及答案

測試題:

0. 以下程式碼體現了面向物件程式設計的什麼特徵?

>>> "FishC.com".count('o')
1
>>> [1, 1, 2, 3, 5, 8].count(1)
2
>>> (0, 2, 4, 8, 12, 18).count(1)
0

都可以呼叫同一個方法,但是結果不一定相同。

1. 當程式設計師不想把同一段程式碼寫幾次,他們發明了函式解決了這種情況。當程式設計師已經有了一個類,而又想建立一個非常相近的新類,他們會怎麼做呢?

非常相近的新類,可以用繼承吧,繼承原先的類,再自己修改

2. self引數的作用是什麼?

用來接收物件的,可以讓Python知道是哪個物件在使用方法和屬性

3. 如果我們不希望物件的屬性或方法被外部直接引用,我們可以怎麼做?

可以使用“__”雙下劃線來使其私有化,雖然是偽私有,因為還是可以通過_類名__方法/屬性 來引用。

4. 類在例項化後哪個方法會被自動呼叫?

__init__()

5. 請解釋下邊程式碼錯誤的原因:

class MyClass:
        name = 'FishC'
        def myFun(self):
                print("Hello FishC!")
                
>>> MyClass.name
'FishC'
>>> MyClass.myFun()
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    MyClass.myFun()
TypeError: myFun() missing 1 required positional argument: 'self'
>>>

沒有一個物件傳給self引數,

動動手

0. 按照以下要求定義一個遊樂園門票的類,並嘗試計算2個成人+1個小孩平日票價。

平日票價100元

週末票價為平日的120%

兒童半票

答案:

class Ticket():
        def __init__(self, weekend=False, child=False):
                self.exp = 100
                if weekend:
                        self.inc = 1.2
                else:
                        self.inc = 1
                if child:
                        self.discount = 0.5
                else:
                        self.discount = 1
        def calcPrice(self, num):
                return self.exp * self.inc * self.discount * num

>>> adult = Ticket()
>>> child = Ticket(child=True)
>>> print("2個成人 + 1個小孩平日票價為:%.2f" % (adult.calcPrice(2) + child.calcPrice(1)))
2個成人 + 1個小孩平日票價為:250.00

1. 遊戲程式設計:按以下要求定義一個烏龜類和魚類並嘗試編寫遊戲。(初學者不一定可以完整實現,但請務必先自己動手,你會從中學習到很多知識的^_^)

 1 import random as r
 2 
 3 legal_x = [0, 10]
 4 legal_y = [0, 10]
 5 
 6 class Turtle:
 7     def __init__(self):
 8         # 初始體力
 9         self.power = 100
10         # 初始位置隨機
11         self.x = r.randint(legal_x[0], legal_x[1])
12         self.y = r.randint(legal_y[0], legal_y[1])
13 
14     def move(self):
15         # 隨機計算方向並移動到新的位置(x, y)
16         new_x = self.x + r.choice([1, 2, -1, -2])
17         new_y = self.y + r.choice([1, 2, -1, -2])
18         # 檢查移動後是否超出場景x軸邊界
19         if new_x < legal_x[0]:
20             self.x = legal_x[0] - (new_x - legal_x[0])
21         elif new_x > legal_x[1]:
22             self.x = legal_x[1] - (new_x - legal_x[1])
23         else:
24             self.x = new_x
25         # 檢查移動後是否超出場景y軸邊界
26         if new_y < legal_y[0]:
27             self.y = legal_y[0] - (new_y - legal_y[0])
28         elif new_y > legal_y[1]:
29             self.y = legal_y[1] - (new_y - legal_y[1])
30         else:
31             self.y = new_y        
32         # 體力消耗
33         self.power -= 1
34         # 返回移動後的新位置
35         return (self.x, self.y)
36 
37     def eat(self):
38         self.power += 20
39         if self.power > 100:
40             self.power = 100
41 
42 class Fish:
43     def __init__(self):
44         self.x = r.randint(legal_x[0], legal_x[1])
45         self.y = r.randint(legal_y[0], legal_y[1])
46         
47     def move(self):
48         # 隨機計算方向並移動到新的位置(x, y)
49         new_x = self.x + r.choice([1, -1])
50         new_y = self.y + r.choice([1, -1])
51         # 檢查移動後是否超出場景x軸邊界
52         if new_x < legal_x[0]:
53             self.x = legal_x[0] - (new_x - legal_x[0])
54         elif new_x > legal_x[1]:
55             self.x = legal_x[1] - (new_x - legal_x[1])
56         else:
57             self.x = new_x
58         # 檢查移動後是否超出場景y軸邊界
59         if new_y < legal_y[0]:
60             self.y = legal_y[0] - (new_y - legal_y[0])
61         elif new_y > legal_y[1]:
62             self.y = legal_y[1] - (new_y - legal_y[1])
63         else:
64             self.y = new_y
65         # 返回移動後的新位置
66         return (self.x, self.y)
67 
68 turtle = Turtle()
69 fish = []
70 for i in range(10):
71     new_fish = Fish()
72     fish.append(new_fish)
73 
74 while True:
75     if not len(fish):
76         print("魚兒都吃完了,遊戲結束!")
77         break
78     if not turtle.power:
79         print("烏龜體力耗盡,掛掉了!")
80         break
81 
82     pos = turtle.move()
83     # 在迭代器中刪除列表元素是非常危險的,經常會出現意想不到的問題,因為迭代器是直接引用列表的資料進行引用
84     # 這裡我們把列表拷貝給迭代器,然後對原列表進行刪除操作就不會有問題了^_^
85     for each_fish in fish[:]:
86         if each_fish.move() == pos:
87             # 魚兒被吃掉了
88             turtle.eat()
89             fish.remove(each_fish)
90             print("有一條魚兒被吃掉了...")