一、常用内置函数
思考一:上节课掌握了类的定义和基本使用,可以通过点操作符去访问属性和方法,但是如果属性不存在会怎么办呢?
思考二:不希望因为属性不存在而出现报错,如何避免这个问题呢?
class Person:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def run(self):
print(f'{self.name}今年{self.age}岁')
hasattr 判断属性是否存在
getattr 得到属性值,没有则报错
setattr 设置属性值
delattr 删除属性
注意:属性名需要加上引号,attr是attribute的缩写
上述这些就是类里面的内置函数,和我们一般在使用列表或者其他序列类型使用的添加删除转化等是一个道理。
if hasattr(xiaoyu,'name'):
a = getattr(xiaoyu,'name')
else:
setattr(xiaoyu,'name','xiangwang')
通过简单if判断就能避免属性不存在时,报错的问题,同时也可以设置属性值
a = getattr(xiaoyu,'name') if hasattr(xiaoyu,'name') else setattr(xiaoyu,'name','xiaowang')
思考:这个三目运算是否保证a得到属性值
是可以的
class Person:
pass
class GuangDong(Person):
pass
class Hunxue(GuangDong):
pass
issubclass 判断第一个类是不是后面类的子类
isinstance 判断实例是否是后面类的实例
type 也可以判断,但是只能判断单个类
总结
1、属性调用
掌握:hasattr、getattr、setattr、delattr
2、对象关系方法
掌握:isinstance和type方法
二、属性调用过程
思考一:学到现在已经接触了很多的内置方法,那么这么多方法,Python底层是如何去实现的呢?
属性调用过程
class Person:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def run(self):
print(f'{self.name}今年{self.age}岁')
xiaoyu = Person('小宇',24,'男')
这里调用了一些内置的方法,也会同样的效果
这是因为那些函数本身就是调用的这些方法
上述的例子我们看下如下这个
当我们在代码中加入如下代码
class Person:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def run(self):
print(f'{self.name}今年{self.age}岁')
def __getattr__(self,item):
print('no attribute')
xiaoyu = Person('小宇',24,'男')
当实例调用一个不存在的属性的时候,会发生报错,
但是如果增加__getattr__方法,则不会报错,而是执行此方法内的内容
属性调用过程
意思就是,当没有定义getattr的时候,系统他会自动调用__getattribute__这个隐藏的内置函数如判断有没有,有的话就返回,没得话就报错(这个是你没有自定义这个内置函数时候的功能),当你自定义以后,你要他输出什么他就输出什么切记自定义的函数名为__getattr__,如果你自定义的是__getattribute__这个函数那么你无论输入什么都是自己定义的输出
三、魔术方法
思考一:刚才展示了这些内置方法,通过这些可以看出,很多时候,都是在使用内置的方法来实现一些功能,通常把这些方法称之为魔法方法
class Earth:
def __new__(cls):
if not hasattr(cls,'_instance''):
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
self.name = 'earth'
Python中很多内置方法都是两个下划线在两边,中间是单词名字,并且这样的方法名字有一定的特殊的含义,把这种方法都称之为魔法方法
比如这里的__new__方法,就是最开始调用的一个方法,会在初始化之前自动调用
class Earth:
def __new__(cls):
if not hasattr(cls,'_instance'):
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
self.name = 'earth'
利用__new__方法,可以很方便的去实现一个类的单例模式
单例模式的意思是:类始终只有一个实例存在,不会同时出现多个实例
class Earth:
def __new__(cls):
if not hasattr(cls,'_instance'):
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
self.name = 'earth'
def __str__(self):
return 'this is __str__ %s' % self.name
def __repr__(self):
return 'this is __repr__ %s' % self.name
str :修改print打印时的样子
repr :修改直接打印时的样子
但是当我们只用一个呢
第一次我们注释掉__str__方法,试下
第二次我们注释掉__repr__方法,试下
对比下来还是有明显的差别的总结一下
1、在交互模式中,如果同时存在方法__str__和__repr__时候,直接打印的时候会调用第二个方法,print打印时候会调用第一个方法
2、在交互模式中,只存在__repr__方法时候,直接打印会调用这个方法,print打印时候会打印出__repr__方法里面的内容
3、在交互模式中,只存在__str__方法时候,直接诶打印会是一个函数体的那种形式,print打印时候会调用__str__方法里面的内容
四、协议
思考一:刚才已经自定义修改了很多属性,那再大胆点是否可以去自定义类似元组这样的序列类型呢?
class IndexTuple:
def __init__(self,*args):
self.values = tuple([x for x in args])
self.index = tuple(enumerate(self.values))
def __len__(self):
return len(self.values)
def __getitem__(self,key):
return self.index[key]
def __repr_(self):
return str(self.values)
序列协议即定义:len、getitem、setitem、__delitem__等协议,如果不需要改变,那后两个就不要定义
Python中很多的协议,比如序列协议等很多协议,只要遵守这些协议,既可以说是对应的类型,比如定义了序列类型协议,则定义的类就是序列类型
class Number:
def __init__(self,end=10):
self.start = 0
self.end = end
def __iter__(self):
return self
def __next__(self):
self.start += 1
if self.start >= self.end:
raise StopIteration
return self.start
nu = Number()
只要类实现了__iter__和__next__方法,则这个类就是迭代器,因此只要实现这两个方法,则是可以迭代对象
import time
class RunTime:
def __enter__(self):
self.start_time = time.time()
return self.start_time
def __exit__(self,exc_type,exc_val,exc_tb):
self.end_time = time.time()
self.run_time = self.end_time - self.start_time
print('Time consuming %s' % self.run_time)
同样的还有上下文协议,即我们之前讲过的with的使用,只是类里面定义__enter__和__exit__这两个方法,则可以使用with去使用此对象
五、本节课总结
1、常用内置函数
掌握:hasattr、getattr、setattr、isinstance
2、属性调用过程
掌握:属性调用的过程
3、魔法方法
掌握:new、str、repr
4、协议
了解:序列类型协议、上下文协议、迭代器协议
六、上节课作业
1、定义一个账户类,可以创建账户、存款、取款 、查询余额、以及销户等操作
2、现在三个人分别去开户,存款 和 销户,请利用上面的类实现出来
提示:账户必备要素:1、账户名 2、密码 3、余额
class Account:
def __init__(self,name,password,money=0):
self.name = name
self.pwd = password
self.money = money
def deposit(self,money):
pwd = input('请输入账户密码:')
if pwd == self.pwd:
if isinstance(money,(int,float)) and money >= 0:
self.money += money
return f'恭喜您存入金额{money}元,当前账户余额为{self.money}‘
else:
print('密码错误,请重新执行')
def balance(self,money):
pwd = input('请输入账户密码:')
if pwd == self.pwd:
if isinstance(money,(int,float)) and money >= 0 and money <= sefl.money:
self.money -= money
return f'你取了{money}元,当前账户余额为{self.money}‘
else:
print('密码错误,请重新执行')
def query(self,pwd):
if pwd == self.pwd:
return self.money
else:
print('密码错误,请重新执行')
mr = Acoount('moran','qwe123',10000)
def account_cancellation():
global mr
if mr.query('qwe123') == 0:
del mr
else:
print('当前账户还有余额请先取出')
七、作业
1、测试列表推导和不用列表推导哪一种速度更快
2、range不可以使用小数做步长,实现一个可迭代对象,可以实现小数步长
参考代码下期见