上一讲,我们讲了python中 面向对象的相关知识。通过前面的学习,大家应该对python的面向对象有了一个大体的认识了,从这一讲开始,我们逐层什么,继续将面向对象中的一些组件都理清理清。
多继承:
什么是多继承?直接看代码吧。
class A:
def show(self):
print('AAAA')
class B:
def fun(self):
print('BBB')
class C(B,A):
pass
x = C()
x.show()
x.fun()
这就是一个多继承的例子。上面例子中,有3个class,一个class叫做A,一个class叫做B。
一个被叫做C的class继承了class A和B。
那么C就同时具有了A和B的方法,然后通过class C再去实例化得到一个实例化对象x的过程,就叫做是多继承了。且这个x同时具有了show()和fun()的方法。
那有的读者可能会问,如果class A和class B有同时具有相同的方法该怎么办?
比如说,
class A:
def show(self):
print('AAAA')
class B:
def show(self):
print('BBB')
class C(B,A):
pass
x = C()
x.show()
此时class A和class B都具有了方法show(),那么最终x.show()会输出什么结果呢?
最终输出的就是'BBB'。记住,在定义class C的时候,谁先被继承,那么方法就用谁的。
这里B是写在A的前面的,所以先继承B。但是,其实在我们coding的过程中,没有人会这样的写的,不同的类,大家肯定会定义不同的方法。
魔法方法:
__init__()
__str__()
__repr__()
__call__()
__init__()这个方法,在前面讲面向对象的时候已经有所介绍,这里就不详细说明了。
直接看代码:
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
c1 = Rectangle(3,4)
这就说明了,此时存在一个实例化对象c1,它的宽是3,高是4
再来看一个比较特殊的例子
x = 'abc\ndef'
当我们在控制台中,输入print(x)和x的时候,两者返回的结果是不一样的。
当我们输入print(x)的时候,会返回 abc \回车 def
当我们输入x的时候,会返回'abc\ndef'。
原因就是在str这个类中,存在两个魔法方法,其中一个是__str__(),另外一个是__repr__()
当我们直接调用x的时候,其实是访问的str类中的__repr__()方法。
当我们直接调用print(x)的时候,其实是访问str类中的__str__()方法。
如果我们把__str__()的方法注释掉,那么在调用print(x)的时候,也是在调用__repr__()的方法。
接下来,再来引入一个非常重要的方法,__call__()
当我们在代码中,直接print(c1())的时候,程序是不允许的,会报出'Rectangle' object is not callable的错误。
但是当我们把代码改成下面的格式:
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
def __str__(self):
return 'width is %d, height is %d'%(self.width,self.height)
def __repr__(self):
return 'area is %s'%self.area()
def area(self):
return self.height*self.width
def __call__(self, *args, **kwargs):
return 'hhh'
c1 = Rectangle(3,4)
print (c1.height,c1.width)
print (c1())
这个时候,程序就运行成功了,而且返回了'hhh'的字符
运算符魔法方法:
__add__()方法:
当我们代码是这样写的时候:
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
def __str__(self):
return 'width is %d, height is %d'%(self.width,self.height)
def __repr__(self):
return 'area is %s'%self.area()
def area(self):
return self.height*self.width
def __call__(self, *args, **kwargs):
return 'hhh'
def __add__(self, other):
return (self.area()+other.area())
c1 = Rectangle(3,4)
c2 = Rectangle(5,6)
print (c1+c2)
调用c1+c2的时候,就会报错
但是当我们加上__add__()方法后,就不会出现问题了。
装饰器:
我们继续使用Rectangle的class来说明什么是装饰器
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
def area(self):
return self.width*self.height
c1 = Rectangle(3,4)
print (c1.area(),c1.height,c1.width)
上面的代码非常好理解,就是通过class实例化了一个叫做c1的对象,然后输出c1的面积,调用的是方法,c1的高和c1的宽,都是class里面的属性。
那我们能不能像调用属性那样去调用方法呢?就是把area后面的括号去掉。
答案是可以的:
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
@property
def area(self):
return self.width*self.height
c1 = Rectangle(3,4)
print (c1.area,c1.height,c1.width)
通过在方法前面加@property的方法,就可以像访问属性一样去访问这个方法了。
再来讲讲下一个方法:
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
@property
def area(self):
return self.width*self.height
def fun():
return 'xxxxx'
c1 = Rectangle(3,4)
print (c1.area,c1.height,c1.width)
print (c1.fun())
通过代码,我们知道,在定义一个class的时候,class里面的方法是必须要有一个参数的,这个参数就是self,用来将该class实例化后生成的对象名赋给self。但是,这里由于fun没有参数,那么调用c1.fun()的时候,肯定就出错了。
那么,在没有参数的时候,我们就可以增加一个叫做@staticmethod的装饰器,成功运行c1.fun()
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
@property
def area(self):
return self.width*self.height
@staticmethod
def fun():
return 'xxxxx'
c1 = Rectangle(3,4)
print (c1.area,c1.height,c1.width)
print (c1.fun())
再讲最后一个装饰器@classmethod,还是先看代码
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
def area(self):
return self.width*self.height
@staticmethod
def fun():
return 'xxxxx'
c1 = Rectangle(3,4)
print (c1.area,c1.height,c1.width)
print (c1.fun())
print (Rectangle.area(),Rectangle.fun())
这样的代码是运行不成功的,原因是Rectangle.area()缺少参数,因为.area()是实例的方法,调用它是需要参数的,而.fun()即可以当做类的方法, 也可以当做实例的方法,怎么调用都是可以的。
再讲一个参数,cls 。我们知道,在定义一个类的时候,类中的方法的一个参数,我们往往都是应用self来当他的参数的。其实也可以用cls。cls表示的就是这个类本身。其实,在使用的时候,可以也不用cls,用a,用b等符号都可以替代cls。下面就从代码来讲讲这个参数吧
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
def area(self):
return self.width*self.height
@staticmethod
def fun():
return 'xxxxx'
@classmethod
def show():
return 'YYYY'
c1 = Rectangle(3,4)
print (c1.area,c1.height,c1.width)
print (c1.fun())
print (Rectangle)
print (Rectangle.show(),Rectangle.fun())
如果我们直接调用Rectangle.show()的话,是会报错的。而如果我们加入了cls这个参数后,程序就可以正常运行了
class Rectangle:
def __init__(self,width,height):
self.width = width
self.height = height
def area(self):
return self.width*self.height
@staticmethod
def fun():
return 'xxxxx'
@classmethod
def show(cls):
print (cls)
return 'YYYY'
c1 = Rectangle(3,4)
print (c1.area,c1.height,c1.width)
print (c1.fun())
print (Rectangle)
print (Rectangle.show(),Rectangle.fun())
我们还可以发现print(Rectangle)和print(cls)打印出来的结果是一样的,就说明了cls参数表示了这个Rectangle本身所特有的信息。