[Udemy] 課堂筆記 – python beyond the basics object oriented programming

點閱: 114

上完python bootcamp後,覺得對於oop的概念還是很模糊,再上一門專門講oop的課,來加強一下概念。

CH2. What is OOP and why?

OOP會把程式碼組織起來,使程式碼具有下列特性

  • 容易使用
  • 容易理解、維護、擴充
  • 容易協作

為什麼要學OOP?

  • 各種語言都有OOP的概念,是一種典範
  • 真實世界的軟體工程,都充滿了OOP的概念

物件的三大特性

  • 封裝(Encapsulation)
  • 繼承(Inheritance)
  • 多型(Polymorphism)

CH3. Object-oriented python

因為python在設計時,就規定所有存在python內的東西都是物件,因為都是物件,所以在使用上,就非常的簡潔、便利、可擴充

CH4. Modules vs classes

module: a file that contains python code that can be imported for further use.

使用module的方法
參考連結: import的補充

# mymod.py
var = 10
def dothis():
    print "executing the dothis() function"

# test.py
## 方法1
import mymod
print mymod.var
mymod.dothis()

## 方法2
import mymod as mm
print mm.var
mm.dothis()

## 方法3
from mymod import var, dothis # 直接把所有變數與方法引用進來,即可直接呼叫
print var
dothis()

## Terminal
python test.py

>>> 10
>>> executing the dothis() function

class: just python code

class跟module很容易搞混,在命名規則上,統一使用PEP 8 的規定:class就是第一個字要大寫,module則是全小寫

CH7. Classes, instances, attributes and methods

file

說明

  • var這個物件(實體instance)為字串類別(class),由於是字串物件,所以具有upper()這個方法(method)可以被呼叫來使用。

CH9. Methods

class Joe(object):
    def callme(self):
    print("Hello World")
thisjoe = Joe()
thisjoe.callme()

在callme()中,為什麼要使用self呢?因為在python中,預設在使用def時必須給予變數才可執行;若使用self指令,他就可以把class這個物件當成變數,丟入callme()這個method中。

CH10. Attributes

屬性(attributes)是用來描述物件的狀態

CH11. Encapsulation – 封裝(物件三大特性之1)

