• 例子:工资结算系统。

      ```Python “”” 月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成 “”” from abc import ABCMeta, abstractmethod

    class Employee(metaclass=ABCMeta): “””员工(抽象类)”””

    class Manager(Employee): “””部门经理”””

    1. return 15000.0

    class Programmer(Employee): “””程序员”””

    1. def __init__(self, name, working_hour=0):
    2. self.working_hour = working_hour
    3. super().__init__(name)
    4. def get_salary(self):
    5. return 200.0 * self.working_hour

    class Salesman(Employee): “””销售员”””

    1. def __init__(self, name, sales=0.0):
    2. self.sales = sales
    3. super().__init__(name)
    4. def get_salary(self):
    5. return 1800.0 + self.sales * 0.05

    class EmployeeFactory: “””创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)”””

    def main(): “””主函数””” emps = [ EmployeeFactory.create(‘M’, ‘曹操’), EmployeeFactory.create(‘P’, ‘荀彧’, 120), EmployeeFactory.create(‘P’, ‘郭嘉’, 85), EmployeeFactory.create(‘S’, ‘典韦’, 123000), ] for emp in emps: print(f’{emp.name}: {emp.get_salary():.2f}元’)

    if name == ‘main‘: main()

    1. - 类与类之间的关系
    2. - is-a关系:继承
    3. - has-a关系:关联 / 聚合 / 合成
    4. - use-a关系:依赖
    5. 例子:扑克游戏。
    6. ```Python
    7. """
    8. 经验:符号常量总是优于字面常量,枚举类型是定义符号常量的最佳选择
    9. """
    10. from enum import Enum, unique
    11. import random
    12. @unique
    13. class Suite(Enum):
    14. """花色"""
    15. SPADE, HEART, CLUB, DIAMOND = range(4)
    16. def __lt__(self, other):
    17. return self.value < other.value
    18. class Card():
    19. """牌"""
    20. def __init__(self, suite, face):
    21. """初始化方法"""
    22. self.suite = suite
    23. self.face = face
    24. def show(self):
    25. """显示牌面"""
    26. suites = ['♠︎', '♥︎', '♣︎', '♦︎']
    27. faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
    28. return f'{suites[self.suite.value]}{faces[self.face]}'
    29. def __repr__(self):
    30. return self.show()
    31. class Poker():
    32. """扑克"""
    33. def __init__(self):
    34. self.index = 0
    35. self.cards = [Card(suite, face)
    36. for face in range(1, 14)]
    37. def shuffle(self):
    38. random.shuffle(self.cards)
    39. self.index = 0
    40. def deal(self):
    41. """发牌"""
    42. card = self.cards[self.index]
    43. self.index += 1
    44. return card
    45. @property
    46. def has_more(self):
    47. return self.index < len(self.cards)
    48. class Player():
    49. """玩家"""
    50. def __init__(self, name):
    51. self.name = name
    52. self.cards = []
    53. def get_one(self, card):
    54. """摸一张牌"""
    55. self.cards.append(card)
    56. def sort(self, comp=lambda card: (card.suite, card.face)):
    57. """整理手上的牌"""
    58. self.cards.sort(key=comp)
    59. def main():
    60. """主函数"""
    61. poker = Poker()
    62. poker.shuffle()
    63. players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')]
    64. while poker.has_more:
    65. for player in players:
    66. player.get_one(poker.deal())
    67. for player in players:
    68. player.sort()
    69. print(player.name, end=': ')
    70. print(player.cards)
    71. if __name__ == '__main__':
    72. main()
    • 垃圾回收、循环引用和弱引用

      Python使用了自动化内存管理,这种管理机制以引用计数为基础,同时也引入了标记-清除分代收集两种机制为辅的策略。

      1. typedef struct _object {
      2. /* 引用计数 */
      3. int ob_refcnt;
      4. /* 对象指针 */
      5. struct _typeobject *ob_type;
      6. } PyObject;
      1. /* 增加引用计数的宏定义 */
      2. #define Py_INCREF(op) ((op)->ob_refcnt++)
      3. /* 减少引用计数的宏定义 */
      4. #define Py_DECREF(op) \ //减少计数
      5. if (--(op)->ob_refcnt != 0) \
      6. ; \
      7. else \
      8. __Py_Dealloc((PyObject *)(op))

      导致引用计数+1的情况:

      • 对象被创建,例如a = 23
      • 对象被引用,例如
      • 对象被作为参数,传入到一个函数中,例如f(a)
      • 对象作为一个元素,存储在容器中,例如list1 = [a, a]

      导致引用计数-1的情况:

      • 对象的别名被显式销毁,例如del a
      • 对象的别名被赋予新的对象,例如a = 24
      • 一个对象离开它的作用域,例如f函数执行完毕时,f函数中的局部变量(全局变量不会)
      • 对象所在的容器被销毁,或从容器中删除对象

      引用计数可能会导致循环引用问题,而循环引用会导致内存泄露,如下面的代码所示。为了解决这个问题,Python中引入了“标记-清除”和“分代收集”。在创建一个对象的时候,对象被放在第一代中,如果在第一代的垃圾检查中对象存活了下来,该对象就会被放到第二代中,同理在第二代的垃圾检查中对象存活下来,该对象就会被放到第三代中。

      以下情况会导致垃圾回收:

      • 调用gc.collect()
      • 程序退出

      如果循环引用中两个对象都定义了__del__方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的__del__方法,这个问题在Python 3.6中得到了解决。

      也可以通过weakref模块构造弱引用的方式来解决循环引用的问题。

    • 魔法属性和方法(请参考《Python魔法方法指南》)

      有几个小问题请大家思考:

      • 自定义的对象能不能使用运算符做运算?
      • 自定义的对象能不能放到set中?能去重吗?
      • 自定义的对象能不能作为dict的键?
      • 自定义的对象能不能使用上下文语法?
    • 例子:自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。

      ```Python class SetOnceMappingMixin:

      1. """自定义混入类"""
      2. __slots__ = ()
      3. def __setitem__(self, key, value):
      4. if key in self:
      5. raise KeyError(str(key) + ' already set')
      6. 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)

    1. - 元编程和元类
    2. 对象是通过类创建的,类是通过元类创建的,元类提供了创建类的元信息。所有的类都直接或间接的继承自`object`,所有的元类都直接或间接的继承自`type`
    3. 例子:用元类实现单例模式。
    4. ```Python
    5. import threading
    6. class SingletonMeta(type):
    7. """自定义元类"""
    8. def __init__(cls, *args, **kwargs):
    9. cls.__instance = None
    10. cls.__lock = threading.RLock()
    11. super().__init__(*args, **kwargs)
    12. def __call__(cls, *args, **kwargs):
    13. if cls.__instance is None:
    14. with cls.__lock:
    15. if cls.__instance is None:
    16. cls.__instance = super().__call__(*args, **kwargs)
    17. return cls.__instance
    18. class President(metaclass=SingletonMeta):
    19. """总统(单例类)"""
    20. pass
    • 面向对象设计原则

      • 单一职责原则 (SRP)- 一个类只做该做的事情(类的设计要高内聚)
      • 开闭原则 (OCP)- 软件实体应该对扩展开发对修改关闭
      • 依赖倒转原则(DIP)- 面向抽象编程(在弱类型语言中已经被弱化)
      • 里氏替换原则(LSP) - 任何时候可以用子类对象替换掉父类对象
      • 接口隔离原则(ISP)- 接口要小而专不要大而全(Python中没有接口的概念)
      • 合成聚合复用原则(CARP) - 优先使用强关联关系而不是继承关系复用代码
      • 最少知识原则(迪米特法则,LoD)- 不要给没有必然联系的对象发消息

      说明:上面加粗的字母放在一起称为面向对象的SOLID原则。

    • GoF设计模式

      • 创建型模式:单例、工厂、建造者、原型
      • 结构型模式:适配器、门面(外观)、代理
      • 行为型模式:迭代器、观察者、状态、策略

      例子:可插拔的哈希算法(策略模式)。

      ```Python class StreamHasher():

      1. """哈希摘要生成器"""
      2. def __init__(self, alg='md5', size=4096):
      3. self.size = size
      4. alg = alg.lower()
      5. self.hasher = getattr(__import__('hashlib'), alg.lower())()
      6. def __call__(self, stream):
      7. return self.to_digest(stream)
      8. def to_digest(self, stream):
      9. """生成十六进制形式的摘要"""
      10. for buf in iter(lambda: stream.read(self.size), b''):
      11. self.hasher.update(buf)

      def main():