python的类中包含什么_Python中的类(中)

本文详细介绍了Python中的类继承,包括单继承和多继承,以及如何使用super关键字访问父类的方法。同时,讨论了方法解析顺序(MRO)的概念。此外,还探讨了__slots__属性的使用,用于限制类实例的属性,以及其在继承中的行为。通过示例代码,解释了__slots__如何影响子类实例的属性添加和访问。
摘要由CSDN通过智能技术生成

上一篇介绍了Python中类相关的一些基本点,本文看看Python中类的继承和__slots__属性。

继承

在Python中,同时支持单继承与多继承,一般语法如下:

classSubClassName(ParentClass1 [, ParentClass2, ...]):

class_suite

实现继承之后,子类将继承父类的属性,也可以使用内建函数insubclass()来判断一个类是不是另一个类的子孙类:

classParent(object):'''parent class'''numList=[]defnumAdd(self, a, b):return a+bclassChild(Parent):passc=Child()#subclass will inherit attributes from parent class

Child.numList.extend(range(10))printChild.numListprint "2 + 5 =", c.numAdd(2, 5)#built-in function issubclass()

printissubclass(Child, Parent)printissubclass(Child, object)#__bases__ can show all the parent classes

print Child.__bases__

#doc string will not be inherited

print Parent.__doc__

print Child.__doc__

代码的输出为,例子中唯一特别的地方是文档字符串。文档字符串对于类,函数/方法,以及模块来说是唯一的,也就是说__doc__属性是不能从父类中继承来的。

281019365637625.png

继承中的__init__

当在Python中出现继承的情况时,一定要注意初始化函数__init__的行为。

1. 如果子类没有定义自己的初始化函数,父类的初始化函数会被默认调用;但是如果要实例化子类的对象,则只能传入父类的初始化函数对应的参数,否则会出错。

classParent(object):def __init__(self, data):

self.data=dataprint "create an instance of:", self.__class__.__name__

print "data attribute is:", self.dataclassChild(Parent):passc= Child("init Child")printc= Child()

代码的输出为:

281019418446978.png

2. 如果子类定义了自己的初始化函数,而没有显示调用父类的初始化函数,则父类的属性不会被初始化

classParent(object):def __init__(self, data):

self.data=dataprint "create an instance of:", self.__class__.__name__

print "data attribute is:", self.dataclassChild(Parent):def __init__(self):print "call __init__ from Child class"c=Child()print c.data

代码的输出为:

281019453139618.png

3. 如果子类定义了自己的初始化函数,显示调用父类,子类和父类的属性都会被初始化

classParent(object):def __init__(self, data):

self.data=dataprint "create an instance of:", self.__class__.__name__

print "data attribute is:", self.dataclassChild(Parent):def __init__(self):print "call __init__ from Child class"super(Child, self).__init__("data from Child")

c=Child()print c.data

代码的输出为:

281019474382003.png

super

前面一个例子中,已经看到了通过super来调用父类__init__方法的例子,下面看看super的使用。

在子类中,一般会定义与父类相同的属性(数据属性,方法),从而来实现子类特有的行为。也就是说,子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的属性和方法。

classParent(object):

fooValue= "Hi, Parent foo value"

deffoo(self):print "This is foo from Parent"

classChild(Parent):

fooValue= "Hi, Child foo value"

deffoo(self):print "This is foo from Child"c=Child()

c.foo()print Child.fooValue

在这段代码中,子类的属性"fooValue"和"foo"覆盖了父类的属性,所以子类有了自己的行为。

281019519222701.png

但是,有时候可能需要在子类中访问父类的一些属性:

classParent(object):

fooValue= "Hi, Parent foo value"

deffoo(self):print "This is foo from Parent"

classChild(Parent):

fooValue= "Hi, Child foo value"

deffoo(self):print "This is foo from Child"

printParent.fooValue#use Parent class name and self as an argument

Parent.foo(self)

c=Child()

c.foo()

这时候,可以通过父类名直接访问父类的属性,当调用父类的方法是,需要将"self"显示的传递进去的方式。

281019540166626.png

这种方式有一个不好的地方就是,需要经父类名硬编码到子类中,为了解决这个问题,可以使用Python中的super关键字:

classParent(object):

fooValue= "Hi, Parent foo value"

deffoo(self):print "This is foo from Parent"

classChild(Parent):

fooValue= "Hi, Child foo value"

deffoo(self):print "This is foo from Child"

