《流畅的Python》第六章学习笔记

2020-12-24 11:36:33 浏览数 (1)

对接口编程,而不是对实现编程。 优先使用对象组合,而不是类继承。

策略模式:定义一系列算法,把他们一一封装起来,并且使它们可以相互替换。本模式使得算法可以独立于使用它的客户而变化。

抽象基类

在Python3.4中,声明抽象基类最简单的方式是子类话abc.ABC.需要使用装饰器@abstractmethod

代码语言:javascript复制
from abc import ABC, abstractmethod


class Foo(ABC):
    @abstractmethod
    def fun(self):
        '''please Implemente in subclass'''


class SubFoo(Foo):
    def fun(self):
        print('fun in SubFoo')

a = SubFoo()
a.fun()

Python3.0到Python3.3,必须在class语句中使用metaclass=ABCMeta

代码语言:javascript复制
from abc import abstractmethod, ABCMeta

class Bar(metaclass=ABCMeta):
    @abstractmethod
    def fun(self):
        '''please Implemente in subclass'''


class SubBar(Bar):
    def fun(self):
        print('fun in SubBar')


b = SubBar()
b.fun()

Python2中使用__metaclass__=ABCMeta

代码语言:javascript复制
from abc import ABCMeta, abstractmethod


class FooBar():
    __metaclass__ = ABCMeta
    @abstractmethod
    def fun(self):
         '''please Implemente in subclass'''


class SubFooBar(FooBar):
    def fun(self):
        print('fun in SubFooBar')

a = SubFooBar()
a.fun()

使用策略进行折扣(中文注释)

代码语言:javascript复制
# -*- coding: utf-8 -*-
# @Time    : 2020/12/16 下午8:34
# @Author  : zhongxin
# @Email   : 490336534@qq.com
# @File    : 策略模式.py
from abc import ABC, abstractmethod
from collections import namedtuple

Customer = namedtuple("Customer", "name fidelity")


class LineItem():
    def __init__(self, product, quantity, price):
        """

        :param product: 产品
        :param quantity:数量
        :param price:价格
        """
        self.product = product
        self.quantity = quantity
        self.price = price

    def total(self):
        """
        计算总价
        :return:
        """
        return self.price * self.quantity


class Order():
    def __init__(self, customer: Customer, cart: [LineItem], promotion=None):
        """

        :param customer: 顾客
        :param cart: 选购清单
        :param promotion: 折扣策略
        """
        self.customer = customer
        self.cart = list(cart)
        self.promotion = promotion

    def total(self):
        """
        计算总价
        :return:
        """
        if not hasattr(self, '__total'):
            self.__total = sum(item.total() for item in self.cart)
            return self.__total

    def due(self):
        """
        计算折扣后价格
        :return:
        """
        if self.promotion is None:
            discount = 0  # 没有任何优惠
        else:
            discount = self.promotion.discount(self)  # 调用折扣计算方法,获得优惠金额
        return self.total() - discount

    def __repr__(self):
        return f"<原价:{self.total():.2f} 折后价 {self.due():.2f}>"


class Promotion(ABC):
    """
    策略:抽象基类
    """

    @abstractmethod
    def discount(self, order):
        """返回折扣金额(正值)"""


class FidelityPromo(Promotion):
    """
    折扣策略:积分1000 的顾客5%折扣
    """

    def discount(self, order):
        return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0


class BulkItemPromo(Promotion):
    """
    折扣策略:单个商品20 个10%折扣
    """

    def discount(self, order):
        discount = 0
        for item in order.cart:
            if item.quantity >= 20:
                discount  = item.total() * 0.1  # 对数量大于20的单个商品进行折扣
        return discount


class LargeOrderPromo(Promotion):
    """
    折扣策略:不同商品达到10 个 7%折扣
    """

    def discount(self, order):
        distinct_items = {item.product for item in order.cart}
        if len(distinct_items) >= 10:
            return order.total() * 0.07  # 对全部商品进行折扣
        return 0


