owned this note changed 3 years ago
Published Linked with GitHub
Python 教學/

類別與物件

VJ 110,5,11


物件

Python是一種物件導向的程式語言。在Python中,幾乎所有的東西都是一個物件(object)。

類別(class)就像構建物件的「藍圖」,或者說是一個構建物件的函式。

另外,物件中的方法是指在物件裡的函式,物件中的屬性是指物件裡的變數


創建一個類別 (Class)

要創建一個類,使用關鍵字 class

例如創建一個名為 MyClass 的類別,他有一個名為 x 的實例屬性。

class MyClass:
    def __init__(self):
      self.x = 5
注意縮排

構建物件

我們可以使用名為 MyClass 的類別來構建物件。在物件中的變數叫作屬性。

例如構建一個名為 p1 的物件,該物件含有屬性x

class MyClass:
    def __init__(self):
      self.x = 5
p1 = MyClass()
print (p1.x)

可以稍微將有回傳東西的函式類別做連結,但物件在函式之上,因為我們可以從它裡面取用東西

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)

練習 1.1
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


修改物件屬性

我們可以像下面這樣修改用上面構建出來的物件裡的屬性(變數)。

p1 = MyClass()
p1.x = 327

練習 1.2
c.l.append(1)
c.d['Mandy']=327

承上題,將你定義的那個空的list使用append放入一個int變數1,再將你定義的那個空的dict放入鍵值對'Mandy':327


Coding Style

通常類別取名字的時候首字母會大寫,比容易讓人認得出這是個類別。由於物件是變數,所以可以不用管,但有一派是不管物件或類別都取首字母大寫的名字。另外函式都建議首字母小寫,不會變的變數(constant)用全大寫。

def myFunc(): pass # 當一個類別或函式什麼屬性都沒有請這樣寫
class MyClass: pass
myClassX = MyClass()
MyClassX = MyClass()
PI = 3.14159

物件的構建函式

物件除了變數外也可以有函式。不過在物件裡函式叫作方法。

所有的類別都有一個叫做 __init__()函式,它總是在類別被構建成物件時執行。

使用 __init__() 函式為物件中的變數賦值,或者在構建物件時進行其他操作


例如構建一個名為 Person 的類別,使用 __init__() 函式為 nameage 賦值。

self 是對類別的代稱,用於類別中函式的呼叫或變數的引用。

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

p1 = Person("Mandy", 20)

print (p1.name)
print (p1.age)
注意:每次用類別構建新物件時,都會自動呼叫類別的__init__()方法。

前面的程式碼相當於

class Person: pass

p1 = Person()

p1.name = "Mandy"
p1.age = 20

print (p1.name)
print (p1.age)
注意:物件的屬性可以隨你變動

練習 2.1
class MyHello:
  def __init__(self):
    print("Hello")

試寫一個名叫MyHello類別,使該類別在構件時印出 Hello。

class MyHello:
  #TO-DO

c = MyHello() #印出 Hello

練習 2.2
class MyString:
  def __init__(self,s):
    print(s)

試寫一個名叫MyString的類別,使該類別在構件時印出字串s

class MyString:
  #TO-DO

s = "Hello"
c = MyString(s) #印出 s

類別屬性(Instance Attribute)與實例屬性(Class Attribute)

在一個類別中,我們有些屬性是可以共用的,例如

class Car: weels=4 # (Class Attribute) def __init__(self,name): self.name = name #(Instance Attribute)
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物件上執行。

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 是實例的代稱,用於類別中對實例方法的呼叫或屬性的引用。

練習 3
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的能力,規格如下。

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,你可以隨心所欲地調用它,但它必須是類中任何函式的第一個參數。


例如,用 mysillyobjectabc 兩個詞代替self

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的類別,它具有firstnamelastname屬性,以及一個名為printname的方法:

class Person:
  def __init__(self, fname, lname):
    self.firstname = fname
    self.lastname = lname

  def printname(self):
    print(self.firstname, self.lastname)

使用Person類別構建一個物件,然後呼叫printname方法:

x = Person("John", "Doe")
x.printname()

子類別

若要寫出一個子類別-繼承其他類別的類別,請在寫子類別的時後將父類別作為引數傳入:

例如我們寫一個名為Student的類別,將該類別繼承Person的屬性和方法:

class Student(Person): pass

現在,類別Student具有與類別Person相同的屬性和方法。

使用類別Student構建一個物件,然後呼叫printname方法:

x = Student("Mike", "Olsen")
x.printname()

你也可以再使用Person 來構建,目前來說用法一樣

y = Person("John", "Doe")
y.printname()

練習 4.1
class BMItable2(BMItable): pass

試著用名為BMItable2的類別繼承練習 3 的BMItable


練習 4.2
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__()函數。

class Person:
  def __init__(self, fname, lname):
    #做其他事
注意:如果在子類別中添加一個與父類別中的方法同名的方法,那麼繼承下來的父類別方法就會被覆蓋。

為了保持對父類別的 __init__() 函式的繼承,可以寫成呼叫父類別的 __init__() 函式。例如:

class Student(Person):
  def __init__(self, fname, lname):
    Person.__init__(self, fname, lname)

使用super()函式

通過使用super()函式,你不需要使用父類別的名字,它會自動繼承父類別的方法和屬性。因為需要多呼叫一個函式,雖然會慢一點,但可讀性高。

class Student(Person):
  def __init__(self, fname, lname):
    super().__init__(fname, lname)

練習 4.2
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的屬性:

class Student(Person):
  def __init__(self, fname, lname):
    super().__init__(fname, lname)
    self.graduationyear = 108

在下面的例子中,108會是一個變量,並在構建Student時傳入。要做到這一點,得在__init__()函式中多寫一個引數。添加一個year引數,並在構建物件時傳入正確的年份:

class Student(Person):
  def __init__(self, fname, lname, year):
    super().__init__(fname, lname)
    self.graduationyear = year

x = Student("Mandy", "Lu", 108)

練習 4.3
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

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的方法。

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)

練習 4.4
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後,多傳入一個引數年齡。

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 的參數一樣。

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

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()
Select a repo