鱼C论坛

 找回密码
 立即注册
查看: 3285|回复: 15

请问一下类中的专有属性、专有变量

[复制链接]
发表于 2022-10-21 10:56:25 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
  1. from abc import ABC, abstractclassmethod
  2. from collections import namedtuple

  3. Customer = namedtuple('Customer', 'name fidelity')

  4. class LineItem:
  5.     def __init__(self, product, quantity, price):
  6.         self.product = product
  7.         self.quantity = quantity
  8.         self.price = price

  9.     def total(self):
  10.         return self.price * self.quantity


  11. class Order:
  12.     def __init__(self, customer, cart, promotion=None):
  13.         self.customer = customer
  14.         self.cart = list(cart)
  15.         self.promotion = promotion

  16.     def total(self):
  17.         if not hasattr(self, '__total'):
  18.             self.__total = sum(item.total() for item in self.cart)
  19.         return self.__total

  20.     def due(self):
  21.         if self.promotion is None:
  22.             discount = 0
  23.         else:
  24.             discount = self.promotion.discount(self)
  25.         return self.total() - discount

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


  29. class Promotion(ABC):

  30.     @abstractclassmethod
  31.     def discount(self, order):
  32.         """返回折扣金额(正值)"""


  33. class FidelityPromo(Promotion):
  34.     """为积分为1000或以上的顾客提供%5的折扣"""

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


  37. class BulkItemPromo(Promotion):
  38.     """单个商品为20个以上时提供10%折扣"""

  39.     def discount(self, order):
  40.         discount = 0
  41.         for item in order.cart:
  42.             if item.quantity >= 20:
  43.                 discount += item.total() * .1
  44.         return discount


  45. class LargeOrderPromo(Promotion):
  46.     """订单中的不同商品达到10个以上时提供7%折扣"""
  47.     def discount(self, order):
  48.         distinct_items = {item.product for item in order.cart}
  49.         if len(distinct_items) >= 10:
  50.             return order.total() * .07
  51.         return 0
复制代码


代码如上,请教一下第24行中的,__total是类中的专属变量,如果将此变量更改为不带下划线的普通变量,程序就会报错,请问这是为什么?

TypeError: 'float' object is not callable
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2022-10-21 11:17:23 | 显示全部楼层
本帖最后由 jackz007 于 2022-10-21 11:21 编辑

         因为你的类中有名为 total 的方法,
  1. class LineItem:
  2. . . . . . .
  3.     def total(self):
  4. . . . . . .
  5. class Order:
  6. . . . . . .
  7.     def total(self):
复制代码

        如果去掉下划线,就会和函数名冲突,在为 total 赋值以后,函数的调用地址就被覆盖了,调用 total() 的时候,Python 就会告诉你,浮点对象不可以被 calll。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-10-21 14:54:02 | 显示全部楼层
本帖最后由 1005663861 于 2022-10-21 14:55 编辑
jackz007 发表于 2022-10-21 11:17
因为你的类中有名为 total 的方法,

        如果去掉下划线,就会和函数名冲突,在为 total  ...


那如果后边的函数名,从total改为其他的话,为什么也不行
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-21 15:00:50 | 显示全部楼层
本帖最后由 jackz007 于 2022-10-21 15:04 编辑
1005663861 发表于 2022-10-21 14:54
那如果后边的函数名,从total改为其他的话,为什么也不行


        如果改方法名,需要同时改定义和调用,显然,如果只改定义,那么调用就会落空,最好通过文本搜索 "total" 进行定位,一定要改彻底,一个不漏。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-10-21 15:05:05 | 显示全部楼层
jackz007 发表于 2022-10-21 15:00
如果改方法名,需要同时改定义和调用,显然,如果只改定义,那么调用就会落空,最好通过文本搜 ...

是的,一个不漏
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-21 15:07:36 | 显示全部楼层

         不可能,上错误信息。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-10-21 15:18:49 | 显示全部楼层
