Hits: 206
上完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
說明
- 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)
封裝的目的
- 確保物件的資料完整性(integrity of the data)
- 安全的保存資料在物件中(在
C#
中,可透過讀寫權限的設定,達到更完善的封裝功能) - 確保資料的訪問只能透過物件的方法來取得
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中的類別六大特性
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
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
總結
1. 會先把單一深層的繼承檢查完(depth lookup),再去檢查橫向的(case2)。
2. 若最底層繼承自多個上層,則上層先檢查左再檢查右(case1)。
3. 若不確定繼承的順序,可在最底層的類別執行.mro()方法來檢查。
CH22. Decorators, static and class methods
使用decorator可簡化程式碼,讓重複使用到的東西有簡易的表示方式(參考連結)
說明
- 上圖左,第1-5行定義了一個function叫做
print_func_name
,裡面再定義一個wrap
function,在wrap裡面,會把傳入print_func_name
的function name回傳出來 - 第7-11行分別定義兩支function叫做
dog_bark
與cat_miaow
- 在14-19行,把
dog_bark
與cat_miaow
丟入print_func_name
中,先得到function_name
,再得到Bark !!!
與Miaow ~~~
- 上圖右,第1-5行同樣定義了
print_func_name
- 第7-13行,在定義
dog_bark
與cat_miaow
之前,加上decorator@print_func_name
,意思就是,把dog_bark
與cat_miaow
各別當作參數丟入decorator中執行 - 第17-21行,呼叫
dog_bark
與cat_miaow
,會直接執行最接近的@decorator,得到同左圖一樣的結果
多重decorators的解讀方式,是以遞迴(recursive)的方式進行運算,會先把function(此例: dog_bark
)丟到最靠近的decorator(@print_time
)中並吐出新的function(此例: wrap_2
)後,再把新的function丟到更外圈的decorator中(此例: @print_func_name
),產生結果,如下示意圖。
Comments