流畅的Python(六)-使用一等函数实现设计模式

一、核心要义

《设计模式:可复用面向对象软件的基础》一书中有23个设计模式,其中有16个在动态语言中"不见了或者简化了"。作为动态语言之一的Python, 我们可以利用一等函数简化其中的某些设计模式,本章主要介绍如何使用一等函数重构”策略“模式(关于何为策略模式,可参考策略设计模式)。

二、案例基本描述

用户购置商品,根据不同条件(用户会员积分、单类商品数量、购买的不同商品种类数量),享有不同的优惠策略,最后结算扣除优惠后应该支付的金额。

实体包括:

  • 用户:使用具名元组,包含名字会员积分两个属性
  • 单类商品订单:使用类,包含商品名称商品数量单价和三个属性
  • 优惠策略

     (1)如果用户积分大于1000,享受5%优惠

     (2)单类商品为20个及以上时,享受10%折扣

     (3)购买的不同商品种类数量达到3个或以上,享受7%折扣

二、代码示例

1、案例涉及前置知识

   (1)globals

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/1/27 12:31
# @Author  : Maple
# @File    : 00-相关背景知识.py
# @Software: PyCharm


def fun1(a,b):
    return a + b

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def reading(self):
        pass


RATIO = 1.2

if __name__ == '__main__':

    g = globals()
    # 获取当前模块的相关信息(包名,模块路径,模块包含的函数,类等)
    print(g) # {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000018405083DC0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/01-study/python/fluent_python/06-设计模式/00-相关背景知识.py', '__cached__': None, 'fun1': <function fun1 at 0x000001840543D1F0>, 'Person': <class '__main__.Person'>, 'RATIO': 1.2, 'g': {...}}

   (2)获取当前模块所包含的所有函数

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/1/22 22:16
# @Author  : Maple
# @File    : promotions.py
# @Software: PyCharm

import inspect
import promotions

"""
获取当前模块包含的所有函数
"""

def f1():
    pass


def f2():
    pass


if __name__ == '__main__':

    print(promotions) # <module 'promotions' from 'D:\\01-study\\python\\fluent_python\\06-设计模式\\promotions.py'>
    pros = [func for name,func in inspect.getmembers(promotions,inspect.isfunction)]
    print(pros) # [<function f1 at 0x00000217F33E5C10>, <function f2 at 0x00000217F36539D0>]

2、策略模式传统写法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/1/27 9:48
# @Author  : Maple
# @File    : 01-策略模式传统写法.py
# @Software: PyCharm
from abc import ABC, abstractmethod
from collections import namedtuple

# 顾客具名元组
Customer = namedtuple('Customer','name fidelity')

# # 定义商品类
class Item:

    def __init__(self,product,quantity,price):
        """
        :param product: 商品名称
        :param quantity: 商品数量
        :param price: 商品单价
        """
        self.product = product
        self.quantity = quantity
        self.price = price


    def totol(self):
        """
        :return:订单总费用
        """
        return self.quantity * self.price


# 定义上下文
class Order:

    def __init__(self,customer,cart,promotion=None):
        """
        :param customer: 顾客
        :param cart: 购物车
        :param promotion: 优惠策略
        """
        self.customer = customer
        self.cart = cart
        self.promotion = promotion

    def total(self):
        """
        :return:顾客订单打折之前的总费用
        """
        if not hasattr(self,'__total'):
            self.__total = sum(item.totol() for item in self.cart)
        return self.__total

    def due(self):
        """
        :return:顾客最终支付的费用
        """
        if self.promotion is None:
            return self.total()
        return self.total() - self.promotion.discount(self)

    def __repr__(self):
        fmt = '<Order total: {:.2f} due: {:.2f}>'
        return fmt.format(self.total(),self.due())


# 定义优惠策略的基类
class BasePromotion(ABC):
    @abstractmethod
    def discount(self,order):
        """返回折扣金额"""

# 具体策略1:积分优惠策略
class FidelityPromo(BasePromotion):
    """如果积分大于1000,享受5%优惠"""
    def discount(self,order):
        if order.customer.fidelity > 1000:
            return order.total() * 0.05
        else:
            return 0

# 具体策略2
class BulkItempromo(BasePromotion):
    """单个商品为20个及以上时,提供10%折扣"""
    def discount(self,order):
        discount = 0
        for item in order.cart:
            if item.quantity >= 10:
                discount += item.totol()* 0.1
        return discount

# 具体策略3
class LargetOrderPromo(BasePromotion):
    """购物车中不同商品种类数量达到3个或以上提供7%折扣"""
    def discount(self,order):
        discount = 0
        # 获取购物车中所有不同的商品
        products = {item.product for item in order.cart}
        if len(products) >=3:
            discount += order.total() * 0.07
        return discount