jackz007 发表于 2022-10-21 15:07
不可能,上错误信息。
  1. rom abc import ABC, abstractclassmethod
  2. from collections import namedtuple

  3. Customer = namedtuple('Customer', 'name fidelity')

  4. class LineItem:
  5.     def __init__(self, product, quantity, price):
  6.         self.product = product
  7.         self.quantity = quantity
  8.         self.price = price

  9.     def total(self):
  10.         return self.price * self.quantity


  11. class Order:
  12.     def __init__(self, customer, cart, promotion=None):
  13.         self.customer = customer
  14.         self.cart = list(cart)
  15.         self.promotion = promotion

  16.     def total1(self):
  17.         if not hasattr(self, '__total'):
  18.             self.total1 = sum(item.total() for item in self.cart)
  19.         return self.total1

  20.     def due(self):
  21.         if self.promotion is None:
  22.             discount = 0
  23.         else:
  24.             discount = self.promotion.discount(self)
  25.         return self.total1() - discount

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


  29. class Promotion(ABC):

  30.     @abstractclassmethod
  31.     def discount(self, order):
  32.         """返回折扣金额(正值)"""


  33. class FidelityPromo(Promotion):
  34.     """为积分为1000或以上的顾客提供%5的折扣"""

  35.     def discount(self, order):
  36.         return order.total1() * .05 if order.customer.fidelity >= 1000 else 0


  37. class BulkItemPromo(Promotion):
  38.     """单个商品为20个以上时提供10%折扣"""

  39.     def discount(self, order):
  40.         discount = 0
  41.         for item in order.cart:
  42.             if item.quantity >= 20:
  43.                 discount += item.total() * .1
  44.         return discount


  45. class LargeOrderPromo(Promotion):
  46.     """订单中的不同商品达到10个以上时提供7%折扣"""
  47.     def discount(self, order):
  48.         distinct_items = {item.product for item in order.cart}
  49.         if len(distinct_items) >= 10:
  50.             return order.total1() * .07
  51.         return 0
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-21 15:24:56 | 显示全部楼层
本帖最后由 jackz007 于 2022-10-21 15:26 编辑


         我不会用你的代码,你不是说改方法名不行吗,我要的是你运行中得到的错误信息。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-10-21 15:47:33 | 显示全部楼层
jackz007 发表于 2022-10-21 15:24
我不会用你的代码,你不是说改方法名不行吗,我要的是你运行中得到的错误信息。

TypeError: 'float' object is not callable
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-21 16:02:04 | 显示全部楼层
本帖最后由 jackz007 于 2022-10-21 16:10 编辑
1005663861 发表于 2022-10-21 15:47
TypeError: 'float' object is not callable


        你自己认为问题会出在哪里???

        真的全部都改了
  1. class LineItem:
  2. . . . . . .
  3.     def total(self):
复制代码

  1. class Order:
  2. . . . . . .
  3.     def total1(self):
  4.         if not hasattr(self, '__total'):
  5.             self.total1 = sum(item.total() for item in self.cart)
复制代码

  1. class BulkItemPromo(Promotion):
  2. . . . . . .
  3.     def discount(self, order):
  4. . . . . . .
  5.                 discount += item.total() * .1
复制代码


小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-10-21 16:23:31 | 显示全部楼层
jackz007 发表于 2022-10-21 16:02
你自己认为问题会出在哪里???

        真的全部都改了

我不知道啊,我把hasattr()里面的第二个参数也改了,还是报错,

TypeError: unsupported operand type(s) for -: 'method' and 'int'
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-21 16:31:08 | 显示全部楼层
1005663861 发表于 2022-10-21 16:23
我不知道啊,我把hasattr()里面的第二个参数也改了,还是报错,

TypeError: unsupported operand ty ...


         我就不得不好奇,你没有哪个能力干嘛要找这个麻烦?就因为看 __total 不顺眼?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-21 16:40:45 | 显示全部楼层
本帖最后由 阿奇_o 于 2022-10-21 17:18 编辑

