面向对象高级进阶
一.类中的装饰器的使用:property,classmethod,staticmethod,
property的使用:property 的作用是讲一个类的函数属性装饰成对象的一个数据属性,能够直接被对象.方法名 调用,不用去通过加()的方式调用。
当property去装饰一个类的函数方法时,与setattr,delattr 共用时,@name.setattr @name.delattr 前面的前缀必须相同
property的本质就实现了 get,set,del 三个方法
class Foo: @property def AAA(self): print('get的时候运行我啊') @AAA.setter def AAA(self,value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA
class Goods: def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价
#实现类型检测功能 #第一关: class People: def __init__(self,name): self.name=name @property def name(self): return self.name # p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常 #第二关:修订版 class People: def __init__(self,name): self.name=name #实例化就触发property @property def name(self): # return self.name #无限递归 print('get------>') return self.DouNiWan @name.setter def name(self,value): print('set------>') self.DouNiWan=value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1=People('alex') #self.name实际是存放到self.DouNiWan里 print(p1.name) print(p1.name) print(p1.name) print(p1.__dict__) p1.name='egon' print(p1.__dict__) del p1.name print(p1.__dict__) #第三关:加上类型检查 class People: def __init__(self,name): self.name=name #实例化就触发property @property #当property将一个类的函数属性定义与__init__中的名字一样时, # 由于property的优先级更高会优先执行property,而property必须通过setter方法去设置对象的新的属性 def name(self): # return self.name #无限递归 print('get------>') return self.DouNiWan @name.setter def name(self,value): print('set------>') if not isinstance(value,str): raise TypeError('必须是字符串类型') self.DouNiWan=value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1=People('alex') #self.name实际是存放到self.DouNiWan里 p1.name=1 案例二
2.classmethod:
class Classmethod_Demo(): role = 'dog' @classmethod #作用及时统一了对象和类调用方法的方式,都是通过同一种方式去调用 def func(cls): print(cls.role) def func1(self): print(self.role) Classmethod_Demo.func() Classmethod_Demo.func1() #必须传一个参数才能实现类调用去下的函数属性。
3.staticmethod:
class Staticmethod_Demo(): role = 'dog' @staticmethod #作用就是当成普通的函数方法使用,类与对象都不用传参数 def func():#注意不自动传self值了 print("当普通方法用") Staticmethod_Demo.func() s =Staticmethod_Demo() s.func()
#1...__new__方法的使用:就是构造方法,创建一个对象 #单例模式:一个类从始至终只实例化一个对象,单例模式的模拟 """ class A: __issingle = False def __init__(self,name,age): print('执行的init方法') self.name = name self.age = age def __new__(cls, *args, **kwargs): print('执行的new方法') if cls.__issingle: return cls.__issingle cls.__issingle = object.__new__(cls) return cls.__issingle a1 = A('yuan',18) a2 = A('rong',18) a1.id = '001' print(id(a1),id(a2)) #在内存中的id都是相同的,实际上操作的是同一个对象 print(a1.name,a2.name) print(a1.age,a2.age) print(a1.id,a2.id)# a2对象而言并没有id属性,用的就是a1.id,实际上就是操作同一个实例化对象 """
#.2.item系列的方法: 字典类型的数据就是通过item的内置方法去创建和操作的。 """ class A: def __init__(self,name): self.name = name def __getitem__(self, item): print('执行的getitem方法') return self.__dict__[item] def __setitem__(self, key, value): print('执行的setitem方法') self.__dict__[key] = value return self.__dict__ def __delitem__(self, key): print('执行的delitem方法') del self.__dict__[key] def __delattr__(self, item): print('执行的delattr方法') del self.__dict__[item] pass a = A('yuan') # print(a.name) # print(a['name']) # a.age = 18 a['age'] = 19 print(a.age) del a['age'] #如果执行del 语句,会执行__delitem__方法, del a.age # 如果执行del 语句,会执行__delattr__方法,就是两种删除的方法不一样执行调用类的方法就不一样, #如果即没有delitem和delattr方法就会去执行父类中的方法,都没有就执行object中的delattr方法 print(a.age) """
#__str__:可以控制对象的属性详细的说明的方法,当类中没有__str__方法时候,会调用__repr__ """ class A: def __str__(self): print('这是A类自己的__str__方法') return 'A' #可以在这里面定义自己想要定义返回的信息 a = A() print(a)#当类中没有定义自己的__str__方法时,会去object中找__str__方法,会返回这个对象的内存地址 #而当类中有自己定义的__str__方法时会优先执行执行的__str__方法,print一个对象的时候会自动执行该对象下的 #__str__方法,该对象的类中没有,找父类,父类没有,找object中。 print(str(a)) %s,str() 就是调用类下的__str__方法 """
# __repr__:将对象的原始数据格式打印到终端,当类中没有__repr__时候,不能调用__str__方法。 #%r repr() 就是调用了类下的__repr__方法 """ class A: def __init__(self,name,age): self.name = name self.age = age def __repr__(self): print('这是自己的repr方法') return str(self.__dict__) #跟str的返回值一样,必须返回一个字符串 a = A('yuan',18) print('%r'% a)#相当于调用_repr__方法 print(repr(a)) """
#__del__:析构函数 # 作用一般是在文件打开后做的收尾工作,与内置的垃圾回收装置相关 """ import time class A: def __del__(self): print('执行自己的del方法') a = A() #如果一个程序代码都执行完之后,会自动执行对象中的__del__方法,即没有手动 # del 对象也会执行__del__里面的代码,与python中的垃圾回收装备有关联关系。 time.sleep(3) """
#__call__:当一个对象被调用的时候会自动执行该对象下的__call__方法 """ class A: pass # def __call__(self, *args, **kwargs): # print('执行自己的call方法') a = A() #相当于执行了 a() ,就是自动执行该对象下的__call__方法 #如果没有该对象下没有__call__方法,及时提示报错,只会从该对象的父类中查找是否有 #__call__方法,不会去object中找寻__call__方法, a() """
#__eq__:判断两个对象是否相等,默认的是比较两个对象的内存地址,如果需要重新 #定制自己的判断相等的方法,只需要在__eq__中定义自己的方法 """ class A: def __init__(self,name): self.name = name def __eq__(self, other): if self.name == other.name: return True a1 = A('yuan') a2 = A('yuan') print(a1 == a2) # 如果没有定制自己的__eq__,就默认执行object中的__eq__,默认比较两个对象的内存地址 """
#__hash__:获得对象的hash值 """ a = 10 a1 ='abc' l = [1,2,3] print(hash(a)) print(hash(a1)) print(hash(l)) #可变数据类型是不可以被hash的 """
#内置方法的实例,使用内置方法会使代码更加的优美 #1 实例一 模拟一副扑克牌 """ import random import json from collections import namedtuple from random import shuffle Card = namedtuple('Card',['rank','suits']) class Poke: ranks = [str(rank) for rank in range(1,11)] + list('JQKA') suits = ['红心','黑桃','方块','梅花'] def __init__(self): self.__cards = [Card(rank,suit) for rank in Poke.ranks for suit in Poke.suits] def __getitem__(self, item): #想要通过[]的形式去取值,就要设置自己的__len__方法, return self.__cards[item] def __len__(self): return len(self.__cards) def __setitem__(self, key, value): self.__cards[key] = value def __str__(self):#想要定义自己的__str__就必须要定义自己的__setitem__方法 return json.dumps(self.__cards,ensure_ascii=False)#__str__可视化要进行json序列化一下 c = Poke() print(c[0]) #取得是第一张牌 print(c[0][0])#取得是第一张牌的数字 print(c[0][1])#取得是第一张牌的花色 print(random.choice(c)) print(c) shuffle(c)#完成洗牌操作 print(c) print(c[:5])#直接去通过__getitem__方法去实现的,类似于用字典的关键字去取值一样 """
#2.实例一 一道面试题 #1.有100的对象,名字和性别相同,但年龄不同 class A: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def __eq__(self, other):#加了__eq__才能完成去重,内置判断去重不是简单的判断hash,还是与__eq__相关联 if self.name == other.name and self.sex == other.sex: return True return False def __hash__(self): return hash(self.name + self.sex) a1 = A('yuan','male',18) a2 = A('yuan','male',18) print(set((a1,a2)))# 还是没有完成去重,加了__hash__和__eq__才能完成去重 """
a1 = A('yuan','male',18) a2 = A('yuan','male',19) print(a1) print(a2) print(a2.__dict__) res =set((a2,a1))# 还是没有完成去重,加了__hash__和__eq__才能完成去重 print(list(res)[0].__dict__) #完成了去重,得到去重后的值
宁静致远