《零基础入门学习Python》第039讲:类和对象:拾遗

本文介绍了Python中的组合概念,作为替代继承的方案,通过实例展示了如何使用组合将不同类的对象整合到一个新类中。同时,解释了类对象和实例对象的区别,强调了类属性和实例属性的差异以及它们的绑定机制。此外,文章还讨论了对象属性与方法同名时可能出现的问题,并提供了解决方案。
摘要由CSDN通过智能技术生成

0. 请写下这一节课你学习到的内容:格式不限,回忆并复述是加强记忆的好方式!

(一)组合

上节课我们学习了继承和多继承,但是我们有时候发现,有些情况你用继承也不合适,用多继承也不是,例如:现在现在要求定义一个类,叫水池,水池里要有乌龟和鱼。那大家就很苦恼了,用多继承就显得很奇葩了,因为如果把水池继承乌龟和鱼,那显然就是不同物种。那怎样才能把它们组成一个和谐的类呢,这就是我们今天首先要讲的内容:组合。

组合的用法很简单,举例说明:

 
  1. class Turtle:

  2. def __init__(self, x):

  3. self.num = x

  4. class Fish:

  5. def __init__(self, x):

  6. self.num = x

  7. class Pool:

  8. def __init__(self, x, y):

  9. self.turtle = Turtle(x)

  10. self.fish = Fish(y)

  11. def print_num(self):

  12. print("水池里有乌龟 %d 只,小鱼 %d 条!" %(self.turtle.num, self.fish.num))

  13. >>> pool = Pool(1, 10)

  14. >>> pool.print_num()

  15. 水池里有乌龟 1 只,小鱼 10 条!

所谓的组合,就是把类的实例化放到新类里面,那么它就把旧类给组合进去了,不用使用继承了,没有什么风险了。组合一般来说就是把几个没有继承关系,没有直线关系的几个类放在一起,就是组合。要实现纵向关系之间的类,就使用继承。

Python 的特性还支持另外一种很流行的编程模式,叫做 Mix-in,叫做混入的意思,有兴趣的可以参见-> Python Mixin 编程机制

(二)类、类对象和实例对象

我们一开始就说了,类、类对象和实例对象是三个不同的物种。先看代码:

 
  1. >>> class C:

  2. count = 0

  3. >>> a = C()

  4. >>> b = C()

  5. >>> c = C()

  6. >>> a.count

  7. 0

  8. >>> b.count

  9. 0

  10. >>> c.count

  11. 0

  12. >>> c.count += 10

  13. >>> c.count

  14. 10

  15. >>> a.count

  16. 0

  17. >>> b.count

  18. 0

  19. >>> C.count

  20. 0

  21. >>> C.count += 100

  22. >>> C.count

  23. 100

  24. >>> a.count

  25. 100

  26. >>> b.count

  27. 100

  28. >>> c.count

  29. 10

我们这里有一个 C 类,只有一个属性 count ,初始化为0。实例化一个 a,一个 b,一个 c,显然 a.count = 0,b.count = 0,c.count = 0。如果对 c.count += 10,现在 c.count = 10,但是 a.count = 0,b.count = 0。因为 C 是一个类,在写完 C 之后就变成了一个类对象,因为Python无处不对象,所有的东西都是对象,方法也是对象,所以我们这里 C.count = 0 也是等于 0 。此时我们对这个类对象加等于100 , C.count += 100,此时 a.count = 100,b.count = 100,但是  c.count = 10。为什么会这样呢?

其实是因为 c.count += 10 这里 c.count 被赋值的时候,我们是对实例化对象 c 的属性进行赋值,相当于我们生成了一个 count 来覆盖类对象的 count,如图:

类定义到类对象,还有实例对象a,b,c,需要注意的是,类中定义的属性都是静态属性,就像 C 里面的count,类属性和类对象是相互绑定的,并不会依赖于下面的实例对象,所以当 c.count += 10 的时候,并不会影响到 C,只是改变了 c 自身,因为在 c.count += 10 的时候,是实例对象 c 多了一个count 的属性,也就是实例属性,它把类属性给覆盖了。这在以后还会继续讲解,在此之前,我们先谈一下:如果属性的名字和方法相同时,属性会把方法覆盖掉。举例说明:

 
  1. >>> class C:

  2. def x(self):

  3. print("X-man")

  4. >>> c = C()

  5. >>> c.x()

  6. X-man

  7. >>> c.x = 1

  8. >>> c.x

  9. 1

  10. >>> c.x()

  11. Traceback (most recent call last):

  12. File "<pyshell#48>", line 1, in <module>

  13. c.x()

  14. TypeError: 'int' object is not callable

c.x = 1 是实例化后的 c ,创建了一个 x 的属性,这时如果要调用 它的函数 x() 就会报错,出错信息说:整型是不能被调用的。

这就是初学者容易发生的一个问题,如果属性的名字和方法名相同,属性会覆盖方法。为了避免名字上的冲突,大家应该遵守一些约定俗成的规矩:

  • 不要试图在一个类里边定义出所有能想到的特征和方法,应该使用继承和组合机制来进行扩展。
  • 用不同词性命名,如属性名用名词,方法名用动词。

(三)到底什么是绑定?