#use super to access Parent attribute

printsuper(Child, self).fooValue

super(Child, self).foo()

c=Child()

c.foo()

对于"super(Child, self).foo()"可以理解为,首先找到Child的父类Parent,然后调用父类的foo方法,同时将Child的实例self传递给foo方法。

但是,如果当一个子类有多个父类的时候,super会如何工作呢?这是就需要看看MRO的概念了。

MRO

假设现在有一个如下的继承结构,首先通过类名显示调用的方式来调用父类的初始化函数:

classA(object):def __init__(self):print "->Enter A"

print "<-Leave A"

classB(A):def __init__(self):print "-->Enter B"A.__init__(self)print "<--Leave B"

classC(A):def __init__(self):print "--->Enter C"A.__init__(self)print "<---Leave C"

classD(B, C):def __init__(self):print "---->Enter D"B.__init__(self)

C.__init__(self)print "<----Leave D"d= D()

从输出中可以看到,类A的初始化函数被调用了两次,这不是我们想要的结果:

281019559065783.png

下面,我们通过super方式来调用父类的初始化函数:

classA(object):def __init__(self):print "->Enter A"

print "<-Leave A"

classB(A):def __init__(self):print "-->Enter B"super(B, self).__init__()print "<--Leave B"

classC(A):def __init__(self):print "--->Enter C"super(C, self).__init__()print "<---Leave C"

classD(B, C):def __init__(self):print "---->Enter D"super(D, self).__init__()print "<----Leave D"d= D()

通过输出可以看到,当使用super后,A的初始化函数只能调用了一次:

281019575162024.png

为什么super会有这种效果?下面就开始看看Python中的方法解析顺序MRO(Method Resolution Order)。

Python的类有一个__mro__属性,这个属性中就保存着方法解析顺序。结合上面的例子来看看类D的__mro__:

>>> print "MRO:", [x.__name__ for x in D.__mro__]

MRO: ['D', 'B', 'C', 'A', 'object']>>>

看到这里,对于上面使用super例子的输出就应该比较清楚了。

Python的多继承类是通过MRO的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数只调用一次(如果每个类都使用super)

混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一个父类函数被调用多次

__slots__

从前面的介绍可以看到,当我们通过一个类创建了实例之后,仍然可以给实例添加属性,但是这些属性只属于这个实例。

有些时候,我们可以需要限制类实例对象的属性,这时就要用到类中的__slots__属性了。__slots__属性对于一个tuple,只有这个tuple中出现的属性可以被类实例使用。

classStudent(object):__slots__ = ("name", "age")def __init__(self, name, age):

self.name=name

self.age=age

s= Student("Wilber", 28)print "%s is %d years old" %(s.name, s.age)

s.score= 96

在这个例子中,当场是给Student的实例s添加一个score属性的时候,就会遇到下面的异常:

281020003287265.png

子类没有__slots__属性

使用__slots__要注意,__slots__定义的属性仅对当前类的实例起作用,对继承的子类实例是不起作用的:

classPerson(object):__slots__ = ("name", "age")pass

classStudent(Person):passs=Student()

s.name, s.age= "Wilber", 28s.score= 100

print "%s is %d years old, score is %d" %(s.name, s.age, s.score)

从代码的输出可以看到,子类Student的实例并不受父类中__slots__属性的限制:

281020026257364.png

子类拥有__slots__属性

但是,如果子类本身也有__slots__属性,子类的属性就是自身的__slots__加上父类的__slots__。

classPerson(object):__slots__ = ("name", "age")pass

classStudent(Person):__slots__ = ("score", )passs=Student()

s.name, s.age= "Wilber", 28s.score= 100

print "%s is %d years old, score is %d" %(s.name, s.age, s.score)print s.__slots__s.city= "Shanghai"

代码的输出为:

281020048133262.png

所以说,对于__slots__属性:

如果父类包含对__slots__的定义,子类不包含对__slots__的定义,解释器忽略__slots__的作用

如果父类包含对__slots__的定义,子类包含对__slots__的定义,并且无论元组的的元素个数,解释器都会按照父类的__slots__和子类的__slots__的并集来检查

总结

本文介绍了Python中的继承,当使用多继承的时候,可以使用super关键字去访问父类中被子类覆盖的方法;对于方法的调用,需要参照MRO。

另外介绍了Python类的__slots__属性,通过这个属性可以限制类实例的可用属性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值