去掉折扣部分,我直接补全,让代码可以运行,让你看看效果和原因:
  1. from collections import namedtuple

  2. Customer = namedtuple('Customer', 'name fidelity')


  3. class Order:
  4.     def __init__(self, customer, cart, promotion=None):
  5.         self.customer = customer
  6.         self.cart = cart
  7.         self.promotion = promotion

  8.     def total(self):
  9.         if not hasattr(self, '__total'):
  10.             self.__total = sum(item.price * item.quantity for item in self.cart)
  11.         return self.__total
  12.     # def total(self):
  13.     #     if not hasattr(self, 'total'):  # 始终存在 total,因为你定义的名字就叫 total
  14.     #         self.total = sum(item.price * item.quantity for item in self.cart)
  15.     #     return self.total  # 这是直接返回该方法本身了!


  16. class Cart:
  17.     def __init__(self, items=None):
  18.         if items:
  19.             self.items = items
  20.    
  21.     def __len__(self):
  22.         return len(self.items)

  23.     def __getitem__(self, position):
  24.         return self.items[position]


  25. if __name__ == "__main__":
  26.     c1 = Customer('张三', 1000)

  27.     Item = namedtuple('Item', 'product price quantity')
  28.     it1, it2 = Item('A', 100, 2), Item('B', 200, 1)
  29.     cart = Cart(items=[it1, it2])

  30.     print(cart.items)
  31.     print(cart[0], cart[0].price, cart[0].quantity)
  32.    
  33.     o = Order(customer=c1, cart=cart)
  34.     print(f"{o.customer.name} 的这个订单,合计:¥ {o.total()} 元")
复制代码

ps:
其实,我觉得 Order 里 total 不用定义为一个实例方法,也不用搞什么私有变量,直接定义为一个实例变量self.total即可,如
        if self.cart:
            self.total = sum(item.price * item.quantity for item in self.cart)
        else:
            self.total = 0
毕竟,订单了创建 一般就意味着 购物车不是空的了(里有商品条目了)。



小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-10-21 17:13:16 | 显示全部楼层
jackz007 发表于 2022-10-21 16:31
我就不得不好奇,你没有哪个能力干嘛要找这个麻烦?就因为看 __total 不顺眼?

想知道其中的原理是什么,就需要尝试一下,更改
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-21 17:25:10 | 显示全部楼层
1005663861 发表于 2022-10-21 17:13
想知道其中的原理是什么,就需要尝试一下,更改

        这个是我修改的,用这个代码再测试呢。
  1. from abc import ABC, abstractclassmethod
  2. from collections import namedtuple

  3. Customer = namedtuple('Customer', 'name fidelity')

  4. class LineItem:
  5.     def __init__(self, product, quantity, price):
  6.         self.product = product
  7.         self.quantity = quantity
  8.         self.price = price

  9.     def totalx(self):
  10.         return self.price * self.quantity


  11. class Order:
  12.     def __init__(self, customer, cart, promotion=None):
  13.         self.customer = customer
  14.         self.cart = list(cart)
  15.         self.promotion = promotion

  16.     def totalx(self):
  17.         if not hasattr(self, 'total'):
  18.             self.total = sum(item.totalx() for item in self.cart)
  19.         return self.total

  20.     def due(self):
  21.         if self.promotion is None:
  22.             discount = 0
  23.         else:
  24.             discount = self.promotion.discount(self)
  25.         return self.totalx() - discount

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


  29. class Promotion(ABC):

  30.     @abstractclassmethod
  31.     def discount(self, order):
  32.         """返回折扣金额(正值)"""


  33. class FidelityPromo(Promotion):
  34.     """为积分为1000或以上的顾客提供%5的折扣"""

  35.     def discount(self, order):
  36.         return order.totalx() * .05 if order.customer.fidelity >= 1000 else 0


  37. class BulkItemPromo(Promotion):
  38.     """单个商品为20个以上时提供10%折扣"""

  39.     def discount(self, order):
  40.         discount = 0
  41.         for item in order.cart:
  42.             if item.quantity >= 20:
  43.                 discount += item.totalx() * .1
  44.         return discount


  45. class LargeOrderPromo(Promotion):
  46.     """订单中的不同商品达到10个以上时提供7%折扣"""
  47.     def discount(self, order):
  48.         distinct_items = {item.product for item in order.cart}
  49.         if len(distinct_items) >= 10:
  50.             return order.totalx() * .07
  51.         return 0
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-21 17:35:40 | 显示全部楼层
你的问题中Order这个类的total可以写成函数形式,也可以写成self.total的形式,就像前面的人说的那样。

