上一讲,我们讲了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本身所特有的信息。