Python 严格要求方法需要有实例才能被调用,这种限制其实就是Python 所谓的绑定概念。


测试题

0. 什么是组合(组成)? 

答:Python 继承机制很有用,但容易把代码复杂化以及依赖隐含继承。因此,经常的时候,我们可以使用组合来代替。在Python里组合其实很简单,直接在类定义中把需要的类放进去实例化就可以了。

例子:

 
  1. // 乌龟类

  2. class Turtle:

  3. def __init__(self, x):

  4. self.num = x

  5. // 鱼类

  6. class Fish:

  7. def __init__(self, x):

  8. self.num = x

  9. // 水池类

  10. class Pool:

  11. def __init__(self, x, y):

  12. self.turtle = Turtle(x) // 组合乌龟类进来

  13. self.fish = Fish(y) // 组合鱼类进来

  14. def print_num(self):

  15. print("水池里总共有乌龟 %d 只,小鱼 %d 条!" % (self.turtle.num, self.fish.num))

  16. >>> pool = Pool(1, 10)

  17. >>> pool.print_num()

1. 什么时候用组合,什么时候用继承?

答:根据实际应用场景确定。简单的说,组合用于“有一个”的场景中,继承用于“是一个”的场景中。例如,水池里有一个乌龟,天上有一个鸟,地上有一个小甲鱼,这些适合使用组合。青瓜是瓜,女人是人,鲨鱼是鱼,这些就应该使用继承啦。

2. 类对象是在什么时候产生?

答:当你这个类定义完的时候,类定义就变成类对象,可以直接通过“类名.属性”或者“类名.方法名()”引用或使用相关的属性或方法。

3. 如果对象的属性跟方法名字相同,会怎样?

答:如果对象的属性跟方法名相同,属性会覆盖方法。

 
  1. class C:

  2. def x(self):

  3. print('Xman')

  4. >>> c = C()

  5. >>> c.x()

  6. Xman

  7. >>> c.x = 1

  8. >>> c.x

  9. 1

  10. >>> c.x()

  11. Traceback (most recent call last):

  12. File "<pyshell#20>", line 1, in <module>

  13. c.x()

  14. TypeError: 'int' object is not callable

4. 请问以下类定义中哪些是类属性,哪些是实例属性?

 
  1. class C:

  2. num = 0

  3. def __init__(self):

  4. self.x = 4

  5. self.y = 5

  6. C.count = 6

答:num 和 count 是类属性(静态变量),x 和 y 是实例属性。大多数情况下,你应该考虑使用实例属性,而不是类属性(类属性通常仅用来跟踪与类相关的值)。

5. 请问以下代码中,bb 对象为什么调用 printBB() 方法失败?

 
  1. class BB:

  2. def printBB():

  3. print("no zuo no die")

  4. >>> bb = BB()

  5. >>> bb.printBB()

  6. Traceback (most recent call last):

  7. File "<pyshell#8>", line 1, in <module>

  8. bb.printBB()

  9. TypeError: printBB() takes 0 positional arguments but 1 was given

答:因为 Python 严格要求方法需要有实例才能被调用,这种限制其实就是 Python 所谓的绑定概念。所以 Python 会自动把 bb 对象作为第一个参数传入,所以才会出现 TypeError:“需要 0 个参数,但实际传入了 1 个参数“。

正确的做法应该是:

 
  1. class BB:

  2. def printBB(self):

  3. print("no zuo no die")

  4. >>> bb = BB()

  5. >>> bb.printBB()

  6. no zuo no die


动动手

0. 思考这一讲我学习的内容,请动手在一个类中定义一个变量,用于跟踪该类有多少个实例被创建

(当实例化一个对象,这个变量+1,当销毁一个对象,这个变量自动-1)。

 
  1. class C:

  2. count = 0

  3. def __init__(self):

  4. C.count += 1

  5. def __del__(self):

  6. C.count -= 1

  7. >>> a = C()

  8. >>> b = C()

  9. >>> c = C()

  10. >>> C.count

  11. 3

  12. >>> del a

  13. >>> C.count

  14. 2

  15. >>> del b, c

  16. >>> C.count

  17. 0

1. 定义一个栈(Stack)类,用于模拟一种具有后进先出(LIFO)特性的数据结构。

至少需要有以下方法:

方法名含义
isEmpty()判断当前栈是否为空(返回 True 或 False)
push()往栈的顶部压入一个数据项
pop()从栈顶弹出一个数据项(并在栈中删除)
top()显示当前栈顶的一个数据项
bottom()显示当前栈底的一个数据项
 
  1. class Stack:

  2. def __init__(self, start=[]):

  3. self.stack = []

  4. for x in start:

  5. self.push(x)

  6. def isEmpty(self):

  7. return not self.stack

  8. def push(self, obj):

  9. self.stack.append(obj)

  10. def pop(self):

  11. if not self.stack:

  12. print('警告:栈为空!')

  13. else:

  14. return self.stack.pop()

  15. def top(self):

  16. if not self.stack:

  17. print('警告:栈为空!')

  18. else:

  19. return self.stack[-1]

  20. def bottom(self):

  21. if not self.stack:

  22. print('警告:栈为空!')

  23. else:

  24. return self.stack[0]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值