Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

14. 進階主題

運用描述器
實作裝飾器
定義 meta 類別
使用相對匯入
泛型進階

  • Identifiez-vous pour voir les commentaires

  • Soyez le premier à aimer ceci

14. 進階主題

  1. 1. 14. 進階主題 • 學習目標 – 運用描述器 – 實作裝飾器 – 定義 meta 類別 – 使用相對匯入 – 泛型進階 2
  2. 2. 描述器 • 擁有 __get__() 方法,以及選擇性的 __set__()、__delete__() 方法 • 當描述器成為某個類別的屬性成員時,對 於類別屬性或者其實例屬性的取得、設定 或刪除,將會交由描述器來決定如何處理 3
  3. 3. 4
  4. 4. • 當 Descriptor 被指定給 Some 類別的 x 屬性時,對於 Some 實例 s 的屬性取值、 指定或刪除,分別相當於進行了以下動作: • 對於 Some.x 這個取值動作,則相當於: 5
  5. 5. 6
  6. 6. 7
  7. 7. • 資料描述器可以攔截對實例的屬性取得、 設定與刪除行為 • 非資料描述器,是用來攔截透過實例取得 類別屬性時的行為 8
  8. 8. 9
  9. 9. 10
  10. 10. • 若想控制可以指定給物件的屬性名稱,可 以在定義類別時指定 __slots__ • 這個屬性要是個字串清單,列出可指定給 物件的屬性名稱 11
  11. 11. 12
  12. 12. • 如果類別定義時指定了 __slots__,那麼 從類別建構出來的實例就不會具有 __dict__ 屬性 13
  13. 13. 14
  14. 14. • __slots__ 中的屬性,Python 會將之實 作為描述器 15
  15. 15. • __slots__ 屬性最好作為類別屬性來使用 • 父類別中定義的 __slots__,僅可以透過 父類別來取得,而子類別的 __slots__ 則 僅可以透過子類別來取得 16
  16. 16. • 若父類別中沒有定義 __slots__,子類別 即使定義了__slots__,以子類別建構出 來的實例,仍然會具有 __dict__ 屬性 17
  17. 17. • 如果父類別定義了__slots__,而子類別 沒有定義自己的 __slots__,子類別建構 出來的實例也會有 __dict__ 18
  18. 18. • 物件本身可以決定存取屬性的行為 • __getattribute__() 一但定義,任何 屬性的尋找都會被攔截,即使是那些 __xxx__ 的內建屬性名稱 • __getattr__() 的作用,是作為尋找屬 性的最後一個機會 19
  19. 19. • 取得屬性的順序 – 實例的 __getattribute__() – 資料描述器的 __get__() – 實例的 __dict__ – 非資料描述器的 __get__() – 實例的 __getattr__() 20
  20. 20. • __setattr__() 的作用,在於攔截所有 對實例的屬性設定 • 設定屬性順序記憶 – 實例的 __setattr__() – 資料描述器的 __set__() – 實例的 __dict__ 21
  21. 21. • __delattr__()的作用,在於攔截所有對 實例的屬性設定 • 刪除屬性順序記憶 – 實例的__delattr__() – 資料描述器的__delete__() – 實例的__dict__ 22
  22. 22. 函式裝飾器 • 裝飾器本質上就是一個函式可接受函式且 傳回函式 • 假設你設計了一個點餐程式… 23
  23. 23. • 一個函式可以接受函式並傳回函式 24
  24. 24. 25
  25. 25. 26
  26. 26. 27
  27. 27. • 如果裝飾器語法需要帶有參數,用來作為 裝飾器的函式,必須先以指定的參數執行 一次,傳回函式物件再來裝飾指定的函式 28
  28. 28. 29
  29. 29. 30
  30. 30. • 除了對函式進行裝飾之外,也可以對類別 作裝飾,也就是所謂類別裝飾器 31
  31. 31. • 如果將經過裝飾的 friedchicken() 函式 本身 print() 出來,會顯示什麼呢? 32
  32. 32. 33
  33. 33. • 若函式接受類別並傳回類別時,就可設計 為類別裝飾器,以用來標註類別 34
  34. 34. 35
  35. 35. • 對類別上定義之方法進行裝飾自然也是可 行的 • 若是實例方法,傳回的函式,第一個參數 是用來接受類別的實例 36
  36. 36. 37
  37. 37. 38
  38. 38. • 除了使用函式來定義裝飾器之外,也可以 使用類別來定義裝飾器 39
  39. 39. • 若要使用類別來定義函式裝飾器: 40
  40. 40. • 若最後 some 參考之實例,想偽裝為被裝飾 前的函式 41
  41. 41. 42
  42. 42. 43
  43. 43. 44
  44. 44. 認識 type 類別 • 每個物件實例本身都有個 __class__ 屬性 • 類別本身也有個 __class__ 屬性 45
  45. 45. • 在類別上呼叫 __call__() 會如何呢? 46
  46. 46. • 使用 type 類別建構類別時,必須指定三個 引數 – 類別名稱(字串) – 類別的父類別(tuple) – 類別的屬性(dict) 47
  47. 47. • 物件是類別的實例,而類別是 type 的實例 • 如果有方法能介入 type 建立實例與初始化 的過程,就會有辦法改變類別的行為,這 就是 meta 類別的基本概念 48
  48. 48. • type 是個類別,那麼可以繼承嗎? 49
  49. 49. • 可以在使用 class 定義類別時,指定 metaclass 為 SomeMeta: 50
  50. 50. • 繼承了 type 的類別可以作為 meta 類別 • metaclass 是個協定 • 若指定了 metaclass 的類別, Python 在 剖析完類別定義後, 會使用指定的 metaclass 來進行類別的建構與初始化 51
  51. 51. • 如果使用 class 定義類別時繼承某個父類 別,亦想要指定 metaclass • 使用類別建立物件時: 52
  52. 52. • 若想改變一個類別建立實例與初始化的流 程,則可以在定義 meta 類別時定義 __call__()方法: 53
  53. 53. • meta 類別就是 type 的子類別 • 藉由 metaclass = MetaClass 的協定, 可在類別定義剖析完後,繞送至指定的 meta 類別 • 可以定義 meta 類別的 __new__()方法, 決定類別如何建立 – 定義 meta 類別的 __init__(),可以決定類 別如何初始 – 定義 meta 類別的__call__()方法,決定若 使用類別來建構物件時,該如何進行物件的建 立與初始 54
  54. 54. • metaclass 並不僅僅可指定類別 • 可以指定的對象可以是類別、函式或任何 的物件,只要它具有 __call__() 方法 55
  55. 55. 56
  56. 56. • 可以定義類別的 __abstractmethods__, 指明某些特性是抽象方法 57
  57. 57. • 子類別不會看的到父類別的 __abstractmethods__ 58
  58. 58. 59
  59. 59. 60
  60. 60. • Python 實際上還支援相對匯入(Relative import) • 如果想在 xyz.py 中匯入 abc 模組,在 xyz.py 中不能寫 import abc,在 Python 3 中這會是絕對匯入 • 實際上會匯入的是標準程式庫的 abc 模組 61
  61. 61. • 如果想在 xyz.py 中匯入 mno 模組,在 xyz.py 中不能寫 import mno • 這會引發 ImportError,指出沒有 mno 這個模組 • 如果要使用絕對匯入,必須撰寫 import pkg1.mno • 若要使用相對匯入,則可以撰寫 from . import mno • 如果想使用的是 mno 模組中的 foo()函式, 使用相對匯入的話,還可以撰寫 from .mno import foo 這樣的方式 62
  62. 62. • 在某個程式中,若 import pkg1 的話, 會執行__init__.py 的內容 • 可以在 pkg1 的 __init__.py 中撰寫: • 只要 import pkg1,就可以直接使用 pkg1.abc、pkg2.mno、pkg1.xyz 來使 用模組了 63
  63. 63. • 如果想要 import pkg1 之後,可以直接 使用 pkg1.abc、pkg2.mno、pkg1.xyz • 還能直接使用 pkg1.sub_pkg.foo、 pkg1.sub_pkg.orz 模組 64
  64. 64. • 那麼在 pkg1 的 __init__.py 中,可以撰寫: • 而在 pkg1.sub_pkg 的 __init__.py 中,可 以撰寫: 65
  65. 65. • 相對匯入只能用在套件之中,如果試圖使 用 python 直譯器執行的某個模組中含有 相對匯入,會引發 SystemError 66
  66. 66. 型態邊界 • 可以指定 bound 來定義型態的邊界 67
  67. 67. 68
  68. 68. 共變性 69
  69. 69. • 如果 B 是 A 的子類別,而 Node[B] 可視為 一種 Node[A],則稱 Node 具有共變性或 有彈性的(flexible) • 使用 TypeVar 建立的佔位型態預設為不可 變的(Nonvariant),然而,可藉由 covariant 為 True 來支援共變性 70
  70. 70. 71
  71. 71. 72
  72. 72. • 在支援共變性的情況下,若使用工具於靜 態時期檢查型態正確性時,要留意底下的 問題: 73
  73. 73. 逆變性 • 如果 B 是 A 的子類別,如果 Node[A] 視為 一種 Node[B],則稱 Node 具有逆變性 (Contravariance) • 可藉由 contravariant 為 True 來支援 逆變性 74
  74. 74. • 希望使用 List[Fruit] 來收集全部籃子 的水果,然後依重量排序呢? 75
  75. 75. 76
  76. 76. 77
  77. 77. 結構型態 • Python的物件間有許多協定 • 像Iterable這類的型態,稱為結構型態 (Structural type) – 既有動態語言鴨子定型的優點,又能進行靜態時期 型態檢查 – 在靜態定型語言中若有實現這類功能,也常稱為靜 態時期鴨子定型(Static duck typing) 78
  78. 78. • Python 3.8以後typing提供了Protocol, 可用來自定義結構型態 79
  79. 79. • 結構型態本身也是個抽象類別 80
  80. 80. • 想基於某個已定義的結構型態,擴充新的 協定方法,也可以透過多重繼承來達成 81
  81. 81. • Protocol支援泛型,若定義結構型態時需 要泛型 82
  82. 82. 字面值型態 • Python 3.8新增了字面值型態(Literal type) • 可以透過typing模組的Literal,為特定 的一組字面值定義型態 83
  83. 83. • 如果想設計一個函式,參數只能接受特定 的一組字面值,可以使用Literal來定義 84
  84. 84. • 定義字面值型態時使用的字面值,可以是 整數、位元組(byte)、字串、布林值、 列舉值與None,而且不能是運算式的結果 85
  85. 85. • 也可以基於既有的字面值型態來組合 86

×