if __name__ == '__main__':
    #1. 策略1示例
    cus1 = Customer('Maple',2000) # 用户积分大于1000,享受5%优惠
    cart1=[Item('banana',20,2.0),Item('apple',10,1.0)]
    p1 = FidelityPromo()
    o1 = Order(cus1,cart1,p1)
    print(o1) # <Order total: 50.00 due: 47.50> 47.5 =  (40 + 10 - 50 *0.05)

    # 2. 策略2示例
    cus2 = Customer('Jack',880)
    cart2= [Item('Book',30,1.0),Item('Radio',5,1.0)] # Book订单超过20个,提供10%折扣
    p2 = BulkItempromo()
    o2 = Order(cus2,cart2,p2)
    print(o2)  # <Order total: 35.00 due: 32.00>  32.00 =  (30 -30 * 0.1) +  5

    # 策略3示例
    cus3 = Customer('Mick', 500)
    cart3 = [Item('Phone', 5, 2.0), Item('Water', 5, 1.0),Item('ring',20,2)] #购物车不同商品达到3个.提供7%折扣
    p3 = LargetOrderPromo()
    o3 = Order(cus3, cart3, p3)
    print(o3)  # <Order total: 55.00 due: 51.15> 51.15> =  (40 + 10 - 50 *0.05)

3、策略模式函数写法

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/1/27 11:25
# @Author  : Maple
# @File    : 02-策略模式函数写法.py
# @Software: PyCharm

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/1/27 9:48
# @Author  : Maple
# @File    : 01-策略模式传统写法.py
# @Software: PyCharm
from abc import ABC, abstractmethod
from collections import namedtuple

# 顾客具名元组
Customer = namedtuple('Customer','name fidelity')

# 定义商品类
class Item:

    def __init__(self,product,quantity,price):
        """
        :param product: 商品名称
        :param quantity: 商品数量
        :param price: 商品单价
        """
        self.product = product
        self.quantity = quantity
        self.price = price


    def totol(self):
        """
        :return:订单总费用
        """
        return self.quantity * self.price


# 定义上下文(订单类)
class Order:

    def __init__(self,customer,cart,promotion=None):
        """
        :param customer: 顾客
        :param cart: 购物车
        :param promotion: 优惠策略
        """
        self.customer = customer
        self.cart = cart
        self.promotion = promotion

    def total(self):
        """
        :return:顾客订单打折之前的总费用
        """
        if not hasattr(self,'__total'):
            self.__total = sum(item.totol() for item in self.cart)
        return self.__total

    def due(self):
        """
        :return:顾客最终支付的费用
        """
        if self.promotion is None:
            return self.total()
        return self.total() - self.promotion(self)

    def __repr__(self):
        fmt = '<Order total: {:.2f} due: {:.2f}>'
        return fmt.format(self.total(), self.due())



# 具体策略1:积分优惠策略
def FidelityPromo(order):
    """如果积分大于1000,享受5%优惠"""
    if order.customer.fidelity > 1000:
        return order.total() * 0.05
    else:
        return 0

# 具体策略2
def BulkItempromo(order):
    """单个商品为20个及以上时,提供10%折扣"""
    discount = 0
    for item in order.cart:
        if item.quantity >= 10:
            discount += item.totol()* 0.1
    return discount

# 具体策略3
def LargetOrderPromo(order):
    """购物车中不同商品种类数量达到3个或以上提供7%折扣"""
    discount = 0
    # 获取购物车中所有不同的商品
    products = {item.product for item in order.cart}
    if len(products) >=3:
        discount += order.total() * 0.07
    return discount


if __name__ == '__main__':
    #1. 策略1示例
    cus1 = Customer('Maple',2000)# 用户积分大于1000,享受5%优惠
    cart1=[Item('banana',20,2.0),Item('apple',10,1.0)]
    o1 = Order(cus1,cart1,FidelityPromo)
    print(o1) # <Order total: 50.00 due: 47.50> 47.5 =  (40 + 10 - 50 *0.05)

    # 2. 策略2示例
    cus2 = Customer('Jack',880)
    cart2= [Item('Book',30,1.0),Item('Radio',5,1.0)] #Book订单超过20个,提供10%折扣
    o2 = Order(cus2,cart2,BulkItempromo)
    print(o2)  # <Order total: 35.00 due: 32.00>  32.00 =  (30 -30 * 0.1) +  5

    # 策略3示例
    cus3 = Customer('Mick', 500)
    cart3 = [Item('Phone', 5, 2.0), Item('Water', 5, 1.0),Item('ring',20,2)] #购物车不同商品达到3个.提供7%折扣
    o3 = Order(cus3, cart3, LargetOrderPromo)
    print(o3)  # <Order total: 55.00 due: 51.15> 51.15> =  (40 + 10 - 50 *0.05)

4、策略模式函数写法-最优策略

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/1/27 11:34
# @Author  : Maple
# @File    : 03-策略模式函数写法-最优策略.py
# @Software: PyCharm

