8. python类的特殊方法

特殊方法、属性通常以双下划线__开头,开发者可以直接调用也可以重写它们

一、 常见的特殊方法

1. __repr__()方法

输出对象的“自我描述”信息,返回该对象实现类的“类名+object at+内存地址”

class Item:
    def __init__(self,name,price):
        self.name=name
        self.price=price

#创建一个Item对象并赋值给im变量
im=Item('鼠标',28.9)
#输出im所引用的Item对象
print(im)  #<__main__.Item object at 0x000002580B687438>

#上面实际输出的是Item对象__repr__()方法的返回值
print(im.__repr__())  #<__main__.Item object at 0x000002580B687438>

#输出为 类名+object at+内存地址

可重写__repr__()方法,返回用户实际需要的信息

class Item:
    def __init__(self,name,price):
        self.name=name
        self.price=price
    #重写__repr__()方法
    def __repr__(self):
        return "Item[name="+self.name+",price="+str(self.price)+"]"

#创建一个Item对象并赋值给im变量
im=Item('鼠标',28.9)

print(im)             # Item[name=鼠标,price=28.9]
print(im.__repr__())  # Item[name=鼠标,price=28.9]

2. 析构方法 __del__()

析构方法 __del__()与构造方法__init__()对应,用于销毁python对象。任何python对象被系统回收之前,系统都会自动调用该对象析构方法。

python自动进行对象垃圾回收过程(释放不再需要的对象占用的内存空间)

python采用自动引用计数(ARC)方式回收对象占用的空间,当一个对象的引用计数变为0(即没有被引用了),python就会回收该对象。

 

3. __dir__()方法

用于列出对象内部所有属性及方法名(包括自定义的和内置的)

当执行dir(对象名)时,实际就是将__dir__()方法的返回值进行排序并且包装成列表

class Item:
    def __init__(self,name,price):
        self.name=name
        self.price=price
    #重写__repr__()方法
    def printItem(self):
        print("Item[name="+self.name+",price="+str(self.price)+"]")

#创建一个Item对象并赋值给im变量
im=Item('鼠标',28.9)

print(im.__dir__())
print(dir(im))

4. __dict__属性

用于查看对象内部存储的所有属性名和属性值组成的字典

既可使用__dict__属性查看对象内部状态,也可通过字典语法来访问或修改指定属性的值

class Item:
    def __init__(self,name,price):
        self.name=name
        self.price=price
    #重写__repr__()方法
    def printItem(self):
        print("Item[name="+self.name+",price="+str(self.price)+"]")

#创建一个Item对象并赋值给im变量
im=Item('鼠标',28.9)

print(im.__dict__)  # {'name': '鼠标', 'price': 28.9}
print(im.__dict__['name'])  # 鼠标
im.__dict__['price']=100
print(im.name)  # 鼠标
print(im.price)  # 100

5. 其他常用方法

__getattribute__(self,name):程序访问对象name属性时被自动调用
__getattr__(self,name):程序访问对象name属性且该属性不存在时被自动调用
__setattr__(self,name):程序对对象name属性赋值时被自动调用
__delattr__(self,name):程序删除对象name属性时被自动调用

 

二、 与反射相关的属性和方法

用于在程序运行过程中动态判断是否包含某个属性、方法,查看及动态设置属性值

1. 动态操作属性

  • hasattr(obj,name):检查obj对象是否包含名为name的属性或方法
  • getattr(obj,name[,default]):获取obj对象中名为name的属性的值(不包括方法)
  • setattr(obj,name,value,/):设置obj对象中名为name的属性的值(包括方法),若设置的name不存在,相当于添加新属性
class Item:
    def __init__(self,name,price):
        self.name=name
        self.price=price
    #重写__repr__()方法
    def printItem(self):
        print("Item[name="+self.name+",price="+str(self.price)+"]")

c=Item('鼠标',28.9)

#判断是否包含指定的属性或方法
print(hasattr(c,'name'))  #True
print(hasattr(c,'printItem'))  #True
print(hasattr(c,'info'))  #False

#获取属性值(不能用于方法)
print(getattr(c,'name'))  #鼠标
#设置属性值
setattr(c,'price',100)  #100
print(c.price)

#setattr设置新方法
def newfunc():
    print("一个新方法")
setattr(c,'printItem',newfunc)
c.printItem()  #一个新方法

#setattr还可将方法替换为普通字符串,此时printItem就变为一个属性
setattr(c,'printItem','test strings')
print(c.printItem)  #test strings

2. __call__属性

hasattr方法检查对象是否包含指定属性或方法,而__call__属性就用于判断这个对象是否可调用,进而判断该对象到底是属性还是方法。

