文章目录
1. 构造方法
__init__方法
在类被实例化时,首先会调用构造方法。所以构造方法是完成类实例化时一些初始化工作的最佳选择。
构造方法名是__init__。
class Person:
def __init__(self,name="Bill"):
print("构造方法已经被调用")
self.name=name
def getname(self):
return self.name
def setname(self,name):
self.name=name
person=Person() #Output:构造方法已经被调用
print(person.getname()) #Output:Bill
person1=Person("Mike") #Output:构造方法已经被调用
print(person1.getname()) #Output:Mike
person1.setname("John")
print(person1.getname()) #Output:John
重写方法
当B类继承A类时,B类会重写A类中的同名方法,对于构造方法也是如此。即在B类继承A类时创建B对象,实际是调用B类中的构造方法,而不是A类中的构造方法。
python语言中,重写方法只看方法名,并不看参数。只有方法名相同,就会覆盖父类的同名方法。
super函数
有时,我们希望能调用超类的一个同名方法,这时便可用super函数来实现。其一个最常见用法就是在子类中调用父类(超类)的初始化方法。(被继承的类一般称为“父类”或“超类”)
super函数返回最近的一个超类对象。super函数可以不带任何参数,也可以带两个参数:第1个参数表示当前类的类型,第2个参数需要传入self。不带参数的super函数返回当前定义的class的超类对象,带参数的super函数返回第1个参数所指定类的超类对象。
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
super().__init__() #调用A的构造方法
print("B")
class C(B):
def __init__(self):
super(B,self).__init__() #调用A的构造方法
super(C,self).__init__() #调用B的构造方法
super().__init__() #同上
n=B() #Output:A B
m=C() #Output:A A B A B
class A:
def __init__(self):
print("A")
class B:
def __init__(self):
print("B")
class C:
def __init__(self):
print("C")
class D(A,B,C): #D类多继承,super函数返回的对象为第1个参数的书写顺序的后一个
def __init__(self):
super(B,self).__init__() #调用C的构造方法
super(A,self).__init__() #调用B的构造方法
super(D,self).__init__() #调用A的构造方法
m=D() #Output:C B A
在 Python 2.7中继承 object 类的是新式类,不继承 object 类的是经典类(旧式类),而在Python3中默认都是继承了object的新式类,所以多继承是按广度优先的顺序,见下图样例。(python2.7中如果是经典类多继承是按深度优先的顺序)
2.特殊成员方法
__new__方法
__ new__() 是一种负责创建类实例的静态方法,它无需使用 staticmethod 装饰器修饰,且该方法会优先 __ init__() 初始化方法被调用。
__new__方法的第一个参数是这个类,而其余的参数会在调用成功后全部传递给__init__方法初始化
自定义序列(__ len__、__ getitem__、__ setitem__、__ delitem__)
除了构造方法__init__,还可以使用如下4个特殊方法定义自己的序列类,就像前面介绍的列表、字典等序列一样,只不过拥有自己特殊的行为。
①__len__(self):返回序列中元素的个数。使用len函数获取序列对象的长度时会调用该方法。
②__getitem__(self,key):返回与所给键对应的值。在使用sequence[key]获取值时会调用该方法。
③__setitem__(self,key,value):设置key对应的值。当使用sequence[key]=value设置序列中键对应的值时调用方法。
④__delitem__(self,key):从序列中删除键为key的key-value对。当使用del关键字删除序列中键为key的key-value对时调用该方法。
#本例定义了一个名为FactorialDict的序列类,该类的功能是计算key对应的value的阶乘
class FactorialDict:
def __init__(self):
self.numDict={} # 创建字典对象
def factorial(self,n):
if n==0 or n==1:
return 1
else:
return n*self.factorial(n-1)
def __getitem__(self,key):
print("__getitem__方法被调用,key={}".format(key))
if key in self.numDict:
return self.factorial(self.numDict[key])
else:
return 0
def __setitem__(self,key,value):
print("__setitem__方法被调用,key={}".format(key))
self.numDict[key]=int(value)
def __delitem__(self,key):
print("__delitem__方法被调用,key={}".format(key))
del self.numDict[key]
def __len__(self):
print("__len__方法被调用")
return len(self.numDict)
d=FactorialDict()
d['4!']=4 #__setitem__方法被调用,key=4!
d['7!']=7 #__setitem__方法被调用,key=7!
print('4!','=',d['4!']) #__getitem__方法被调用,key=4! 4! = 24
del d['7!'] #__delitem__方法被调用,key=7!
print('7!','=',d['7!']) #__getitem__方法被调用,key=7! 7! = 0
print('len','=',len(d)) #__len__方法被调用 len = 1
从内建列表、字符串和字典继承
在实现自定义的序列/映射时,需要实现上面的4个方法,但每次都要实现所有的4个方法太麻烦了。因此python提供了几个内建类(list、dict、str),分别实现列表、字典和字符串的默认操作。
所以现在要实现自己的序列(列表、字典和字符串),大可不必从头实现这4个方法,只需要从这三个类继承,并实现必需的方法即可。
'''
本例CounterList类和CounterDict类只重写了__init__方法和__getitem__方法,分别用来初始化计数器counter和当获取值时计数器加1。
MultiString类扩展了字符串,可以通过构造方法的可变参数指定任意多个字符串类型参数,并将这些参数值首尾相连并以sep参数作分隔符形成一个新的字符串。
'''
#定义一个从list继承的类
class CounterList(list):
#list的构造方法必须指定一个可变参数,用于初始化列表
def __init__(self,*args):
super().__init__(*args)
self.counter=0
#当从列表获取值时,计数器+1
def __getitem__(self,index):
self.counter+=1
#调用超类__getitem__方法获取指定的值,当前方法只负责计数器+1
return super(CounterList,self).__getitem__(index)
c=CounterList(range(10))
print(c) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
c.reverse()
print(c) #[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
del c[2:7]
print(c) #[9, 8, 2, 1, 0]
print(c.counter) # 0
print(c[1]+c[2]) # 10
print(c.counter) # 2
#定义一个从dict继承的类
class CounterDict(dict):
#dict的构造方法必须指定一个可变参数,用于初始化字典
def __init__(self,*args):
super().__init__(*args)
self.counter=0
#当从列表获取值时,计数器+1
def __getitem__(self,key):
self.counter+=1
#调用超类__getitem__方法获取指定的值,当前方法只负责计数器+1
return super(CounterDict,self).__getitem__(key)
d=CounterDict({'name':'Bill'})
print(d) #{'name': 'Bill'}
print(d.get('age'),d.counter) #None 0(get方法并不会调用__getitem__方法,所以计数器不会+1 )
d['age']=30
print(d['age']) # 30
print(d.counter) # 1
#定义一个从str继承的类
class MultiString(str):
#该方法会在__init__方法之前调动,用于验证字符串构造方法的参数,
#该方法的参数要和__init__方法的参数保持一致
def __new__(cls, *args, sep=' '):
s=''
#将可变参数中所有的值连接成一个大字符串,中间用end指定的分隔符分隔
for arg in args:
s+=arg+sep
#最后需要去掉字符串结尾的分隔符,所有先算出最后的分隔符的开始索引
index=-len(sep)
if index==0:
index=len(s)
#返回当前的MultiString对象
return str.__new__(cls,s[:index])
def __init__(self,*args,sep=' '):
pass
cs1=MultiString('a','b','c')
cs2=MultiString('a','b','c',sep=',')
cs3=MultiString('a','b','c',sep='')
print(cs1) #a b c
print(cs2) #a,b,c
print(cs3) #abc
3.属性
通常将类的成员变量称为属性。( Java语言中称为字段(field) )
property函数
在使用对象属性时,按一般用obj.propertyName即可。但如果希望可以监控对propertyName的读写操作,就要使用property函数。
property函数可以与三个方法绑定,该函数会创建一个属性,并通过返回值返回这个属性。
定义: property( fget=None, fset=None, fdel=None, doc=None )
参数:fget – 获取属性值的函数 ,fset – 设置属性值的函数,fdel – 删除属性值函数,doc – 属性描述信息
返回值:返回新式类属性。
(property函数的参数都是可选的。若没有参数,产生的属性既不可读也不可写。若只有一个取值方法,产生的属性是只读的。)
class Test:
def __init__(self):
self._x = None
def get_x(self):
print("I'm the getter method")
return self._x
def set_x(self, value):
print("I'm the setter method")
self._x = value
def del_x(self):
print("I'm the deleter method")
del self._x
x = property(get_x, set_x, del_x, "I'm the 'x' property.")
t = Test()
t.x = '100' # 等同于t.set_x("100")
print(t.x) # 等同于print(t.get_x())
t.x = '90'
print(t.x)
#Output:
I'm the setter method
I'm the getter method
100
I'm the setter method
I'm the getter method
90
监控对象中所有的属性(__ getattr__、__ setattr__、__ delattr__、__ dict__)
如果需要监控的属性很多,则可用下述的三个特殊成员方法,当任何一个属性进行读写和删除操作时,都会调用它们中的一个方法进行处理。
①__getattr__(self,name):用于监控所有属性的读操作,name为监控的属性名。
②__setattr__(self,name,value):用于监控所有属性的写操作,name为监控的属性名,value为设置的属性值。
③__delattr__(self,name):用于监控所有属性的删除操作,name为监控的属性名。
class Rectangle:
def __init__(self):
self.width=0
self.height=0
self.left=0
self.top=0
#对属性执行写操作时调用该方法,当设置size属性和position属性时实际上设置了width、height以及left、top属性的值
def __setattr__(self, name, value):
print("{}被设置,新值为{}".format(name,value))
if name=="size":
self.width,self.height=value
elif name=="position":
self.left,self.top=value
else:
#__dict__是内部维护的一个特殊成员变量,用于保存成员变量的值,所以这条语句必须加上
self.__dict__[name]=value
#对属性执行读操作时调用该方法,当读取size属性和position属性时实际上返回的是width、height以及left、top属性的值
def __getattr__(self, name):
print("{}被获取".format(name))
if name == "size":
return self.width, self.height
elif name == "position":
return self.left, self.top
#当删除属性时调用该方法,当删除size属性和position属性时实际上是将width、height以及left、top属性的值重新设置为0
def __delattr__(self, name):
if name == "size":
self.width, self.height = 0, 0
elif name == "position":
self.left, self.top = 0, 0
>>>r = Rectangle()
width被设置,新值为0
height被设置,新值为0
left被设置,新值为0
top被设置,新值为0
>>>r.size = 300, 500
size被设置,新值为(300, 500)
width被设置,新值为300
height被设置,新值为500
>>>r.position =100, 400
position被设置,新值为(100, 400)
left被设置,新值为100
top被设置,新值为400
>>>print(r.size)
size被获取
(300, 500)
>>>print(r.position)
position被获取
(100, 400)
>>>del r.size,r.position
width被设置,新值为0
height被设置,新值为0
left被设置,新值为0
top被设置,新值为0
关于__setattr__方法中使用的一个__dict__成员变量:
这是系统内置的成员变量,用于保存对象中所有属性的值。如果不在类中定义__setattr__方法,则系统默认在设置属性值时将这些值都保存到__dict__变量中;但是如果定义了__setattr__方法,则要靠自己将这些属性的值保存到字典__dict__中。所以可利用这个特性来控制某个属性可设置值的范围。
class Myclass:
def __setattr__(self, name, value):
if name=='value':
#只允许value的值大于0,否则不设置name指定的属性值
if value>0:
self.__dict__[name]=value
else:
print('{}属性的值必须大于0'.format(name))
else:
self.__dict__[name]=value
c=Myclass()
c.value=20
print(c.value) #20
c.value=-20 #value属性的值必须大于0
print(c.value) #20
c.qwe=-10
print(c.qwe) #-10
4. 实例方法、静态方法和类方法
Python类包含三种方法:实例方法,类方法、静态方法。
实例方法:
第一个参数必须是实例对象,该参数名一般约定为“self”,代表当前类的实例,通过它来传递实例的属性和方法(也可以传类的属性和方法)。调用:只能由实例对象调用。
类方法:
使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,这个参数表示类的元数据(即类本身),通过它来传递类的静态属性和静态方法(不能传实例的属性和方法)。调用:实例对象和类对象都可以调用。
静态方法:
使用装饰器@staticmethod。静态方法是类中的函数,不需要实例,逻辑上属于类,但是和类本身没有关,可理解为静态方法是个独立单纯的函数,仅仅是托管于某个类的名称空间中,便于使用和维护。参数随意,没有“self”和“cls”参数,但是方法体中不能使用实例的任何属性和方法。调用:实例对象和类对象都可以调用。
class Myclass:
name="Bill" #Myclass类的静态属性
def __init__(self):
print("Myclass的构造方法被调用")
self.value=20
@staticmethod
def run():
print(Myclass.name)
@classmethod
def do(cls):
print(cls.name) #使用静态属性
cls.run() #调用静态方法
def dol(self):
print(self.value,self.name)
Myclass.run()
Myclass.do()
m=Myclass()
m.dol()
5.迭代器
自定义可迭代的类( __ iter__方法、__ next__方法)
列表也可进行循环获取所有元素,但列表是一下子将所有数据都转载在内存中,而且是一块连续的内存空间,在数据量较小时实现较容易,而在数据量很大的时候会非常消耗内存资源。
迭代就不同,迭代时读取多少元素,就将多少元素装载到内存中,不读取就不装载。
__ iter__:如果在一个类中定义该方法,那么这个类的实例就是一个迭代器,该方法需要返回一个迭代器,所以返回对象本身即可(即self)。当对象每迭代一次,就会调用迭代器中的另外一个特殊成员方法__next__。
__ next__:该方法返回当前迭代的结果。
#可无限迭代输出直角三角形
class Rriangle:
def __init__(self):
self.n=1
def __next__(self):
result='*'*(2*self.n-1)
self.n+=1
return result
def __iter__(self):#该方法返回一个迭代器
return self
rt=Rriangle()
for e in rt:
if len(e)>10:break;
print(e)
*
***
*****
*******
*********
#可无限迭代斐波那契数列
class Fibonacci:
def __init__(self):
self.a=0
self.b=1
def __next__(self):
result=self.a #self.a就是当前要迭代的值
self.a,self.b=self.b,self.a+self.b
return result #返回当前迭代的值
def __iter__(self):
return self
fibs=Fibonacci()
for i in fibs:
print(i,end=" ")
if i>500:break;
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610
将迭代器转换为列表
迭代器不具备通过索引获取某个元素,进行分片操作这些功能,所以很多时候需要将迭代器转换为列表。
通过list函数可直接将迭代器转换为列表。
很多时候迭代器都是无限迭代,在将迭代器转换为列表时需要限定个范围防止内存溢出。要让迭代器停止迭代,只需要抛出StopIteration移除即可。
#斐波那契数列——迭代器转换为列表
class Fibonacci:
def __init__(self):
self.a=0
self.b=1
def __next__(self):
result=self.a
self.a,self.b=self.b,self.a+self.b
if result>500:raise StopIteration
return result
def __iter__(self):
return self
fibs=Fibonacci()
print(list(fibs))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
6.生成器
迭代器是以类为基础的单值产生器,生成器则是以函数为基础的单值产生器。(迭代器和生成器都只能一个值一个值地产生,每迭代一次得到一个值)
创建生成器(yield语句)
首先需要定义一个函数,在该函数中对某个集合或迭代器进行迭代,然后使用yield语句产生当前要生成的值,这是函数会被冻结,直到调用生成器的代码继续迭代下一个值,生成器才会继续执行。
#定义一个生成器函数
def myGenerator():
num=list(range(1,9)) #[1, 2, 3, 4, 5, 6, 7, 8]
for i in num:
yield i #yield语句会冻结当前函数,并提交当前要生成的值
#对生成器进行迭代
for num in myGenerator():
print(num,end=" ")
1 2 3 4 5 6 7 8
#利用生成器将二维列表转换为一维列表
num=[[1,2,3],[4,3,2],[1,2,4,5,7]]
def enumlist(num): #生成器函数
for i in num:
for j in i:
yield j
for i in enumlist(num): #对生成器函数进行迭代
print(i,end=" ")
#1 2 3 4 3 2 1 2 4 5 7
print()
#将生成器函数转换为列表
numList=list(enumlist(num))
print(numList) #[1, 2, 3, 4, 3, 2, 1, 2, 4, 5, 7]
print(numList[1:4]) #[2, 3, 4]
递归生成器
#利用生成器将多维列表进行一维化处理
def enumlist(num):
try:
#对多维列表进行迭代
for i in num:
#将多维列表的每一个元素传入enumlist函数,如果该元素是一个列表则继续迭代
#否则会抛出TypeError异常,在异常处理代码中直接通过yield语句返回这个普通的元素值
#这个异常也是递归的终止条件
for j in enumlist(i):
yield j
except TypeError:
yield num
num=[4,[1,2,[3,5,6]],[4,3,[1,2,[4,5]],2],[1,2,4,5,7]]
#迭代生成器
for i in enumlist(num):
print(i,end=" ")
4 1 2 3 5 6 4 3 1 2 4 5 2 1 2 4 5 7
上面代码中如果某元素值是字符串类型,那么也会继续进行迭代(因为字符串可以看作是字符的列表)。
如果想要将字符串整体输出,则先要判断当前元素值是否是字符串(判断一个值是否是字符串最简单的方法就是利用try语句),具体实现见下面代码。
#将多维列表进行一维化处理,字符串整体返回
def enumlist(num):
try:
try:num+'' #如果num不是字符串类型会抛出异常
except TypeError:pass #如果num不是字符串类型的值,会继续使用for语句对其迭代
else:raise TypeError #如果num是字符串类型直接抛出异常,在异常处理代码中直接yield语句返回该值
#继续对num进行迭代
for i in num:
for j in enumlist(i):
yield j
except TypeError:
yield num
num=['a',['b',['c'],20,123,['hello world',['bill']]]]
for i in enumlist(num):
print(i,end=" ")
a b c 20 123 hello world bill