if __name__ == '__main__':
    joe = Customer('Joe Doe', 0)  # 积分0
    ann = Customer("Ann Smith", 1100)  # 积分1100
    cart = [
        LineItem('香蕉', 4, 0.5),
        LineItem('苹果', 10, 1.5),
        LineItem('西瓜', 5, 5.0),
    ]
    print(Order(joe, cart, FidelityPromo()))  # <原价:42.00 折后价 42.00>
    print(Order(ann, cart, FidelityPromo()))  # <原价:42.00 折后价 39.90>

    banana_cart = [
        LineItem("香蕉", 30, 0.5),
        LineItem("苹果", 10, 1.5),
    ]
    print(Order(joe, banana_cart, BulkItemPromo()))  # <原价:30.00 折后价 28.50>

    long_cart = [LineItem(str(item_code), 10, 1.0) for item_code in range(10)]  # 10件不同的商品
    print(Order(joe, long_cart, LargeOrderPromo()))  # <原价:100.00 折后价 93.00>

享元:享元是可共享的对象,可以同时在多个上下文中使用。

Order类可以通过传入不同的promotion来实现不同的折扣策略

inspect 检查对象

官方文档:https://docs.python.org/zh-cn/3/library/inspect.html

inspect 模块提供了一些有用的函数帮助获取对象的信息,例如模块、类、方法、函数、回溯、帧对象以及代码对象。例如它可以帮助你检查类的内容,获取某个方法的源代码,取得并格式化某个函数的参数列表,或者获取你需要显示的回溯的详细信息。

该模块提供了4种主要的功能:

  • 类型检查
  • 获取源代码
  • 检查类与函数
  • 检查解释器的调用堆栈

判断是否为类

代码语言:javascript复制
def isclass(object):
    """Return true if the object is a class.

    Class objects provide these attributes:
        __doc__         documentation string
        __module__      name of module in which this class was defined"""
    return isinstance(object, type)

判断是否为函数

代码语言:javascript复制
def isfunction(object):
    """Return true if the object is a user-defined function.

    Function objects provide these attributes:
        __doc__         documentation string
        __name__        name with which this function was defined
        __code__        code object containing compiled function bytecode
        __defaults__    tuple of any default values for arguments
        __globals__     global namespace in which this function was defined
        __annotations__ dict of parameter annotations
        __kwdefaults__  dict of keyword only parameters with defaults"""
    return isinstance(object, types.FunctionType)

包含对象的所有成员的(name, value)列表

代码语言:javascript复制
def getmembers(object, predicate=None):
    """Return all members of an object as (name, value) pairs sorted by name.
    Optionally, only return members that satisfy a given predicate."""
    if isclass(object):
        mro = (object,)   getmro(object)
    else:
        mro = ()
    results = []
    processed = set()
    names = dir(object) # 使用dir方法拿到全部的属性
    # :dd any DynamicClassAttributes to the list of names if object is a class;
    # this may result in duplicate entries if, for example, a virtual
    # attribute with the same name as a DynamicClassAttribute exists
    try:
        for base in object.__bases__:
            for k, v in base.__dict__.items():
                if isinstance(v, types.DynamicClassAttribute):
                    names.append(k)
    except AttributeError:
        pass
    for key in names:
        # First try to get the value via getattr.  Some descriptors don't
        # like calling their __get__ (see bug #1785), so fall back to
        # looking in the __dict__.
        try:
            value = getattr(object, key)
            # handle the duplicate key
            if key in processed:
                raise AttributeError
        except AttributeError:
            for base in mro:
                if key in base.__dict__:
                    value = base.__dict__[key]
                    break
            else:
                # could be a (currently) missing slot member, or a buggy
                # __dir__; discard and move on
                continue
        if not predicate or predicate(value):
            results.append((key, value))
        processed.add(key)
    results.sort(key=lambda pair: pair[0])
    return results

getmembers

获取源码

代码语言:javascript复制
inspect.getsource(Order)

获取源码

0 人点赞