知识点
- 在前面有乌龟类和鱼类,现在要求定义一个类,叫水池,水池里要有乌龟和鱼,但是仔细想想我们如果用继承的话就把鱼和乌龟当成一个物种,这样就显得太乱了。那要怎么才能将它们组合成一个和谐的类喃,下面我们就要介绍组合
组合其实很简单,下面我们直接上代码:
class Turtle:
def __init__(self,x):
self.num = x
class Fish:
def __init__(self,x):
self.num = x
class Pool:
def __init__(self,x,y):
self.turtle = Turtle(x)#把需要的类直接实例化放进去,就是组合了(将没有直接关联的类直接组合直接在一起)
self.fish = Fish(y)
def print_num(self):
print('乌龟%d,小鱼%d'%(self.turtle.num,self.fish.num))
=======================================================================================
>>> pool = Pool(1,10)
>>> pool.print_num()
乌龟1,小鱼10
-
类、类对象、类事例对象
>>> class C:
count = 0
>>> a=C()
>>> b=C()
>>> c=C()
>>> a.count
0
>>> b.count
0
>>> c.count += 10#相当于生成了以count覆盖了类对象的count,所以造成了下面c.count不会变
>>> c.count
10
>>> a.count
0
>>> b.count
0
>>> C.count
0
>>> C.count += 100
>>> a.count
100
>>> b.count
100
>>> c.count
10
实例对象的改变不会影响类对象或者其他实例对象。
-
当属性和方法的名称相同
当我们建立一个与方法同名的属性的时候,那个同名的方法却不能再次使用,属性就会覆盖方法
>>> class C:
def x(self):
print("X-man!")
>>> c=C()
>>> c.x()
X-man!
>>> c.x=1#Pyhton中的变量不用定义,所以这样相当于建立一个与方法同名的x
>>> c.x
1
>>> c.x()
Traceback (most recent call last):
File "<pyshell#126>", line 1, in <module>
c.x()
TypeError: 'int' object is not callable
那么为避免名字上的冲突,大家应该遵循一些规则:
- 不要试图在一个类里面定义出所有能想到的特性和方法,应该利用继承和组合机制来进行扩展
- 用不同的词性命,如属性名用名词,方法名用动词
到底什么是绑定?
Python严格要求方法需要有事例才能被调用,这种限制其实就是Python所谓的绑定概念
那部分人会这样想,用类对象直接调用也可以调用类中的方法
>>> class BB:
def printBB():#这里少了一个self
print('no zuo no die')
>>> BB.printBB()
no zuo no die
但是这样我们实例化一个变量去调用这个类的方法,我们是调用不到的
>>> bb = BB()
>>> bb.printBB()
Traceback (most recent call last):
File "<pyshell#133>", line 1, in <module>
bb.printBB()
TypeError: printBB() takes 0 positional arguments but 1 was given
那我们再讲一个例子
>>> class cc:
def setXY(self,x,y):
self.x = x
self.y = y
def printXY(self):
print(self.x,self.y)
>>> dd = cc()
>>> dd.__dict__#查看他的属性
{}
>>> cc.__dict__#查看类对象的属性
mappingproxy({'__module__': '__main__', 'setXY': <function cc.setXY at 0x03FCAE40>, 'printXY': <function cc.printXY at 0x03FCAE88>, '__dict__': <attribute '__dict__' of 'cc' objects>, '__weakref__': <attribute '__weakref__' of 'cc' objects>, '__doc__': None})
接下来我们来调用dd.setXY()
>>> dd.setXY(4,5)
>>> dd.__dict__
{'x': 4, 'y': 5}
现在发现dd的属性发生了改变,你也会发现这个属性只属于dd
>>> cc.__dict__
mappingproxy({'__module__': '__main__', 'setXY': <function cc.setXY at 0x03FCAE40>, 'printXY': <function cc.printXY at 0x03FCAE88>, '__dict__': <attribute '__dict__' of 'cc' objects>, '__weakref__': <attribute '__weakref__' of 'cc' objects>, '__doc__': None})
为什么会是这样喃,这跟绑定完全分不开。当dd调用setXY的时候,他是传递的dd.setXY(dd,4,5),所以你在赋值的时候self就是dd,相当于dd.x=x(4),dd.y=y(5),这个数据存放的位置是实例对象中一个位置,所以这个属性只属于实例对象。
现在我们将类对象删除
>>> del cc
>>> ee = cc()
Traceback (most recent call last):
File "<pyshell#155>", line 1, in <module>
ee = cc()
NameError: name 'cc' is not defined
>>> dd.setXY(4,5)
>>> dd
<__main__.cc object at 0x03FCF2D0>
>>> dd.printXY()
4 5
我们将类对象删除之后,dd仍然可以使用原cc中的方法,这是因为类对象中定义的属性和方法是静态的,所以尽管类对象被删除之后,他们依然存在。
知识点
0.什么是组合(组成)?
答:Python基础机制很有用,但容易吧代码复杂化以及依赖隐含继承。因此,经常的时候,我们可以使用组合来代替。在Python李组合其实很简单,直接再类定义中吧需要的类放进去实例化就可以了。(前面有相关的例子)
1.什么时候用组合,什么时候用继承?
答:根据实际应用场景确定。简单来说,组合用于“有一个”的场景(关联性较弱的时候),继承用于“是一个”的场景(关联性比较强)
2.类对象是在什么时候产生的?
答:当你这个类定义完的时候,类定义就变成对象,可以直接通过‘类名.属性’或者‘类名.方法名()’引用或使用相关的属性和方法。
3.如果对象的属性跟方法同名,会怎样?
答:如果对象的属性跟方法名相同,属性会覆盖方法。
4.请问以下类定义中那些是类属性,那些是实例属性?
答:num和count是类属性(静态变量),x和y是实例属性。大多数情况下,你应该考虑使用实例属性,而不是类属性(类属性通常仅用来跟踪与类相关的值)
5.请问下面的代码,bb对象为什么要调用printBB()方法失败?
答;因为Python严格要求方法需要有实例才能被调用,这种限制其实就是Python所谓绑定的概念。所以Python会自动把bb对象作为第一个参数传入,所以才会出现错误。
动动手
0.思考这一讲我学习的内容,请动手在一个类中定义一个变量,用于跟踪该类有多少个实例被创建(当实例化一个对象,这个对象变量+1,当删除一个对象,这个变量自动-1)
>>> class C:
count = 0
def __init__(self):
C.count += 1
>>> class C:
count = 0
def __init__(self):
C.count += 1
def __del__(self):
C.count -= 1
>>> a=C()
>>> b=C()
>>> c=C()
>>> C.count
3
>>> del a
>>> C.count
2
1.定义一个栈类,用于模拟一种具有后进先出特性的数据结构,至少需要有以下的方法
class Stack:
def __init__(self,start=[]):
self.stack = []
for x in start:
self.push(x)
def isEmpty(self):
return not self.stack
def push(self,obj):
self.stack.append(obj)
def pop(self):
if not self.stack:
print('警告:栈为空!')
else:
return self.stack.pop()
def top(self):
if not self.stack:
print('警告:栈为空!')
else:
return self.stack[-1]
def bottom(self):
if not self.stack:
print('警告:栈为空!')
else:
return self.stack[0]
======================== RESTART: D:/python/Stack.py ========================
>>> bb = Stack('asdasdasdafadfas')
>>> bb.pop()
's'