【Python学习笔记】八、方法、属性和迭代器

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 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值