8 面向对象高级
8.1 常用内置函数
8.1.1 属性访问函数——# python的内置函数
例:class Person:一个
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print(self.name,'正在吃饭')
yige=Person('一个',18)
hasattr | 判断属性是否存在 | print(hasattr(yige,'sex'))——False |
getattr | 得到属性值,没有则报错 | print(getattr(yige,'name'))——一个 |
setattr | 设置属性值 | setattr(yige,'name','两个') print(getattr(yige,'name'))——两个 |
delattr | 删除属性 | delattr(yige,'name') print(hasattr(yige,'name'))——False |
注意:属性名需要加上引号,attr 即attribute的缩写。
通过简单 if 判断就能避免属性不存在时,报错的问题,同时也可以设置属性值。
例:if hasattr(yige,'sex'):
print(yige.sex)
else:
setattr(yige,'sex','男')
print(yige.sex)
======
res=getattr(yige,'sex') if hasattr(yige,'sex') else setattr(yige,'sex','男') #三目运算
print(yige.sex)
8.1.2 属性调用过程——# object方法
这里调用了一些内置的方法,也会产生同样的效果,这是因为那些函数本身就是调用的这些方法。
__getattribute__ | print(yige.__getattribute__('name'))——一个 |
__setattr__ | yige.__setattr__('name','两个') print(yige.__getattribute__('name'))——两个 |
__delattr__ | yige.__delattr__('name') print(yige.__getattribute__(name))——报错,没有名字 |
当实例调用一个不存在的属性的时候,会发生报错,但是如果增加__getattr__方法,则不会报错,而是执行此方法内的内容。
例:class Person:
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print(self.name,'正在吃饭')
def __getattr__(self,item):
print('no attribute',item)
yige=Person('一个',18) #实例
yige.sex——no attribute sex
8.1.3对象关系方法
issubclass | 判断第一个类是不是后面类的子类 | print(issubclass(Person,object))——True |
isinstance | 判断实例是否是这个类的实例, 或者判断数据是否是这个类型 | print(isinstance(yige,Person))——True print(isinstance((yige,int))——False |
type | 只能判断单个实例是否是属于这个类 | print(type(yige)==Person)——True |
print(isinstance(yige,(Person,int)))
——False #判断是否在两个类,加括号,用逗号隔开,两个类是or的关
8.2 魔法方法
8.2.1 __new__方法
Python中很多内置方法都是两个下划线在两边,中间是单词名字,并且这样的方法名字有一定的特殊的含义,把这种方法都称之为魔法方法。
比如__new__方法,就是最开始调用的一个方法,会在初始化之前自动调用。
例:class Earth:
def __new__(cls):
print('new')
def __init__(self):
print('init')
e = Earth() ——new
实例是__new__方法创建的,所以__new__会最先执行,因为我们重用的__new__把它的功能改成print('new'),所以不具备实例创建的功能,所以init并没有被调用,实例并没有被创建,如果我们想要继续创建实例,就需要调用object的__new__方法进行重用super().__new__(cls)。
例:class Earth:
def __new__(cls):
print('new')
return super().__new__(cls)
def __init__(self):
print('init')
e = Earth() ——new init
8.2.2 单例模式
例:class Phone:
def __init__(self,name):
self.name = name
xm = Phone('小米')
hw = Phone('华为')
print(xm.name)
print(hw.name) #此时一个类对应两个实例
单例模式的意思是:类始终只有一个实例存在,不会同时出现多个实例。利用__new__方法,可以很方便的去实现一个类的单例模式。
例:class RootUtil:
def __new__(cls): #重新定义new方法,cls就是一个类,是RootUtil
if not hasattr(cls,'a'):
cls.a= super().__new__(cls) #重用new;定义一个属性a保存创建的实例
return cls.a
def __init__(self):
self.name = 'rootUtil'
ru1 = RootUtil() # print(ru1)输出的ru1,ru2地址值相同,操作的是同一个实例
ru2 = RootUtil()
print(ru1.name)
print(ru2.name)——#输出都是rootUtil
8.2.3 输出魔法方法
__str__ : 修改print打印时的样子
__repr__ : 修改直接打印时的样子
例:class RootUtil:
def __new__(cls):
if not hasattr(cls,'a'):
cls.a= super().__new__(cls)
return cls.a
def __init__(self):
self.name = 'rootUtil'
def __str__(self): #修改print打印时的样子,即交互模式下直接输入print(ru1)
return f'姓名:{self.name}'
def __repr__(self): #修改直接打印时的样子,即交互模式下直接输入ru1
return f'name:{self.name}'
ru1 = RootUtil()
print(ru1.name)——rootUtil #此时查看的是object的__str__
print(ru1)——姓名:rootUtil #此时查看的是__str__,__repr__
print() 会先找__str__,如果没有,再看__repr__,object的__str__,所以当用__str__,__repr__时,使用print()会打印出输出内容。
8.3 协议
8.3.1 序列协议:列表和元组
序列协议即定义:__len__, __getitem__,__setitem__,__delitem__等协议,如果不需要改变,那就不要定义。
例:class IndexTuple:
def __init__(self,*args):
self.value = args #元组
self.index = tuple(enumerate(self.value)) # enumerate,枚举函数,可以得到
def __len__(self): #获取长度 每个值及对应的索引
return len(self.value)
def __getitem__(self,key): #索引
return self.index[key][1]
def __repr__(self): #获取元组
return str(self.value) #字符串强转
my_t = IndexTuple(1,2,3)
print(len(my_t ))——3
print(my_t[1]) ——2 # my_t[1]此处索引是中括号,key=1,得到的值是2
print(my_t) ——(1,2,3) # 得到元组类型
8.3.2 迭代器协议:列表和迭代对象
只要类实现了__iter__ 和 __next__ 方法,则这个类就是迭代器,因此只要实现这两个方法,则是可以迭代对象。
例:class Number:
def __init__(self,end=10): #迭代到9
self.start = -1
self.end = end
def __iter__(self): #使他能够迭代
return self #返回本身
def __next__(self): #获取下一个next
self.start += 1
if self.start >= self.end:
raise StopIteration # raise:引发异常,中止运行
return self.start
nu = Number()
print(next(nu))——0
for i in Number(20): #从0迭代到19
print(i)
8.3.3 上下文协议:with关键字的使用
只要类里面定义__enter__和__exit__这两个方法,则可以通过with去使用此对象。
例:import time
class RunTime:
def __enter__(self):
self.start_time = time.time()
return self.start_time #先赋值,再return,不能直接return赋值
def __exit__(self,exe_type,exc_val,exc_tb): #三个参数值exe_type,exc_val,exc_tb必写,错误类型,错误值,错误位置,记住即可
self.end_time = time.time()
self.run_time = self.end_time - self.start_time
print('程序运行时间',self.run_time)
with RunTime():
for i in range(100000):
pass # 不做任何事情,站位语句