"""
如何自动选择最优策略?
"""

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/1/27 11:25
# @Author  : Maple
# @File    : 02-策略模式函数写法.py
# @Software: PyCharm

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/1/27 9:48
# @Author  : Maple
# @File    : 01-策略模式传统写法.py
# @Software: PyCharm
from abc import ABC, abstractmethod
from collections import namedtuple

# 顾客具名元组
Customer = namedtuple('Customer','name fidelity')

# 定义商品类
class Item:

    def __init__(self,product,quantity,price):
        """
        :param product: 商品名称
        :param quantity: 商品数量
        :param price: 商品单价
        """
        self.product = product
        self.quantity = quantity
        self.price = price


    def totol(self):
        """
        :return:订单总费用
        """
        return self.quantity * self.price


# 定义上下文(订单类)
class Order:

    def __init__(self,customer,cart,promotion=None):
        """
        :param customer: 顾客
        :param cart: 购物车
        :param promotion: 优惠策略
        """
        self.customer = customer
        self.cart = cart
        self.promotion = promotion

    def total(self):
        """
        :return:顾客订单打折之前的总费用
        """
        if not hasattr(self,'__total'):
            self.__total = sum(item.totol() for item in self.cart)
        return self.__total

    def due(self):
        """
        :return:顾客最终支付的费用
        """
        if self.promotion is None:
            return self.total()
        return self.total() - self.promotion(self)

    def __repr__(self):
        fmt = '<Order total: {:.2f} due: {:.2f}>'
        return fmt.format(self.total(), self.due())


# 具体策略1:积分优惠策略
def FidelityPromo(order):
    """如果积分大于1000,享受15%优惠"""
    if order.customer.fidelity > 1000:
        return order.total() * 0.15
    else:
        return 0

# 具体策略2
def BulkItemPromo(order):
    """单个商品为20个及以上时,提供10%折扣"""
    discount = 0
    for item in order.cart:
        if item.quantity >= 10:
            discount += item.totol()* 0.1
    return discount

# 具体策略3
def LargetOrderPromo(order):
    """购物车中不同商品种类数量达到3个或以上提供7%折扣"""
    discount = 0
    # 获取购物车中所有不同的商品
    products = {item.product for item in order.cart}
    if len(products) >=3:
        discount += order.total() * 0.07
    return round(discount,2)

# 最优策略
def optimization_strategy(order):
    """
    :param order: 订单类
    :return:最优策略和最大折扣
    """
    # 手动定义所有优惠策略
    promos = [FidelityPromo,BulkItemPromo,LargetOrderPromo]
    p_final =  None
    discount_final = 0
    for p in promos:
        discount = p(order)
        if discount > discount_final:
            discount_final = discount
            p_final = p
    return (p_final,discount_final)

# 最优策略-获取当前模块所有策略的另外一种方式
def optimization_strategy2(order):
    # globals返回一个字典,表示当前的全局符号表
    promos = [globals()[name] for name in globals()
              if name.endswith('Promo')]

    p_final = None
    discount_final = 0
    for p in promos:
        discount = p(order)
        if discount > discount_final:
            discount_final = discount
            p_final = p
    return (p_final, discount_final)





if __name__ == '__main__':
    #1. 最优策略示例1
    cus1 = Customer('Maple',2000)# 用户积分大于1000,享受15%(注意:为了测试,数值从5%调整到15%)优惠
    cart1=[Item('banana',20,2.0),Item('apple',10,1.0)]
    o1 = Order(cus1,cart1,FidelityPromo)
    print(optimization_strategy(o1)) # (<function FidelityPromo at 0x000001C46A4D8DC0>, 7.5)
    print(optimization_strategy2(o1)) # (<function FidelityPromo at 0x000001C46A4D8DC0>, 7.5)

    print('=====================================================')

    # 2. 最优策略示例2
    cus2 = Customer('Jack',880)
    cart2= [Item('Book',30,1.0),Item('Radio',5,1.0)] #Book订单超过20个,提供10%折扣
    o2 = Order(cus2,cart2,BulkItemPromo)
    print(optimization_strategy(o2)) # (<function BulkItemPromo at 0x0000018BF0F041F0>, 3.0)
    print(optimization_strategy2(o2)) # (<function BulkItemPromo at 0x00000233DF99E1F0>, 3.0)
    print('=====================================================')

    # 3. 最优策略示例3
    cus3 = Customer('Mick', 300)
    cart3 = [Item('Phone', 5, 2.0), Item('Water', 5, 1.0),Item('ring',8,2)] #购物车不同商品达到3个.提供7%折扣
    o3 = Order(cus3, cart3, LargetOrderPromo)
    print(optimization_strategy(o3))  # (<function LargetOrderPromo at 0x0000024CEE094280>, 2.17)
    print(optimization_strategy2(o3)) # (<function LargetOrderPromo at 0x00000233DF99E280>, 2.17)

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值