前言
上篇文章python学习——【第九弹】中我们学习了python面向对象的三大特征:封装,继承,多态。这篇文章也是有关类的一些特殊的使用方法和属性。
特殊方法
Python 类中,凡是以双下划线 "__" 开头和结尾命名的成员(属性和方法),都被称为类的特殊成员(特殊属性和特殊方法)
这篇文章主要学习python中的四种特殊方法;
__len()__ 通过重写该方法 让内置函数len()的参数可以是自定义类型
__add()__ 通过重写该方法 可以使自定义对象具有 + 的功能能
__new()__ 用于创建对象
__init()__ 对创建的对象进行初始化
内置方法和自定义方法
在python中分为内置方法和自定义方法,python内置的方法有很多,有些内置方法在object类中已经定义,子类可以拿来直接使用,也可以重写;
但是有些内置方法object类中没有,比如 __len__ 方法(而len()方法会调用对应类中的 __len__ 方法),需要根据需求来进行定义,这样的就是自定义方法。
方法__len__
Python中 len() 方法返回对象(字符、列表、元组等)长度或项目个数。这个我们并不陌生:
lst=[11,22,33,44]
strs="hello wolrd"
print(len(lst)) #4
print(len(strs)) #11
初学__len__方法我们可能会觉得这个方法有些多此一举,因为我们经常直接使用len()方法就可以直接计算出一组数据的长度。但是看过之前关于python中类的定义及使用(python学习——【第八弹】)就会明白,len()方法其实是调用对应类中的__len__方法,只不过python中已经封装好了这个方法,因此我们可以直接拿来用。如果我们不在一个类中定义出需要的方法,那么直接利用这个方法是无法运行的,以至于报错。
那么接下来就看一下__len__方法的使用。
class Count(object):
def __init__(self,*args): # *args为可变参数的长度
self.values=[i for i in args] #利用for循环将数据传递给列表
self.count={}.fromkeys(range(len(self.values)),0)
def __len__(self):
return len(self.values)
def __get_item__(self,key):
self.count[key]+=1
return self.values[key]
a1=Count(11,22,33,44,55)
print(a1.values) #[11, 22, 33, 44, 55]
print(len(a1)) #5
print(Count.__len__(a1)) #5
我们在Count类中定义了__len__函数 此时调用该方法返回数据的长度;但是如果我们不在类中定义__len__函数呢,此时还可以用len()方法计算出数据的长度吗?
我们可以发现,程序开始报错。
从上面这个例子也能看出,我们在编写程序时直接用到的很多方法其实都是定义在类中的函数,就比如下面这个__add__方法。
方法__add__
可以通过重写__add__方法 使自定义对象具有 + 的功能:
# 创建一个类,如何编写特殊方法使得两个该类的实例对象进行”加法“运算
class People:
def __init__(self,name):
self.name=name
# 编写一个特殊方法__add__() 进行实例对象之间的相加
def __add__(self,other):
return self.name+other.name
# 编写特殊方法 __len__() 计算字符串的长度
def __len__(self):
return len(self.name)
peo1=People('张三')
peo2=People('李四')
print(peo1.__add__(peo2))
# 张三李四
同样的,如果我们不在类内定义__add__函数,那么还能在类外使用__add__方法吗?
很显然,也是直接报错的。
下面是对python中常用的已被封装好了的方法,这些方法是可以直接拿来用的:
另外,我们也可以借助help()方法来查看某个类所定义好的方法和属性有哪些:
方法__new__
__new__方法用于创建对象。
实例化对象是谁取决于__new__方法,__new__返回什么就是什么(可以在一个类中重写父类object的__new__方法)。
实例化过程中会自动调用__new__方法:
class Animal(object):
# 如果不重写,__new__默认结构如下
def __new__(cls, *agrs, **kargs):
print('我是__new__被调用')
def show():
print('我是实例化方法show(),我被调用')
tigger = Animal() #我是__new__被调用 实例化过程中会自动调用__new__()方法
如果是和本例一样在Animal类中没有重写这个方法(即没有在该类中定义__new__方法), 在该类实例化的时候是会默认调用父类中的__new__方法 ; 这个方法返回值为Animal类的实例(self),并提供给实例方法show()的第一个参数(即默认参数self),我们不妨查看一下输出对象的id值:
class Animal(object):
name='动物'
def __new__(cls,*args,**kwargs):
res=object.__new__(cls,*args,**kwargs)
print(res) #调用Animal的父类object类的__new__()方法,并打印出其返回值 <__main__.Animal object at 0x7f5ed9ddbb50>
return res
def show(self):
print(self)
return self.name #通过在Animal类中进行__new__方法的重写,我们得到调用父类中__new__方法的返回值,该返回值将提供给实例方法show()作为show()的第一个默认参数self <__main__.Animal object at 0x7f5ed9ddbb50>
print('实例对象调用show()方法:',dog.show()) #实例对象调用show()方法: 动物
通过这个例子我们能发现在类的实例化过程中 Animal类继承了object类 ; 同时也继承了object类的__new__()方法;
方法__init__
__init__方法为初始化方法,为类的实例提供一些属性和完成一些动作。
实际上 __new__是真正的实例化方法 ;为类提供外壳制造出实例框架; 然后调用框架内的__init__方法进行操作。
我们可以将类理解为一个厨师, 将__new__()理解为前期的原材料购买环节 , 而__init__()方法可以在原基础上为饭菜添加佐料使其更美味。
class Animal(object):
def __new__(cls,*agrs,**kwargs):
res=object.__new__(cls,*agrs,**kwargs)
print(res) #<__main__.Animal object at 0x7f34ceb76a50>
return res
def __init__(self,name='小动物'):
self.name=name
def show(self):
print(self) #<__main__.Animal object at 0x7f34ceb76a50>
return self.name
dog=Animal()
print(dog.show()) #小动物
最后我们将这两个方法放到一起,看一下程序是如何运行的:
class Person(object):
# 创建对象 __new__()方法的重载
def __new__(cls, *agrs, **kargs):
print('__new__被调用执行,cls的id为{0}'.format(id(cls))) #__new__被调用执行,cls的id为93839126143888
obj = super().__new__(cls) #调用的是Person父类object类的__new__()方法
print('创建的对象的id是:{0}'.format(id(obj))) #创建的对象的id是:140107158284816
return obj
# 对创建的对象进行初始化
def __init__(self, name, age):
print('__init__被调用执行,self的id值是{0}'.format(id(self))) #__init__被调用执行,self的id值是140107158284816
self.name = name
self.age = age
print('object这个类对象的id为:{0}'.format(id(object))) #object这个类对象的id为:140107168097920
print('Person这个类对象的id为:{0}'.format(id(Person))) #Person这个类对象的id为:93839126143888
p1 = Person('张三', 20)
print('p1这个Person类的实例对象的id:{0}'.format(id(p1))) #p1这个Person类的实例对象的id:140107158284816
分析:
首先分别打印出person类和其父类(object)的id地址,类外创建person类的实例对象p1,当p1开始调用类person时, 首先会调用的方法是__new__方法 ; 执行person类的__new__ 方法时 , 先打印出该类的cls参数 , 接着打印出调用的object的__new__方法的返回值, 该返回值会给接下来person类的实例方法提供第一个参数(即self参数) ; 然后开始执行__init__方法进行对创建对象的初始化, __init__方法的第一个参数是由调用的object的__new__方法的返回值提供的 , 因此当我们打印self的id地址时返回的依然是实例对象的id地址 ; 最后打印输出实例对象的id地址。
总结:
__new__至少要有一个参数cls ,代表要实例化的类 ; cls参数在实例化的时候由python解释器自动提供 ,其他的参数直接传递给__init__方法。
__new__决定是否要使用__init__方法, 因为__new__可以调用其他类的构造方法或者是直接返回别的实例对象来作为本类的实例,如果__new__没有返回实例对象, 那么__init__就不会被调用。
但是在__new__方法中不能调用自己的__new__方法 , 即 cls.__new__(cls) 这样做会超过最大的递归深度,报错Recursionerror:maximum recursion depth exceeded
特殊属性
Python中对象包括很多双下划线开始和结束的属性,这些特殊属性,有特殊用法。
# 特殊属性 __dict__ 获得类对象或实例对象所绑定的所有属性和方法的字典
class A(object):
pass
class B(object):
pass
class C(A,B):
def __init__(self,name,age):
self.name=name
self.age=age
# 创建C类的对象
one=C('tom',10)
print('---------对象的属性值----------\n')
print(one.__dict__)#实例对象的属性字典 {'name': 'tom', 'age': 10}
print('\n----------类的属性和方法----------\n')
print(C.__dict__) #类的属性和方法 {'__module__': '__main__', '__init__': <function C.__init__ at 0x7f94
print('\n-----------对象所属类--------\n')
print(one.__class__) #<class '__main__.C'>
print('\n-----------输出类的所有父类------------\n')
print(C.__bases__) #(<class '__main__.A'>, <class '__main__.B'>)
print('\n------------只输出离该类最近的父类---------\n')
print(C.__base__) #<class '__main__.A'>
print('\n--------------输出类的层次结构-------\n')
print(C.__mro__) #(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
print(A.__subclasses__()) #[<class '__main__.C'>] 输出了A的子类
每篇一语
知道的越多,知道的越少!
如有不足,感谢指正!