而且看了你的代码我觉得有些问题,你虽然使用了ABC进行抽象,但是似乎自己都没有规划好每个接口应该具备怎样的能力。从设计模式的角度来说,你虽然使用了接口,但是却违背了里氏替换原则和依赖倒置原则。所以我把你的代码顺序进行了大幅改动,逻辑微调(就是Order类的total方法那个部分),保留了你要的total,也删除了__total这个私有属性,希望修改后的代码能对你有用。另外请注意,我使用了Python 3.10中的新特性来标注参数类型与返回值类型:
  1. from abc import ABCMeta, abstractmethod
  2. from collections import namedtuple

  3. # ----------抽象层,定义各种接口----------
  4. Customer = namedtuple('Customer', 'name fidelity')


  5. class InterfaceItem(metaclass=ABCMeta):
  6.     """商品接口,子类须具备product、price、quantity属性,
  7.     必须实现total方法
  8.     """

  9.     product: str
  10.     price: int | float
  11.     quantity: int | float

  12.     @abstractmethod
  13.     def total(self) -> int | float:
  14.         """商品总价"""
  15.         pass


  16. class InterfaceOrder(metaclass=ABCMeta):
  17.     """订单接口,子类须具备cart、customer、total属性,
  18.     且必须实现due方法
  19.     """

  20.     cart: list[InterfaceItem]
  21.     customer: Customer

  22.     @property
  23.     @abstractmethod
  24.     def total(self) -> int | float:
  25.         """订单商品的总金额"""
  26.         pass

  27.     @abstractmethod
  28.     def due(self) -> int | float:
  29.         """应该支付金额"""
  30.         pass


  31. class InterfacePromotion(metaclass=ABCMeta):
  32.     """促销活动接口,子类必须实现discount方法"""

  33.     @abstractmethod
  34.     def discount(self, order: InterfaceOrder) -> int | float:
  35.         """返回折扣金额(正值)"""
  36.         pass


  37. # ----------实现层,实现各种接口----------
  38. class LineItem(InterfaceItem):
  39.     """具体商品,实现IterfaceItem接口"""

  40.     def __init__(
  41.         self,
  42.         product: str,
  43.         quantity: int | float,
  44.         price: int | float
  45.     ) -> None:
  46.         self.product = product
  47.         self.quantity = quantity
  48.         self.price = price

  49.     def total(self) -> int | float:
  50.         return self.price * self.quantity


  51. class Order(InterfaceOrder):
  52.     """具体订单,实现IterfaceOrder接口"""

  53.     def __init__(
  54.             self,
  55.             customer: Customer,
  56.             *cart: InterfaceItem,
  57.             promotion: InterfacePromotion = None
  58.     ) -> None:
  59.         self.customer = customer
  60.         self.cart = list(cart)
  61.         self.promotion = promotion

  62.     @property
  63.     def total(self) -> int | float:
  64.         if self.cart:
  65.             return sum(item.total() for item in self.cart)
  66.         return 0

  67.     def due(self) -> int | float:
  68.         if self.promotion is None:
  69.             discount = 0
  70.         else:
  71.             discount = self.promotion.discount(self)
  72.         return self.total - discount

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


  76. class FidelityPromo(InterfacePromotion):
  77.     """为积分为1000或以上的顾客提供%5的折扣"""

  78.     def discount(self, order: InterfaceOrder) -> int | float:
  79.         return order.total * .05 if order.customer.fidelity >= 1000 else 0


  80. class BulkItemPromo(InterfacePromotion):
  81.     """单个商品为20个以上时提供10%折扣"""

  82.     def discount(self, order: InterfaceOrder) -> int | float:
  83.         discount = 0
  84.         for item in order.cart:
  85.             if item.quantity >= 20:
  86.                 discount += item.total() * .1
  87.         return discount


  88. class LargeOrderPromo(InterfacePromotion):
  89.     """订单中的不同商品达到10个以上时提供7%折扣"""

  90.     def discount(self, order: InterfaceOrder) -> int | float:
  91.         distinct_items = {item.product for item in order.cart}
  92.         if len(distinct_items) >= 10:
  93.             return order.total * .07
  94.         return 0


  95. # ----------业务层,处理具体业务逻辑----------
  96. def business() -> None:
  97.     # 实例化各种促销方案
  98.     fp: InterfacePromotion = FidelityPromo()

  99.     c1: Customer = Customer('张三', 1000)
  100.     goods: list[InterfaceItem] = [
  101.         LineItem('商品A', 20, 126), LineItem('商品B', 11, 51.8), LineItem('商品C', 3, 26)
  102.     ]
  103.     order: Order = Order(c1, *goods, promotion=fp)
  104.     print(order)


  105. if __name__ == '__main__':
  106.     business()
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-26 00:52

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表