用户:Xyy23330121/Python/类的私有量与类的继承
为篇幅起见,另起一个页面讲这些内容。
类的一个重要方法是继承。我们可以这样定义一个类型的子类型:
class C: pass
class D(C): pass
此时, D
会继承来自 C
的一切属性或方法。这种单重继承的方式和定义函数时、变量的作用域很相似的。我们可以简单理解为:D
是 C
的子类型时,D
的作用域也是 C
作用域的下级作用域。
class C: pass
class D: pass
class E(C,D): pass
通过以上方法,可以让 E
同时作为 C
和 D
的子类型。
子类继承父类型方法的顺序是按照其mro()
方法所示的顺序的,读者可以通过输出 ClassName.mro()
的结果,得知 Python 会按照什么顺序去寻找方法或属性。
以上述例子为例,E.mro()
的输出为:
<class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>
即,它会先从类型 E 中查找方法。找不到时,再查找类型 C。若还找不到,则查找类型 D。最后查找类型 object。
如果这个顺序无法生成,则新的子类无法正常创建,并且会报错。比如:
class RootA: pass
class RootB: pass
class C(RootA, RootB): pass
class D(RootB, RootA): pass
class E(C,D): pass #报错:TypeError: Cannot create a consistent method resolution order (MRO) for bases RootA, RootB
读者在完全了解这些顺序之前,应当不要随意使用多重继承。具体顺序,参见此文档。
大多数 Python 代码遵循一个规律:以下划线开头的名称应该被当作是私有部分,不应从外面随意更改——如果进行更改,可能会破坏一部分功能。这个思想在“类”上的体现是一个“名称改写”的功能。
class C:
__a = 0
def prt(self):
print(self.__a)
#print(C.__a) #报错:AttributeError: type object 'C' has no attribute '__a'
print(C._C__a)#输出:0
C.prt(C) #输出:0
具体来讲,在类型 ClassName
中使用以两个下划线开头的函数或属性时,Python 会自动把属性名中的 __
改为 _ClassName__
。而在类型外时,则不会进行这样的改写。
在 Python 中,任何变量的数值都是可以改写的。
class property(fget=None, fset=None, fdel=None, doc=None)
property 是一个特殊的、用作类型属性的类型。对 property 实例进行的读取、赋值和更改等操作,都会被改为执行 fget、fset 和 fdel 函数。比如以下示例:
class C:
def __init__(self): self.__x = 0
def getx(self): return self.__x
def setx(self, value): self.__x = value
def delx(self): del self.__x
x = property(getx, setx, delx, "I'm the 'x' property.")
c = C()
print(c.x) #输出:0
c.x = 1
print(c._C__x) #输出:1
del c.x
try: print(c._C__x)
except: print("c._C__x deleted")
#输出:c._C__x deleted
property 支持作为 装饰器 来使用。以下代码得到的结果和上面是相同的:
class C:
def __init__(self): self.__x = 0
@property
def x(self): return self.__x
@x.setter
def x(self, value): self.__x = value
@x.deleter
def x(self): del self.__x
我们可以利用 property,来达成类似“只读属性”的效果,比如:
class AlwaysZero:
@property
def x(self): return 0
@x.setter
def x(self, value): raise AttributeError("readonly attribute")
@x.deleter
def x(self): raise AttributeError("readonly attribute")
a = AlwaysZero
print(a.x) #输出:0
a.x = 1 #报错:AttributeError: readonly attribute
这样,就实现了只读属性的效果。以上代码还可以简化为:
class AlwaysZero:
@property
def x(self): return 0
a = AlwaysZero()
print(a.x) #输出:0
a.x = 1 #报错:AttributeError: property 'x' of 'AlwaysZero' object has no setter
classinfo
应输入类型、多个类型组成的元组或联合类型。
如果 object
是 classinfo
中某个类型的实例,或某个类型的、子类型的实例。则返回 True
,反之,返回 False
。
同 isinstance
,classinfo
应输入类型、多个类型组成的元组或联合类型。
如果 class
是 classinfo
中某个类型的子类型,则返回 True
,反之,返回 False
。
抽象基类是用于判断某个自定义类是否包含对应方法的。比如 collections.abc
模块中的 collection.abc.Sized
。当一个类,其实例具有 __len__
方法时,该类就会被视为是 collection.abc.Sized
的一个子类。这和创建该类时,是否显式地说明该类继承于 collection.abc.Sized
无关。
这个子类关系,是可以通过上述 isinstance
和 issubclass
函数检测出来的。这在一些情况下很有用。