python3 中所有类都是继承自 object,包括 type。
type 是所有类对象的模版,比如 isinstance(str,type) 是 True。
自带的魔术成员
-
__dict__
:字典,包含所有成员 -
__doc__
: -
__name__
: -
__module__
:所属模块 -
__bases__
:父类
构造器
-
__new__(cls,[...])
:创建实例,基本不会去修改该它 -
__init__(self,[...])
:初始化 -
__del__(self)
:对象被销毁时,也就是使用 del 关键字或回收时,被调用的方法
__new__(cls,...)
和__init__(self,...)
方法接受到的自定义参数都是一样的,父类中如果自定义了__new__
方法,那它得到的 cls 参数是子类的 class 对象,而不是自己的。
通过classname(args)
的方式创建实例时,args 参数会传到 new 和 init 方法。如果没有自定义的 new 和 init,就会调用 object的 new 和 init,但是 args 参数不会传给 object
注意:通过 super 调用到 object 的 new/init 方法时,除了 cls/self 外,不要传其它参数,object 就接受这一个参数
实例的字符串显示
__str__(self)
: 调用 str() 函数时被调用,当需要用到对象的字符串表示时,一般都是用这个方法,比如 print(obj)。
__repr__(self)
: 调用 repr() 函数时被调用,它返回的字符串原则是让eval(repr(x)) == x
为真,如果无法返回满足这个原则的字符串,那就返回一个用尖括号包围的字符串,用来说明这个实例。
比如:<_io.TextIOWrapper name='file.dat' mode='r' encoding='UTF-8'>
__format__(self, format_spec)
:根据 format_spec 返回一个格式化后的字符串。
"Hello, {0:<10}".format(a)其实就调用了a.__format__("<10")
用来将a格式化。
__index__(self)
:需要根据对象得到数字时,调用该函数,返回一个 int,比如切片的时候l[obj]
,或者 bin()、otc()、hex() 函数
限制实例绑定对象
python 在创建实例后依旧可以动态的绑定对象,可以使用__slots__
属性
__slots__ = ('name', 'age')
,如果有类定义了这种 slots 属性,那它的实例实例只能动态绑定名字为 name/age 的属性/方法了
如果父类中有__slots__
,子类中没有,那子类实例绑定对象没有限制;如果子类也有__slots__
,那父类与子类的__slots__
都会对子类的实例起限制作用
不能通过实例的句柄给类成员赋值
class TestClass():
x=1
def func(self):
x=1 # 错误,无法访问到x
TestClass.x=2 # 修改了类的成员,所有该类的实例中的x属性都是指向这个类成员的
self.x # 顺利访问类成员x
self.x=2 # 在当前实例中又创建了成员x,实例中的x将不再是类中的成员,而是该实例自身的
python 不支持方法的重载
同一个类中的同名成员(不管是方法还是变量),后一个都会覆盖前一个
想想重载不就是为了让多个同名函数看起来像是一个可以接受不同类型与数量的参数的函数吗?Python 有*args
和**kwargs
啊,那还要什么重载啊
当然还是有办法实现函数的重载的
继承与多态
子类可以继承父类的成员。
魔术成员也可以被继承,但是某些魔术成员会被自动覆盖,比如自定义类中不显示定义__doc__
,那它的__doc__
就是None,而不是从 object 中继承__doc__
。
class Parent():
def __init__(self,name):
self.name=name
class Child(Parent):
def __init__(self,name,age):
# 调用父类构造器,因为super()等同与super(__class__,self),所以调用它的方法时不用再传self参数了
super().__init__(name)
self.age=age
MRO(Method Resolution Order)
单继承中方法的调用顺序比较简单,就是在子类中找,子类没有就去父类找;而多继承因为有多个父类,所以先去哪个父类找就成了一个问题。
MRO 就是方法的解析顺序。python3 中这个顺序是从左往右,深度优先,重复类只保留最后一个。
举个例子

上述中的顺序为[D, B, A, object, C, A, object],但是因为重复类只保留最后一个,所以实际顺序为 [D, B, C, A, object]
Python的方法解析顺序(MRO)
class Song(object):
def __init__(self, author):
self._author = author
class Singer(object):
def __init__(self, name):
self._name = name
class Mtv(Song, Singer):
def __init__(self, name, author):
# Song.__init__(self,author)
# Singer.__init__(self,name)
# 下面这种做法等同与上面注释中的做法
super().__init__(author)
super(Song, self).__init__(name)
多继承如果多个父类中有相同的方法,不会报错,而是在调用是只调用先找到的那个方法。
super() 在干什么
super() 函数接收两个参数,第二个参数是一个实例或类,super 根据这个实例或类生成它的 MRO 列表,第一个参数是一个类,用这个类来定位 MRO 列表中的起点。super 从这个起点的后一个元素中开始查找,得到查找到的第一个方法。
super() 等同于super(__class__, <first argument>)
,__class__
是当前类,所以不带参数的 super 是从 MRO 列表的第二个元素开始查找。
类型判断与转换
isinstance(obj,cls):判断对象是否是某一个类的实例,子类的实例也被认为是父类的
type(obj)==cls:判断对象是否是这个类的实例,不考虑继承关系
python 没有强制类型转换!!!
想想我一般在 java 中用强制类型转换做什么?把一个父类的对象转换成子类的对象,比如 android 的 findviewbyid 得到的都是 view,一定要强制类型转换。
但是 python 的变量没类型,实例才有类型,所以不会出现父类的引用指向子类的实例,也就不用强制类型转换
其他魔术方法
魔术方法是由前后双下划线包起来的类的成员方法,能够赋予这个类的实例魔法的这么一类方法
比较操作符
__cmp__(self, other)
: self与other相等,则返回0,小于则返回负整数,大于则返回正整数
该方法在 python3 中被取消
-
__eq__(self, other)->bool
: 定义等于操作符(==)的行为。 -
__ne__(self, other)->bool
: 定义不等于操作符(!=)的行为。 -
__lt__(self, other)->bool
: 定义小于操作符(<)的行为。 -
__gt__(self, other)->bool
: 定义大于操作符(>)的行为。 -
__le__(self, other)->bool
: 定义小于等于操作符(<)的行为。 -
__ge__(self, other)->bool
: 定义大于等于操作符(>)的行为。
访问控制
下面这些方法可以改变实例中的成员(属性或函数)被访问时的行为
-
__getattribute__(self, name)
: 成员被访问时调用,成员不存在则抛出异常。
如果重写该方法,使得成员不存在也不抛出异常,那当属性不存在时,将当用下面这个方法。
不要试着去实现这个方法,这种做法很少见的。 -
__getattr__(self, name)
: 访问不存在的成员时,被调用 -
__setattr__(self, name, value)
: 成员被设置时调用 -
__delattr__(self, name)
: 成员被删除时调用
__setattr__
和__delattr__
要注意避免无限递归的问题
# 无限递归
def __setattr__(self, name, value):
self.name = value
# 应该改为
def __setattr__(self, name, value):
self.__dict__[name] = value
网友评论