实际上方法可执行关键就在于__call()__方法,func(arg1,arg2)实际上只是func.__call__(arg1,arg2)的快捷写法

class Item:
    def __init__(self,name,price):
        self.name=name
        self.price=price

    def printItem(self):
        print("Item[name="+self.name+",price="+str(self.price)+"]")

c=Item('鼠标',28.9)

#判断对象是否可调用
print(hasattr(c.name,'__call__'))  #False
print(hasattr(c.price,'__call__'))  #False
print(hasattr(c.printItem,'__call__'))  #True

三、自定义序列

__len__(self):返回值决定元素个数
__contains__(self, item):判断序列是否包含指定元素
__getitem__(self, key):获取指定索引对应元素,key应为整数或slice,否则该方法会引发KeyError异常
__setitem__(self, key, value):设置指定索引对应元素值为value
__delitem__(self, key):删除指定索引对应的元素

若要实现自定义不可变序列,通常需指定前3个方法;若要实现自定义可变序列,通常需指定上面5个方法

'''
下面非程序实现一个字符串序列,新序列中每个元素长度都为3个字符,类似'aac','fff','kkk'
'''

def check_key(key):
    '''
    检查序列的索引,索引必须为非负整数值。
    若不为整数,引发TypeError异常;若为负数,引发IndexError异常
    '''
    if not isinstance(key,int):  #若不为整数,引发TypeError异常
        raise TypeError('索引值必须为整数')
    if key<0:  #若为负数,引发IndexError异常
        raise IndexError('索引值不能为负数')
    if key>= 26**3:
        raise IndexError('索引值不能超过%d' % 26**3)

class StringSeq:
    def __init__(self):
        #用于存储被修改的数据
        self.__changed={}
        # 用于存储已删除的元素索引
        self.__delete=[]

    #返回序列元素个数
    def __len__(self):
        return 26**3

    #根据索引获取序列中元素
    def __getitem__(self, key):
        check_key(key)
        #若在self.__changed中找到修改后的数据
        if key in self.__changed:
            return self.__changed[key]

        # 若key在self.__delete中,说明元素已被删除
        if key in self.__delete:
            return None

        #否则根据计算规则返回序列元素
        three = key // (26*26)
        two = (key-three*26*26) // 26
        one = key % 26
        return chr(65+three)+chr(65+two)+chr(65+one)

    #根据索引修改序列中元素
    def __setitem__(self, key, value):
        check_key(key)
        #将修改的元素以key-value对的形式保存在__changed中
        self.__changed[key]=value

    #根据索引删除序列中的元素
    def __delitem__(self, key):
        check_key(key)
        #若__delete列表中没有包含被删除的key,则添加被删除的kwy
        if key not in self.__delete:
            self.__delete.append(key)
        #__changed中包含被删除的key,则删除它
        if key in self.__changed:
            del self.__changed[key]

#创建序列
sq=StringSeq()
#获取序列长度
print(len(sq))  #17576
print(sq[26*26])  #BAA

print(sq[1])  #AAB
#修改sq[1]元素
sq[1]='hhh'   
print(sq[1])  #hhh
#删除sq[1]
del sq[1]
print(sq[1])  #None

2. 自定义迭代器

可迭代对象(元组、列表、字典等)都属于迭代器

若需要自行实现迭代器,通常只要实现如下两个方法:

  • __iter__(self):该方法返回一个迭代器,迭代器必须包含一个next()方法,该方法返回迭代器的下一个元素
  • def __reversed__(self):为内建的reversed()反转函数提供支持(可选)
#定义斐波那契数列迭代器
class Fibs:
    def __init__(self,len):
        self.first=0
        self.sec=1
        self.__len=len

    #定义迭代器所需的__next__方法
    def __next__(self):
        if self.__len==0:
            # 若__len__属性为0,结束迭代
            raise StopIteration
        #完成数列计算
        self.first,self.sec=self.sec,self.first+self.sec
        #数列长度减一
        self.__len -= 1
        return self.first

    #定义__iter__方法,该方法返回迭代器
    def __iter__(self):
        return self

#创建Fibs对象
fibs=Fibs(10)
#获取下一个元素
print(next(fibs))  # 1
#遍历迭代器
for i in fibs:
    print(i,end=' ')  # 1 2 3 5 8 13 21 34 55 

3. 扩展列表、元组和字典

有时标准的列表、元组和字典无法满足业务要求,我们可以重新自定义一个全新的,也可以选择扩展它们——继承系统已有的列表、元组和字典,然后重写或新增方法。

