範例 - 工資结算系统。之前前面練習有提到。
某公司有是部門經理、程序員和銷售員,根據提供的員工訊息來計算月薪。
部門經理的月薪是每月固定 45000 元
程序員的月薪按本月工作時間計算每小時 200 元
銷售員的月薪是 22000 元的底薪加上銷售額 5% 的加成
from abc import ABCMeta, abstractmethod
class Employee(metaclass = ABCMeta): # 抽象父類別 (class) 員工
def __init__ (self, name):
self.name = name
@abstractmethod
def get_salary(self): # 月薪
pass
class Manager(Employee): # 子類別 (class) 部門經理
def get_salary(self): # 繼承抽象父類別 (class) 並覆寫
return 45000.0
class Programmer(Employee): # 子類別 (class) 程序員
def __init__ (self, name, working_hour = 0):
self.working_hour = working_hour
super().__init__(name) # 繼承抽象父類別 (class)
def get_salary(self): # 繼承抽象父類別 (class) 並覆寫
return 200.0 * self.working_hour
class Salesman(Employee): # 子類別 (class) 銷售員
def __init__(self, name, sales = 0.0):
self.sales = sales
super().__init__(name) # 繼承抽象父類別 (class)
def get_salary(self): # 繼承抽象父類別 (class) 並覆寫
return 22000.0 + self.sales * 0.05
class EmployeeFactory():
# 創建員工的工廠(透過工廠實現物件使用者和物件之間的解耦合 (decoupling))
@staticmethod
def create(emp_type, *args, **kwargs): # 創建員工
emp_type = emp_type.upper()
emp = None
if emp_type == 'M':
emp = Manager(*args, **kwargs)
elif emp_type == 'P':
emp = Programmer(*args, **kwargs)
elif emp_type == 'S':
emp = Salesman(*args, **kwargs)
return emp
def main():
"""主函数"""
emps = [
EmployeeFactory.create('M', 'Alan'),
EmployeeFactory.create('P', 'Andy', 120),
EmployeeFactory.create('P', 'Angle', 85),
EmployeeFactory.create('S', 'Alex', 123000),
]
for emp in emps:
print('%s : %.2f 元' % (emp.name, emp.get_salary()))
if __name__ == '__main__':
main()
範例 - 撲克牌遊戲。之前前面練習有提到。
from enum import Enum, unique
import random
@unique
class Suite(Enum): # 花色
SPADE, HEART, CLUB, DIAMOND = range(4)
def __lt__(self, other):
return self.value < other.value
class Card(): # 每張牌
def __init__(self, suite, face): # 初始化(花色,數字)
self.suite = suite
self.face = face
def show(self): # 顯示牌面
suites = ['♠️', '♥️', '♣️', '♦️']
faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
return f'{suites[self.suite.value]} {faces[self.face]}'
def __str__(self):
return self.show()
def __repr__(self):
return self.show()
class Poker(): # 一副牌
def __init__(self):
self.index = 0
self.cards = [Card(suite, face)
for suite in Suite
for face in range(1, 14)]
def shuffle(self): # 洗牌(隨機)
random.shuffle(self.cards)
self.index = 0
def deal(self): # 發牌
card = self.cards[self.index]
self.index += 1
return card
@property
def has_more(self): # 還有沒有牌
return self.index < len(self.cards)
class Player(): # 玩家
def __init__(self, name):
self.name = name
self.cards = []
def get_one(self, card): # 拿牌
self.cards.append(card)
def sort(self, comp = lambda card: (card.suite, card.face)):
# 整理牌
self.cards.sort(key = comp)
def main():
poker = Poker()
poker.shuffle()
players = [Player('pA'), Player('pB'), Player('pC'), Player('pD')]
while poker.has_more:
for player in players:
player.get_one(poker.deal())
for player in players:
player.sort()
print(player.name, end = ': ')
print(player.cards)
if __name__ == '__main__':
main()
物件的複制(深度複製 / 深拷貝 / 深度克隆和淺度複製 / 淺拷貝 / 影子克隆)
深度複製(Deep Copy):不僅複製物件的基本型別,同時也複製原物件中的物件.完全產生新物件。
淺度複製(Shallow Copy):只複製物件的基本型別,物件型別,仍屬於原來的引用。
垃圾回收、循環引用和弱引用
指不能確保其引用的對象不會被垃圾回收器回收的引用。
以下情況會導致垃圾回收:
Python 使用自動化內存管理,以引用計數為基礎,也引入標記 - 清除和分代收集兩種機制為輔。
導致引用計數 +1 的情況:
導致引用計數 -1 的情況:
引用計數可能會導致循環引用問題,而循環引用會導致內存洩露。
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
為了解決這個問題,Python 中引入'標記 - 清除'和'分代收集'。
在創建一個物件的時,物件被放在第一代中,如果在第一代的垃圾檢查中,物件存活下來,該物件就會被放到第二代 中,同理在第二代的垃圾檢查中對象存活下來,該物件就會被放到第三代中。
如果不想造成循環引用可以使用 weakref 模組構造弱引用。
混入 (Mixin)
範例 - 自定義字典限制只有在指定的 key 不存在時才能在字典中設置鍵值對。
class SetOnceMappingMixin(): # 定義混入 (Mixin) 類別
__slots__ = ()
def __setitem__(self, key, value):
if key in self:
raise KeyError(str(key) + ' already set')
return super().__setitem__(key, value)
class SetOnceDict(SetOnceMappingMixin, dict):
# 定義字典
pass
my_dict = SetOnceDict()
try:
my_dict['username'] = 'jackfrued'
my_dict['username'] = 'hellokitty'
except KeyError:
pass
print(my_dict)
範例 - 用元類別實現單例模式。
import threading
class SingletonMeta(type): # 定義元類別
def __init__(cls, *args, **kwargs):
cls.__instance = None
cls.__lock = threading.Lock()
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
with cls.__lock:
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
class President (metaclass = SingletonMeta):
# 單例類別
pass
t1 = President()
t2 = President()
print(t1)
print(t2)
物件導向設計原則 (SOLID)
GoF 設計模式
範例 - 找出文件的雜湊函式 (Hash function)。
要先下載 Python-3.7.1.tgz 此檔案才能測試程式,其他檔案也行。
class StreamHasher(): # 雜湊函數生成器 (策略模式)
def __init__(self, alg='md5', size=4096):
self.size = size
alg = alg.lower()
self.hasher = getattr(__import__('hashlib'), alg.lower())()
def __call__(self, stream):
return self.to_digest(stream)
def to_digest(self, stream): # 生成十六進制形式
for buf in iter(lambda: stream.read(self.size), b''):
self.hasher.update(buf)
return self.hasher.hexdigest()
def main():
hasher1 = StreamHasher()
with open('Python-3.7.1.tgz', 'rb') as stream:
print(hasher1.to_digest(stream))
hasher2 = StreamHasher('sha1')
with open('Python-3.7.1.tgz', 'rb') as stream:
print(hasher2(stream))
if __name__ == '__main__':
main()