###### [Python 教學/](/@NCHUIT/py) # 類別與物件 > [name=VJ][time= 110,5,11] --- # 物件 Python是一種**物件導向**的程式語言。在Python中,幾乎所有的東西都是一個**物件**(object)。 **類別**(class)就像**構建物件的「藍圖」**,或者說是一個構建物件的函式。 另外,物件中的***方法***是指在物件裡的***函式***,物件中的***屬性***是指物件裡的***變數***。 ---- ## 創建一個類別 ~(Class)~ 要創建一個類,使用關鍵字 `class`。 例如創建一個名為 `MyClass` 的類別,他有一個名為 `x` 的實例屬性。 ```python=! class MyClass: def __init__(self): self.x = 5 ``` ###### ~注意縮排~ ---- ## 構建物件 我們可以使用名為 `MyClass` 的類別來構建物件。在物件中的變數叫作屬性。 例如構建一個名為 `p1` 的物件,該物件含有***屬性***`x`。 ```python=! class MyClass: def __init__(self): self.x = 5 p1 = MyClass() print (p1.x) ``` ---- 可以稍微將***有回傳東西的函式***與***類別***做連結,但物件在函式之上,因為我們可以從它裡面取用東西 ```python=! def myFunc(): x = 1 y = 7 return x class Myclass: def __init__(self): self.x = 1 self.y = 7 f = myFunc() print (f) c = MyClass() print (c.x, c.y) ``` ---- :::spoiler 練習 1.1 ```python=! class MyClass: def __init__(self): self.x = int() #或任意數字 self.l = list() #[] 也對 self.d = dict() #{} 也對 c = MyClass() print(c.x, c.l, c.d) ``` ::: 試寫一個類別,該類別含有一個`int`變數`x`、一個空的`list`和一個空的`dict`。 ---- ## 修改物件屬性 我們可以像下面這樣修改用上面構建出來的物件裡的**屬性**(變數)。 ```python=! p1 = MyClass() p1.x = 327 ``` ---- :::spoiler 練習 1.2 ```python=! c.l.append(1) c.d['Mandy']=327 ``` ::: 承上題,將你定義的那個空的`list`使用`append`放入一個`int`變數`1`,再將你定義的那個空的`dict`放入鍵值對`'Mandy':327`。 ---- ## Coding Style 通常**類別**取名字的時候首字母會大寫,比容易讓人認得出這是個類別。由於物件是變數,所以可以不用管,但有一派是不管物件或類別都取首字母大寫的名字。另外函式都建議首字母小寫,不會變的變數(constant)用全大寫。 ```python=! def myFunc(): pass # 當一個類別或函式什麼屬性都沒有請這樣寫 class MyClass: pass myClassX = MyClass() MyClassX = MyClass() PI = 3.14159 ``` --- ## 物件的構建函式 物件除了變數外也可以有函式。不過在物件裡函式叫作方法。 所有的類別都有一個叫做 `__init__()` 的***函式***,它總是在類別被構建成物件時執行。 使用 `__init__()` 函式為物件中的變數賦值,或者在構建物件時進行**其他操作**。 ---- 例如構建一個名為 `Person` 的類別,使用 `__init__()` 函式為 `name` 和 `age` 賦值。 `self` 是對類別的代稱,用於類別中函式的呼叫或變數的引用。 ```python=! class Person: def __init__(self, name, age): self.name = name self.age = age p1 = Person("Mandy", 20) print (p1.name) print (p1.age) ``` ###### ~注意:每次用類別構建新物件時,都會自動呼叫類別的~`__init__()`~方法。~ ---- 前面的程式碼相當於 ```python=! class Person: pass p1 = Person() p1.name = "Mandy" p1.age = 20 print (p1.name) print (p1.age) ``` ###### ~注意:物件的屬性可以隨你變動~ ---- :::spoiler 練習 2.1 ```python=! class MyHello: def __init__(self): print("Hello") ``` ::: 試寫一個名叫`MyHello`的***類別***,使該類別在***構件***時印出 Hello。 ```python=! class MyHello: #TO-DO c = MyHello() #印出 Hello ``` ---- :::spoiler 練習 2.2 ```python=! class MyString: def __init__(self,s): print(s) ``` ::: 試寫一個名叫`MyString`的類別,使該類別在構件時印出字串`s`。 ```python=! class MyString: #TO-DO s = "Hello" c = MyString(s) #印出 s ``` --- ## 類別屬性(Instance Attribute)與實例屬性(Class Attribute) 在一個類別中,我們有些屬性是可以共用的,例如 ```python= class Car: weels=4 # (Class Attribute) def __init__(self,name): self.name = name #(Instance Attribute) ``` ```python= print(Car.weels) my_car = Car('SaberSR') his_car = Car('CottonSR') print(my_car.weels) Car.weels=8 print(his_car.weels) my_car.weels = 4 print(his_car.weels) ``` 可以看到,只要我們修改類別的屬性,所有的屬性都會受到影響 ## 物件的方法 物件除了變數外也可以有函式。不過在物件裡函式叫作方法。 讓我們在上面的`Person`類別中寫一個方法。 插入一個輸出問候語的函式,並在`p1`物件上執行。 ```python=! class Person: def __init__(self, name, age): self.name = name self.age = age def myfunc(self): print("Hello my name is " + self.name) p1 = Person("Mandy", 20) p1.myfunc() ``` ###### ~注意:引數~ `self` ~是實例的代稱,用於類別中對實例方法的呼叫或屬性的引用。~ ---- :::spoiler 練習 3 ```python=! class BMI: def __init__(self,name,h,w): self.bmi = dict() self.add(name,h,w) def add(self,name,h,w): self.bmi[name]={"身高":h,"體重":w,"BMI":w/(h/100)**2} ``` ::: 試寫一個名為`BMItable`的類別,使該類別擁有紀錄人的名字、身高、體重和BMI的能力,規格如下。 ```python=! class BMItable: def __init__(self, name, high, weight): self.bmi = dict() #{} 亦可 #TO-DO c = BMItable("John",171,57) c.add("Mandy",159,45) c.add("VJ",163,48) print (c.bmi) #印出: #{'John': {'身高': 171, '體重': 57, 'BMI': 19.493177387914233}, #'Mandy': {'身高': 159, '體重': 45, 'BMI': 17.799928800284796}, #'VJ': {'身高': 163, '體重': 48, 'BMI': 18.06616733787497}} ``` ---- ## `self`參數 `self` 視作實例方法第一個傳入的引數,用於實例中方法的呼叫或屬性的引用。 不一定要命名為`self`,你可以隨心所欲地調用它,但它必須是類中任何函式的第一個參數。 ---- 例如,用 `mysillyobject` 和 `abc` 兩個詞代替`self`。 ```python=! class Person: def __init__(mysillyobject, name, age): mysillyobject.name = name mysillyobject.age = age def myfunc(abc): print("Hello my name is " + abc.name) p1 = Person("John", 36) p1.myfunc() ``` 不過不要這麼做好嗎= =,一般來說我們還是會以`self`作為傳入的那個實例的命名 --- # 繼承 Python 中類別間可以**繼承**---允許我們定義一個***複製另一個類別的所有方法和屬性***的類別。 父類別(Parent class)是被繼承的類別,也叫基底類別。 子類別(Child class)是繼承自另一個類別的類別,也叫衍生類別。 ---- ## 父類別 任何類別都可以是父類別,因此語法與寫任何其他類別一樣,接下來所指的父類別都會是以下例子。 例如我們寫一個名為`Person`的類別,它具有`firstname`和`lastname`屬性,以及一個名為`printname`的方法: ```python=! class Person: def __init__(self, fname, lname): self.firstname = fname self.lastname = lname def printname(self): print(self.firstname, self.lastname) ``` ---- 使用`Person`類別構建一個物件,然後呼叫`printname`方法: ```python=! x = Person("John", "Doe") x.printname() ``` ---- ## 子類別 若要寫出一個子類別---***繼承其他類別***的類別,請在寫子類別的時後將父類別作為引數傳入: 例如我們寫一個名為`Student`的類別,將該類別繼承`Person`的屬性和方法: ```python=! class Student(Person): pass ``` ---- 現在,類別`Student`具有與類別`Person`相同的屬性和方法。 使用類別`Student`構建一個物件,然後呼叫`printname`方法: ```python=! x = Student("Mike", "Olsen") x.printname() ``` 你也可以再使用Person 來構建,目前來說用法一樣 ```python=! y = Person("John", "Doe") y.printname() ``` ---- :::spoiler 練習 4.1 ```python=! class BMItable2(BMItable): pass ``` ::: 試著用名為`BMItable2`的類別***繼承***練習 3 的`BMItable` ---- :::spoiler 練習 4.2 ```python=! class BMItable2(BMItable): def secret(name): self.bmi[name]={'身高'='xxx','體重'='xxx','BMI'='xxx'} ``` ::: 試著用名為`BMItable2`的類別***繼承***練習 3 的`BMItable` 並且新增method `secret(name)` ,使該name擁有的身高、體重、BMI都變成'xxx' ## `__init __()` 函式的複寫 到目前為止,我們已經成功寫了一個子類別,這個子類別是從父類別複製屬性和方法。 我們要在子類別寫`__init __()`函式(而不是pass關鍵字)。 例如我們在`Student`類別裡**複寫**`__init__()`函數。 ```python=! class Person: def __init__(self, fname, lname): #做其他事 ``` ###### ~注意:如果在子類別中添加一個與~*父類別中的方法*~同名的方法,那麼繼承下來的父類別方法就會被覆蓋。~ ---- 為了保持對父類別的 `__init__()` 函式的繼承,可以寫成呼叫父類別的 `__init__()` 函式。例如: ```python=! class Student(Person): def __init__(self, fname, lname): Person.__init__(self, fname, lname) ``` ---- ## 使用`super`()函式 通過使用`super()`函式,你不需要使用父類別的名字,它會自動繼承父類別的方法和屬性。因為需要多呼叫一個函式,雖然會慢一點,但***可讀性***高。 ```python=! class Student(Person): def __init__(self, fname, lname): super().__init__(fname, lname) ``` ---- :::spoiler 練習 4.2 ```python=! class BMII(BMI): def __init__(self,name,h,w): BMI.__init__(self,name,h,w) print ("這是BMII") ``` ::: 承上題(4.1),試著用名為`BMII`的類別***繼承***練習 3 的`BMI`後,印出 ``` 這是BMII ``` ---- ## 添加屬性 現在我們已經準備好 `__init__()` 函式了,還同時繼承了父類別,我們準備在 `__init__()` 函式中進行其他操作。例如我們在`Student`類別中多添加一個名為`graduationyear`的屬性: ```python=! class Student(Person): def __init__(self, fname, lname): super().__init__(fname, lname) self.graduationyear = 108 ``` ---- 在下面的例子中,`108`會是一個變量,並在構建`Student`時傳入。要做到這一點,得在`__init__()`函式中多寫一個引數。添加一個`year`引數,並在構建物件時傳入正確的年份: ```python=! class Student(Person): def __init__(self, fname, lname, year): super().__init__(fname, lname) self.graduationyear = year x = Student("Mandy", "Lu", 108) ``` ---- :::spoiler 練習 4.3 ```python=! class BMII(BMI): def __init__(self,name,h,w,d): self.bmi = d #要先定義不然放下面會覆蓋 BMI.__init__(self,name,h,w) ``` ::: 承上題(4.2),試著用名為`BMII`的類別***繼承***練習 3 的`BMI`後,多傳入一個`dict`定義給`bmi`。 ```python=! d = {"VJ":{'身高': 163, '體重': 48, 'BMI': 18.07}} c = BMII("Mandy",159,45,d) print (d) #印出: #{'VJ': {'身高': 163, '體重': 48, 'BMI': 18.07}, #'Mandy': {'身高': 159, '體重': 45, 'BMI': 17.799928800284796}} ``` ---- ## 添加方法 例如我們在`Student`類中添加一個名為`welcome`的方法。 ```python=! class Student(Person): def __init__(self, fname, lname, year): super().__init__(fname, lname) self.graduationyear = year def welcome(self): print("Welcome", self.firstname, self.lastname, "to the class of", self.graduationyear) ``` ---- :::spoiler 練習 4.4 ```python=! class BMII(BMI): def __init__(self,name,h,w,age): BMI.__init__(self,name,h,w)) self.bmi[name]['年齡'] = age def add(self,name,h,w,age = None): self.bmi[name]={"身高":h,"體重":w,"BMI":w/(h/100)**2} if age == None: self.bmi[name]['年齡'] = age ``` ::: 試著用名為`BMII`的類別***繼承***練習 3 的`BMI`後,多傳入一個引數年齡。 ```python=! c = BMII("VJ",162,48,21) c.add("Mandy",159,45,20) print (c.bmi) #印出: #{'VJ': {'身高': 163, '體重': 48, 'BMI': 18.06616733787497, '年齡': 21}, #'Mandy': {'身高': 159, '體重': 45, 'BMI': 17.799928800284796, 年齡': 20}} ``` # 補充 ## class method 當一個 method 是帶有 class 本身作為參數傳入時,我們稱之為 class method class method 必須傳入 class 本身作參數,就是**cls**,概念跟 instance 用 self傳入instance method 的參數一樣。 ```python= class Person2: scientific_name='Homo sapiens' @classmethod def evolution(cls): cls.scientific_name = 'Hyper Homo sapiens' print(Person2.scientific_name) Person2.evolution() #使用class呼叫class mothod print(Person2.scientific_name) ``` 其實也可以用instance 呼叫class method ```python= class Person: year = 100 def __init__(self, height, weight): self.height = height self.weight = weight @classmethod def Kokuhaku(cls): print(f'I love Tatara {cls.year} years') Person.Kokuhaku() Yourdaughter = Person(180,100) Yourdaughter.Kokuhaku() ``` <style>hr{display:none;}</style>
{"metaMigratedAt":"2023-06-16T00:12:23.732Z","metaMigratedFrom":"YAML","title":"類別與物件 - Python 教學","breaks":true,"description":"中興大學資訊研究社1091學期程式分享會主題社課","image":"none","lang":"zh-tw","contributors":"[{\"id\":\"6d6e3ba2-6820-4c6f-9117-f09bccc7f7aa\",\"add\":0,\"del\":205},{\"id\":\"4039c7c6-929e-4623-bcab-ee47f79a408c\",\"add\":1100,\"del\":606},{\"id\":\"4c23290c-4304-45d6-9c21-163639f3ac69\",\"add\":1636,\"del\":363},{\"id\":\"e86b6571-4dea-4aa4-ba20-ece559b0e015\",\"add\":13518,\"del\":5523}]"}
    1990 views
   owned this note