class ValueDict(dict):
    def __init__(self,*args,**kwargs):
        #调用父类构造方法
        super().__init__(*args,**kwargs)

    #新增getkeys方法
    def getkeys(self,val):
        result = []
        for key,value in self.items():
            if value == val:
                result.append(key)
        return result

my_dict=ValueDict(Chinese=92,Math=93,English=92)
#获取92对应所有key
print(my_dict.getkeys(92))  #['Chinese', 'English']
my_dict['History']=92
print(my_dict.getkeys(92))  #['Chinese', 'English', 'History']

四、 生成器

生成器是一个特殊的迭代器,它保存的是算法,每次调用next()或send()就计算出下一个元素的值,直到计算出最后一个元素,没有更多的元素时,抛出StopIteration。

生成器有两种类型,一种是生成器表达式(又称为生成器推导),一种是生成器函数。

生成器表达式是通过一个Python表达式语句去计算一系列数据,但生成器定义的时候数据并没有生成,而是返回一个对象,这个对象只有在需要的时候才根据表达式计算当前需要返回的数据。

生成器函数是一种语句中包含yield关键词的特殊的函数,它本身是一个迭代器,外部需要访问该迭代器数据的代码通过调用next函数(或迭代器的next方法)或send方法,触发函数执行计算并通过yield返回一个计算结果数据,返回数据后该函数立即停止执行,函数状态会保存在本地变量中,直到外部下次调用再激活,从上次停止执行部分开始执行。

 

1. 创建生成器

有2种方法:

  • 一是前面文章记录过的使用for循环创建生成器
  • 二是定义一个包含yield语句的函数,再调用该函数创建生成器
#生成val范围内差值递增的数列
def test(val,step):
    cur=0
    for i in range(val):
        cur += i*step
        yield cur

t=test(10,2)
print(next(t))
print(next(t))

#可使用函数将生成器能生成的所有值转换为列表或元组
t2=test(10,1)
print(list(t2))  # [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
t2=test(10,1)
print(tuple(t2))  # (0, 1, 3, 6, 10, 15, 21, 28, 36, 45)

yield语句作用如下:

  • 每次返回一个值,类似return语句
  • 程序每执行到yield语句时就会暂停,冻结执行
  • 程序被yield语句冻结后,当程序再调用next函数获取生成器下一个值时,程序才会向下执行

调用包含yield语句的函数并不会立即执行,只是返回一个生成器。只有当程序通过next()函数调用或遍历生成器时,函数才会真正执行。

 

2. 生成器的方法

send()

send()方法可实现生成器与外部程序动态交换数据,它与next功能类似,都用于获取生成的下一个值,不同之处在于它可以接收一个参数(外部程序可向生成器发送数据)

程序可通过yield表达式来获取send()方法所发送的值

只有等生成器被冻结后,外部程序才能使用send方法向生成器发送数据,所以获取生成器第一次生成的值需要通过next()方法,如果一定要用send(),只能传入None参数

#若使用send指定参数则求指定参数平方值,若不指定则依次求生成器下个值的平方
def square_gen(val):
    i=0
    out_val=None
    # 使用yield语句生成值,使用out_val接收send方法发送的参数值
    while True:
        out_val = (yield out_val**2) if out_val is not None else (yield i**2)
        if out_val is not None:
            print("====%d" % out_val)
        i+=1

sg=square_gen(5)
#第一次调用,只能传入None作参数
print(sg.send(None))  #未指定,相当于求0的平方;运行时i==0,运行完后i==1
print(next(sg))       #next函数取生成器下个值的平方;运行时i==1,运行完后i==2

#调用send方法获取生成器下一个值,参数9会被发送给生成器
print(sg.send(9))     #指定9,求9的平方;运行时i==2,运行完后i==3
print(next(sg))       #next函数取生成器下个值的平方;运行时i==3(即求3的平方),运行完后i==4

#输出如下
0
1
====9
81
9

生成器还有两个常用方法

  • close():停止生成器
  • throw():在生成器内部(yield语句内)引发一个异常

 

最后,总结一下:

  • 什么是可迭代对象?可迭代对象实现了能返回迭代器的iter或getitem方法,且其参数是从零开始的索引。
  • 什么是迭代器?迭代器实现了无参数的 next 方法,返回下一个元素,如果没有元素了,那么抛出 StopIteration 异常;并且实现iter方法,返回迭代器本身。
  • 什么是生成器?生成器是带有 yield 关键字的函数。调用生成器函数时,会返回一个生成器对象。
  • 什么是生成器表达式? 生成器表达式是创建生成器的简洁句法,这样无需先定义函数再调用。

 

参考

https://www.jianshu.com/p/891701629974

https://blog.csdn.net/zhusongziye/article/details/80246910

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hehuyi_In

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值