封裝的目的

  1. 確保物件的資料完整性(integrity of the data)
  2. 安全的保存資料在物件中(在C#中,可透過讀寫權限的設定,達到更完善的封裝功能)
  3. 確保資料的訪問只能透過物件的方法來取得

參考文章

CH12. Init constructor

__init__: initialize attributes at the time that an instance is constructed.

class MyNum(object):
    def __init__(self, value):
        print("Call __init__ first")
        self.value = value

    def increment(self):
        self.value += 1

dd = MyNum(5)

>>> Call __init__ first

dd.increment()
dd.increment()
print(dd.value)

>>> 7

CH13. Class attributes

類別屬性與實體屬性是有階層順序的,請看下列程式的示範

class YourClass(object):
    classy = "class value"
dd = YourClass()
print(dd.classy) # class attribute
>>> class value

dd.classy = "inst value"
print(dd.classy) # instance attribute
>>> inst value

del dd.classy # delete instance attribute
print(dd.classy) # get back to instance attribute
>>> class value

總結一下python中的類別六大特性
file

CH14. Working with class and instance data

這章探討的是在設計時,要把什麼樣的資料放在類別層級,什麼樣的資料要放在instance層級。會被共享的資料,可放在類別層級,下列以instance counter為範例

class InstanceCounter(object):
    count = 0

    def __init__(self, value):
        self.value = value
        InstanceCounter.count += 1

    def set_value(self, new_value):
        self.value = new_value

    def get_value(self):
        return self.value

    def get_count(self):
        return InstanceCounter.count

a = InstanceCounter(5)  # 此時,InstanceCounter建立1次,執行1次__init__,因此count為1
b = InstanceCounter(13) # 此時,InstanceCounter建立2次,執行2次__init__,因此count為2
c = InstanceCounter(17) # 此時,InstanceCounter建立3次,執行3次__init__,因此count為3
d = InstanceCounter(20) # 此時,InstanceCounter建立4次,執行4次__init__,因此count為4

for obj in (a, b, c, d):
    print("value of obj: %s" % (obj.get_value()))
    print("count: %s" % (obj.get_count()))

>>> value of obj: 5
>>> count: 4
>>> value of obj: 13
>>> count: 4
>>> value of obj: 17
>>> count: 4
>>> value of obj: 20
>>> count: 4

CH15 & 16 是作業

作業目標:設計一段程式,可以新增值到一個list中,並設定一最大值,若list的長度超過最大值,則自動把物件的第一筆去除

#### assignments.py
class MaxSizeList(object):
    def __init__(self, value): # {1}
        self.max_size = value
        self.list = []

    def push(self, string): # {2}
        self.list.append(string)
        if len(self.list) > self.max_size:
            self.list.pop(0)

    def get_list(self): # {3}
        return self.list

# {1}: 設定物件的最大值
# {2}: 持續的新增值到物件中,當物件的長度 > {1}所指定的長度時,把物件的第一筆刪掉
# {3}: 回傳物件
# 注意,從頭到尾都是用self來傳遞變數

#### executing

from assignments import MaxSizeList
a = MaxSizeList(3)
b = MaxSizeList(1)

a.push("hey")
a.push("hi")
a.push("let's")
a.push("go")

b.push("how")
b.push("old")
b.push("are")
b.push("you")

print(a.get_list())
print(b.get_list())

CH17 & 18. Inheriting and examples – 繼承(物件三大特性之2)

class Date(object):
    def get_date(self):
        return "2019-04-15"

class Time(Date):
    def get_time(self):
        return "17:16"

dt = Date()
print(dt.get_date())
>>> 2019-04-15

tm = Time()
print(tm.get_time())
>>> 17:16

print(tm.get_date()) # inherit from class Date
>>> 2019-04-15

簡易筆記

  • 母類別(inherited class) class YourClass(object):
  • 子類別(inheriting class class Myclass(YourClass):

CH19. Polymorphism – 多型(物件三大特性之3)

Two classes that have methods of the same name
不同的兩個class,有相同的method name

# 以下範例,示範Dog與Cat兩個不同繼承自Animal的class類別,有相同的method: show_affection()可用
class Animal(object):
    def __init__(self, name):
        self.name = name

    def eat(self, food):
        print('{} eats {}'.format(self.name, food))

class Dog(Animal):
    def fetch(self, thing):
        print('{} goes after the {}'.format(self.name, thing))

    def show_affection(self):
        print('{} wags tail'.format(self.name))

class Cat(Animal):
    def swatstring(self):
        print('{} shreds the string'.format(self.name))

    def show_affection(self):
        print('{} purrrrrrrrr'.format(self.name))

Dog('mm').show_affection()
Cat('cc').show_affection()

>>> mm wags tail
>>> cc purrrrrrrrr

CH20. Inheriting the constructor

import random

class Animal(object):
    def __init__(self, name):
        self.name = name

    def bark(self, sound):
        self.sound = sound
        print('this is Animal.bark() method')

class Dog(Animal):
    def __init__(self, name):
        super(Dog, self).__init__(name)  # 呼叫父類別Animal.__intit__()
        self.breed = random.choice(['moon', 'husky', 'haha'])

    def bark(self, sound):
        self.sound = sound
        print(sound, 'this is create from Dog class which inherit from father class(Animal)')

# create the instance of Dog class
d = Dog("dogname is static")

# print result
print(d.name)
print(d.breed, ': it is inherit from Animal class, but it is a new atribute create from super()')
d.bark(sound=random.choice(['wowww', 'rrrrrrr', 'gggggg']))

>>> dogname is static
>>> moon : it is inherit from Animal class, but it is a new atribute create from super()
>>> rrrrrrr this is create from Dog class which inherit from father class(Animal)

為什麼會有這樣的設計呢?課堂中提到,這樣作的原因是可以設計一個通用的(general)類別,然後繼承了通用的類別後,在比較專用(specific)的類別上進行額外的設計,也方便日後的維護。

CH21. Multiple inheritance and the lookup tree

參考連結
在python中,可以進行多重繼承,多重繼承的搜尋順序是:從最底層的子類別(subclass/child class)開始依序向上搜尋直到最頂層的父類別(parent class)為止,若每一層的父類別有多個繼承的類別,則從左到右開始搜尋。若想確定繼承的順序,可在最底層的子類別執行mro()方法(method)來確認。

case1:

class A(object):
    def method1(self):
        print('A.method1')

    def method2(self):
        print('A.method2')

class B(A):
    def method3(self):
        print('B.method3')

class C(A):
    def method2(self):
        print('C.method2')

    def method3(self):
        print('C.method3')

class D(B, C):
    def method4(self):
        print('D.method4')

d = D()
d.method4()  # 在 D 找到,D.method4
d.method3()  # 以 D->B 順序找到(因為B在左邊),B.method3
d.method2()  # 以 D->B->C 順序找到(左邊的B找不到,續找右邊的C),C.method2
d.method1()  # 以 D->B->C->A 順序找到,A.method1
print(D.mro())  # 對class執行mro方法,可列出class的繼承順序: [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

示意圖1
file

case2

class A(object):
    def dothis(self):
        print('doing this in A')

class B(A):
    pass

class C(object):
    def dothis(self):
        print('doing this in C')

class D(B, C):
    pass

d_instance = D()
d_instance.dothis()  # 以D->B->A的順序找到, doing this in A
print(D.mro())  # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>]

示意圖2

file

總結

1. 會先把單一深層的繼承檢查完(depth lookup),再去檢查橫向的(case2)。
2. 若最底層繼承自多個上層,則上層先檢查左再檢查右(case1)。
3. 若不確定繼承的順序,可在最底層的類別執行.mro()方法來檢查。

CH22. Decorators, static and class methods

使用decorator可簡化程式碼,讓重複使用到的東西有簡易的表示方式(參考連結)

file

說明

  1. 上圖左,第1-5行定義了一個function叫做 print_func_name ,裡面再定義一個 wrap function,在wrap裡面,會把傳入 print_func_name 的function name回傳出來
  2. 第7-11行分別定義兩支function叫做 dog_barkcat_miaow
  3. 在14-19行,把 dog_barkcat_miaow丟入print_func_name中,先得到function_name,再得到Bark !!!Miaow ~~~
  4. 上圖右,第1-5行同樣定義了print_func_name
  5. 第7-13行,在定義dog_barkcat_miaow之前,加上decorator @print_func_name,意思就是,把dog_barkcat_miaow各別當作參數丟入decorator中執行
  6. 第17-21行,呼叫dog_barkcat_miaow,會直接執行最接近的@decorator,得到同左圖一樣的結果

多重decorators的解讀方式,是以遞迴(recursive)的方式進行運算,會先把function(此例: dog_bark)丟到最靠近的decorator(@print_time)中並吐出新的function(此例: wrap_2)後,再把新的function丟到更外圈的decorator中(此例: @print_func_name),產生結果,如下示意圖。
file

About the Author

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts