面向对象函数式编程
#狗的特征
dog1 =
{'name' : '冀祥'
'gender' : '母'
'type' : '柯基'}
dog2 =
{'name' : '刘鹏'
'gender' : '母'
'type' : '法斗'}
dog3 =
{'name' : '姚宇轩'
'gender' : '公'
'type' : '泰迪'}
def dog(name,gender,type): #这是一个类
def jiao(dog): #这是类的函数属性
print('有一条狗[%s]在汪汪汪' % dog['name'])
def chi_shi(dog): #这是类的函数属性
print('有一条[%s]在吃屎' % dog['type'])
def init(name,gender,type): #此步骤为初始化函数,值为传来的参数,key为新的参数,防止有默认值
d1 = {'name' : name,
'gender' : gender,
'type' : type,
'jiao' : jiao,
'chi_shi' : chi_shi}
return d1
return init(name,gender,type) #直接返回实例的信息
dog1 = dog('冀祥','母','柯基') #实例化
dog1['jiao'](dog1) #用实例调用类的方法,用实例本身当作参数传入
dog1['chi_shi'](dog1)
def school(name,addr,type):
def init(name,addr,type):
sch = {
'name' : name,
'addr' : addr,
'type' : type,
'examination' : examination,
'recruit_students' : recruit_students
}
return sch
def examination(school):
print('%s 学校正在考试' %school['name'])
def recruit_students(school):
print('%s学校 %s 正在招生' %(school['type'],school['name']))
return init(name,addr,type)
s1 = school('剑桥','England','私立')
print(s1)
print(s1['name'])
s1['recruit_students'](s1)
s2 = school('哈佛','America','公立')
print(s2['name'])
s2['examination'](s2)
面向对象独有语法class
isinstance(obj,cls) 查询一个对象是由哪个类产生的,返回布尔值。
判断s是否是字符串类型
s = 'harvey'
print(isinstance(s, str)) # True
issubclass(sub,super) 查看前一个类是否是后一个类的子类,返回布尔值。
class Dog: #正规格式为首字字母大写
def __init__(self,name,gender,type): #self为默认参数,实则代表实例名
self.name = name #dog1.xingming = name
self.gender = gender #dog1.xingbie = gender
self.type = type #dog1.leixing = type
def bark(self):
print('一条叫[%s]的[%s]不停地叫唤' %(self.name,self.type))
def bite(self):
print('[%s]又咬人了' %(self.name))
def chi_shi(self):
print('一条[%s]正在吃屎' %(self.type))
dog1 = Dog('冀祥','female','柯基')
print(dog1.__dict__) #以字典形式
dog2 = Dog('刘鹏','male','藏獒')
dog3 = Dog('宇轩','male','京巴')
# dog1.bark()
# dog2.bite()
dog3.chi_shi() #用.的方式调用本质上就是在自己的属性字典中中寻找函数属性,相当于:
Dog.__dict__['chi_shi'](dog3)
# print(Dog.__dict__['bite'](dog1))
print(Dog.__name__)
print(Dog.__doc__)
print(Dog.__module__) #__main__,代表文件名
类属性增删改查
class Chinese:
country = 'China' #新建一个实例,即数据属性
def __init__(self,name):
self.name = name
def play_games(self,ball):
print('%s 正在打%s' %(self.name))
#查看
print(Chinese.country)
#修改
Chinese.country = 'America'
print(Chinese.country) #America
p1 = Chinese('harvey')
print(p1.__dict__) #{'name': 'harvey'}
print(p1.country) #America
#增加
Chinese.addr = 'Asian'
print(Chinese.addr)#Asian
print(p1.addr)#Asian
#删除
del Chinese.addr
del Chinese.country
print(Chinese.country) #报错
def have_breakfast(self,food):
print('%s 正在吃 %s' %(self.name,food))
Chinese.food = have_breakfast #将一个函数添加到Chinese类中成为他的函数属性
print(Chinese.__dict__) #此时已经在Chinese的字典属性中
p1.food('提拉米苏')
def test(self):
print('我在玩泥巴')
Chinese.play_games = test #覆盖之前的play_games
p1.play_games() #p1.Chinese.play_games(p1) 默认传入p1到self位置
实例属性增删改查
class Chinese:
country = 'China' #新建一个实例,即数据属性
def __init__(self,name):
self.name = name
def play_games(self,ball):
print('%s 正在打%s' %(self.name))
p1 = Chinese('harvey')
print(Chinese.__dict__)
#查看
print(p1.name)
print(p1.play_games)
#增加
p1.age = 18 #增加数据属性
print(p1.__dict__) #{'name': 'harvey', 'age': '18'}
print(p1.age)
#删除
del p1.age
print(p1.age) #报错
#修改
#不要修改底层的属性字典
#p1.__dict__['gender'] = 'female'
p1.age = 19
print(p1.age) #19
class Chinese:
country='China'
l=['a','b']
def __init__(self,name):
self.name=name
def play_ball(self,ball):
print('%s 正在打 %s' %(self.name,ball))
p1=Chinese('alex')
print(p1.l) #['a', 'b']
p1.l=[1,2,3]
print(Chinese.l) #['a', 'b']
print(p1.__dict__) #{'name': 'alex', 'l': [1, 2, 3]}
p1.l.append('c') #用实例调用来l,添加字符串'c',但并不会改变类的l
print(p1.__dict__) #{'name': 'alex', 'l': [1, 2, 3, 'c']},实例的字典属性中添加成功
print(Chinese.l) #['a', 'b']但类中不改变
封装
class Room:
tag = 1
def __init__(self,name,owner,width,length,heigh):
self.name = name
self.owner = owner
self.width = width
self.length = length
self.heigh = heigh
# def area(self):
# print('%s住的%s面积是%s' %(self.owner,self.name,self.width*self.length))
#r1 = Room('海景房','harvey',100,80,10)
#r1.area() #r1.area 封装后不需要括号
@property #封装
def area(self):
return self.width*self.length
r1 = Room('海景房','harvey',100,80,10)
print(r1.area) #加了返回值可以实现用户或别人这样使用你的程序,封装逻辑
print(r1.name) #使你的程序更有神秘感,无法被看破,调用形式都相同
#那如果我们不想通过实例,直接访问类的详情信息呢?
def test(self):
# print('这是test函数',self.name)
# print(Room.tag)
# Room.test() #报错,因为类调用类自己的函数属性必须通过实例
# Room.test(1) #1.name,随便传一个参数也报错
@classmethod #类可以直接调用,不需要实例了。用于解决类调用自己的方法而不借助实例
def tell_info(cls): #cls相当于接收了一个类名
print(cls) #<class '__main__.Room'>
print(cls.tag)
#Room.tell_info() #1,可以直接调用类的方法了
#工具包,既不跟实例捆绑,也不跟类捆绑,完全脱离的方法,只为类提供服务
@staticmethod
def take_shawer(a,b,c):
print('%s %s %s正在一起洗澡' %(a,b,c))
Room.take_shawer('我','你','她')
组合
class School:
def __init__(self,name,addr):
self.name = name
self.addr = addr
def recruit(self):
print('%s 正在招生' %self.name)
class Course:
def __init__(self,name,price,school):
self.name = name
self.price = price
self.school = school
s1 = School('剑桥','England')
s2 = School('剑桥','America')
# c1 = Course('English','30k','剑桥','England')#报错,位置参数过多
c1 =Course('English','30k',s1)
print(c1.school.name)
print(s2.name)
print(c1.name)
s2.recruit()
class School:
def __init__(self,name,addr):
self.name = name
self.addr = addr
def recruit(self):
print('%s 正在招生' %self.name)
class Course:
def __init__(self,name,price,school):
self.name = name
self.price = price
self.school = school
s1 = School('剑桥','England')
s2 = School('剑桥','America')
s3 = School('剑桥','Australia')
msg = '1 剑桥 Endland,2 剑桥 America,3 剑桥 Australia'
while True:
print(msg)
menu = {
'1':s1,
'2':s2,
'3':s3
}
choice = input('选择学校:')
school_obj = menu[choice] #choice为key,此处取对应的value
name = input('选择课程:')
price = input('课程费用:')
new_course = Course(name,price,school_obj)
print('课程[%s]属于[%s],费用是[%s]' %(new_course.name,new_course.school.name,new_course.price) )
继承
class Dad:
money = 10
def __init__(self,name):
print('这是爸爸')
self.name = name
def teach(self):
print('%s正在教育他儿子%s' %self.name)
class Son(Dad):
money = 999999
def __init__(self,name,age):
self.name = name
self.age = age
def teach(self):
print('来自子类')
print(Son.money) #999999
Son.teach(2) #来自子类
s1 = Son('harvey',18)
s1.teach() #来自子类
print(s1.money) #999999
print(s1.name) #harvey
print(Dad.money) #10
子类调用父类的方法
class Vehicle:
Country = 'China'
def __init__(self,name,speed,load,power):
self.name = name
self.speed = speed
self.load = load
self.power = power
def run(self):
print('(爸爸)出发啦')
class Subway(Vehicle):
def __init__(self,name,speed,load,power,line):
super().__init__(name,speed,load,power)#super(__class__,self).__init__(name,speed,load,power)
#效果相同 super(Subway,self).__init__(name,speed,load,power)
self.line = line
def tell_info(self):
print(self.name,self.speed,self.load,self.power,self.line)
def run(self):
super().run() #Vehicle.run(self)也可以,但不实用。这是父类的run
print('(儿子)%s %s 出发啦' %(self.name,self.line)) #这是子类的run
四号线 = Subway('北京地铁','10km/s','10万','电力','4')
四号线.run()
四号线.tell_info()
#接口继承
import abc
class All_file(metaclass=abc.ABCMeta):
@abc.abstractmethod
def read(self):
print('All_file read')
@abc.abstractmethod
def write(self):
print('All_file write')
class Disk():
def read(self):
print('Disk read')
def write(self):
print('Disk write')
class Cdrom():
def read(self):
print('Cdrom read')
def write(self):
print('Cdrom write')
class Men():
def read(self):
print('Men read')
def write(self): #若此类没有写入方法,在没有abc模块的约束下,程序也能运行
print('Men write')#abc模块帮我们规范各类的方法,此类必须要跟上类有共同方法,不然在实例化时会报错
m1 = Men()
m1.read()
m1.write()
继承顺序参考https://www.cnblogs.com/zihe/p/7122055.html
多态
不同的类在继承了 一个相同的基类之后调用了基类的同一个方法,这个实现过程叫多态(多态的基础实际就是继承)
class H2O:
def __init__(self,name,temperature):
self.name = name
self.temperature = temperature
def evolve(self):
if self.temperature < 0:
print('%s 温度太低结成了冰' %self.name)
elif self.temperature > 0 and self.temperature < 100:
print('%s 温度刚好我还是水' %self.name)
elif self.temperature >= 100:
print('%s 温度太高了成水蒸汽了' %self.name)
class Water(H2O):
pass
class Ice(H2O):
pass
class Vapor(H2O):
pass
h1 = Water('水', 50)
h2 = Ice('冰', -8)
h3 = Vapor('水蒸汽', 108)
# h1.evolve()
# h2.evolve()
def func(obj): # 接口函数
obj.evolve()
func(h1)
封装
class People:
leader = 'Rick'
_leader = 'Daryl' # 单下划线是一种语言约定,代表创造者不希望使用者访问这个数据
__leader = 'Negan' # 双下划线的数据python会重命名,调用的方法也不同
def __init__(self,name,age,id):
self.name = name
self.age = age
self.id = id
def get_id(self):
print(self.id) # 属于创造者开的接口,可以直接访问id
def get_leader(self): # 这是专门用来访问__leader的接口
print(self.__leader)
print(People._leader) # 只是一种约定,仍然可以拿到
p1 = People('harvey','18','0414')
p1.get_id()
p1.get_leader()
# 双下划线的那位哥究竟被重命名为什么了呢?
print(p1._People__leader) # 这样就可以调到了,实际上还是只是语言约定
# 加了下划线将无法通过import *拿到,但可以import _leader
class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high
def tell_area(self): #此时我们想求的是面积
return self.__width * self.__length
def tell_high(self):
return self.__high
r1=Room('卫生间','harvey',100,100,10000)
# 此时使用者需求是求面积
print(r1.tell_area())
# 若使用者有新需求,要求体积了,而此时长宽高这些属性已经被我们做成私有的了
# 那我们就只能再定义一个专门计算体积的接口函数或者专门获取tell_high的接口函数供他使用了
# 若我们错误的理解封装的意义,将使用者会频繁使用到的属性封装起来,后期我们只有通过定义无数个接口函数来解决了
# 我们的程序也将被打的千疮百孔
反射以及动态导入
class Mediator:
features = 'ugly'
def __init__(self,name,addr):
self.name = name
self.addr = addr
def sell_house(self):
print('[%s] 在卖房子呢,买不起啊' % self.name)
def rent_out(self):
print('[%s] 在出租房子呢,现在都直接买了谁租' % self.name)
# hasattr 判断
print(hasattr(Mediator,'features')) # True
m = Mediator('五角大楼','美国')
# m.name = m.__dict__['name']
print(hasattr(m, 'name')) # True
print(hasattr(m, 'sell_house')) # True判断能否使用某个方法
# getattr 查看
print(getattr(m, 'name')) # 五角大楼
print(getattr(m, 'rent_out')) # 内存地址
func = getattr(m, 'rent_out')
func() # [五角大楼] 在出租房子呢,现在都直接买了谁租
# print(getattr(m,'rent_ou')) #没有则报错
print(getattr(m, 'rent_ou', '没有这个属性')) # 没有这个属性
# setattr 修改
setattr(m, 'sb', True)
print(m.__dict__) # {'name': '五角大楼', 'addr': '美国', 'sb': True}
setattr(m, 'name', 'SB')
print(m.__dict__) # {'name': 'SB', 'addr': '美国', 'sb': True}
setattr(m,'func1', lambda self:self.name+'sb')
print(m.__dict__) # {'name': 'SB', 'addr': '美国', 'sb': True, 'func1': <function <lambda> at 0x0000022344E43E18>}
# delattr 删除
delattr(m, 'func1')
print(m.__dict__) # {'name': 'SB', 'addr': '美国', 'sb': True}
delattr(m, 'sb')
print(m.__dict__) # {'name': 'SB', 'addr': '美国'}
del m.addr
print(m.__dict__) # {'name': 'SB'}
# 这位类的创建者出去度蜜月了,只留下了一半未完成的代码
class FtpClient:
'ftp客户端,但是还么有实现具体的功能'
def __init__(self,addr):
print('正在连接服务器[%s]' %addr)
self.addr=addr
def put(self):
print('正在上传文件')
# 这位哥想调用一下客户端的类,但是那位哥并没有完成,为了不影响当前工作进度,有了以下代码
from FTP_Client.FTP import FtpClient
f1=FtpClient('8.8.8.8')
# f1.put() 显然这样写会报错
if hasattr(f1,'put'):
func_get = getattr(f1,'put') # 这样判断如果put方法完成了,程序就能一路执行下去
func_get()
else:
print('其他的逻辑') #如果被调用的方法并没有完成,也可以不报错地执行我自己的任务,等到那位哥回来了,也不用改动代码
# 动态导入其实就是根据反射来实现的
# 动态导入第一种
module_h = __import__('h.hy')
# print(module_h) # <module 'h' from 'C:\\Users\\HY\\PycharmProjects\\untitled\\h\\__init__.py'>
module_h.hy.test1() # 这是test1
from h.hy import *
test1()
# test2() *无法调用带下划线的_test2()
from h.hy import test1,_test2 #但这样可以调用
_test2()
# 动态导入第二种
import importlib
h = importlib.import_module('h.hy')
print(h)
h.test1()
h._test2()
类的内置方法
attr系列
# 以get为例,当访问不存在的方法时系统会自动触发__getattr__,会报错。
# 当我们自己定义之后,会覆盖系统内置的这个函数,执行我们自定义的方法,不变的是当你访问不存在的方法时,同样还是会触发__getattr__
# 此时就能运行我们自定义的逻辑了
# __getattr__
class Num:
x = 1
def __init__(self,y):
self.y = y
def __getattr__(self, item):
print('__getattr__执行了') # 此处是为了表现__getattr__在内部运行了
n1 = Num(8)
print(n1.y)
n1.sss # __getattr__执行了,在调用不能调用的方法时才会触发
# __delattr__
class Num:
x = 1
def __init__(self,y):
self.y = y
def __delattr__(self, item):
print('__delattr__执行了') # 此处是为了表现__delattr__在内部运行了
# del self.item 无限递归
self.__dict__.pop(item) #解决递归问题
n1 = Num(8)
del n1.y # __delattr__执行了
# __setattr__
class Num:
x = 1
def __init__(self,y):
self.y = y
# 将所有key换成value
def __setattr__(self, key, value):
print('__setattr__执行了') # 此处是为了表现__setattr__在内部运行了
# self.key = value 参照len(str)原理是str__len__,即setattr('key','value')
# 换一次就会执行一次,执行一次就会换一次,将会递归上限
self.__dict__[key] = value
# 修改的原理也是通过__dict__来实现,既然上面会造成无限递归,那我们直接用索引来修改key和value
n1 = Num(8)
print(n1.__dict__) # {'y': 8}
n1.z = 10 # 第二次修改
print(n1.__dict__) # {'y': 8, 'z': 10}
n1.x = 6 # 修改类的数据属性
print(n1.__dict__) # {'y': 8, 'z': 10, 'x': 6}
print(Num.x) # 类的数据属性本身没变
class Foo():
def __init__(self, x):
self.x = x
def __getattr__(self, item):
print('执行getattr')
def __getattribute__(self, item):
print('执行getattribute')
# raise TabError('小弟发现异常')
# raise ('hhha')
f1 = Foo(10)
f1.x # 执行getattribute
f1.xxx # 你访问的属性不存在,触发__getattribute__
f1.xxx # 小弟发现异常
# getattribute是小弟,getattr为大哥,无论调用是否存在的属性都会触发系统内置的小弟,小弟原来的技能就是返回正确的属性/报错
# 当小弟和大哥同时存在时,小弟先上,此处我们重新定义了小弟,就可以自定义小弟报出错误的提示了
继承的方式完成包装
class List(list):
def append(self, p_object):
if type(p_object) is str:
# self.append(p_object) 此处会进入无限循环
# list.append(p_object) 此处调用的是list的append
super().append(p_object) # 调用父类方法是使用super()
else:
print('只能添加字符串')
def show_middle(self):
mid = int(len(self)/2)
return self[mid]
L1 = List('hello_world')
l2 = list('hello_world')
print(L1,type(L1)) # <class '__main__.List'>
print(l2,type(l2)) # <class 'list'>
print(L1.show_middle()) # _ 拿到最中间的字符串了
l2.append('qwer') # 工厂函数append在乖乖地履行着自己的义务
print(l2) # 成功添加
L1.append(123) # 只能添加字符串 调用的是自己类下的方法
L1.append('qwer') # 自己类下的方法
print(L1) # 定制的append函数添加成功
import time
class FileHandle:
def __init__(self,filename, mode = 'r', encoding = 'utf-8'):
self.file = open(filename, mode, encoding = encoding)
# 定义了一个self.file的方法,方法内容是用工厂函数open,得到一个文件句柄
self.mode = mode
self.encoding = encoding
def write(self,line):
# print('运行了自己的write',line)
t = time.strftime('%Y-%m-%d %X') # 我们可以定制我们的write,在运用了原有的功能之外还增添了新功能
self.file.write('%s %s' % (t, line)) # 此处的write是工厂函数的write方法
def __getattr__(self, item):
# print(item, type(item))
return getattr(self.file, item) # self.file可以拿到工厂函数open
# item即f1.read的read,字符串类型,getattr为字符串提供找到自己的属性
f1 = FileHandle('a.txt', 'r+')
print(f1.file) # <_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'>
print(f1.read) # 找不到会触发__getattr__,此时可以拿到f1的read方法了
f1.read() # a.txt成功创建
f1.write('qwer\n') # 成功写入'qwer'
f1.write('cpu负载过高\n')
f1.write('内存剩余不足\n')
f1.write('硬盘剩余不足\n')
f1.seek(0)
print(f1.read())
item系列
class Foo:
def __init__(self, item):
self.item = item
def __getitem__(self, item):
print('__getitem__触发了')
return self.__dict__[item]
def __setitem__(self, key, value):
print('__setitem__触发了')
self.__dict__['key'] = value
def __delitem__(self, key):
print('__delitem__触发了')
self.__dict__.pop(key)
f1 = Foo(8)
print(f1.__dict__) # {'item': 8}
f1.item = 9
print(f1.__dict__) # {'item': 9} 修改成功,但没有触发setitem
f1['item'] = 10 # 这样也修改成功,并且触发了setitem
f1.name = 'harvey' # ---->setattr-------->f1.__dict__['name']='egon'
print(f1.__dict__) # {'item': 9, 'key': 10, 'name': 'harvey'} 添加成功
f1.__dict__['age'] = 18 # --->setitem--------->f1.__dict__['name']='egon'
print(f1.__dict__) # {'item': 9, 'key': 10, 'name': 'harvey', 'age': 18} 添加成功
# 用.调用并不会触发setitem,而触发setattr,用__dict__索引修改才会触发setitem
# del f1.name # 删除也是一样
# del f1.__dict__['age']
# print(f1.__dict__)
f1['age'] # __getitem__触发了
print(f1['age']) # 18 查询成功
自定义对象的字符串显示形式(__str__,__repr__)
# l = list('hello')
# print(l) # ['h', 'e', 'l', 'l', 'o']
# file = open('test.txt','w')
# print(file) # <_io.TextIOWrapper name='test.txt' mode='w' encoding='cp936'>
# # 每个对象都有自己默认的字符串显示形式,通过以下方法可以自定义其显示方法
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '名叫%s,年龄%s' %(self.name, self.age)
f1 = Foo('harvey', 'age')
print(f1) # 名叫harvey,年龄age
# 原理是当执行print()时,会触发str(),str(f1)-->f1.__str__,当我们覆盖了系统内置的__str__s时,就可以实现了
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '这是str'
def __repr__(self):
return '名叫%s,年龄%s' %(self.name, self.age)
# repr作用和原理与str相同,在解释器中默认触发的是repr,在此两者都存在时,会优先触发str
# 如果找不到str,repr会完成str的效果,心甘情愿充当替身
f1 = Foo('harvey', 'age')
print(f1) # 这是str
省内存之__slots__
# 省内存方法 __slots__
class Foo:
__slots__ = ['name', 'age'] # ['name':None,'age':None]
f1 = Foo()
f1.name = 'harvey'
# print(f1.__dict__) 报错,在slots方法下,将不再产生dict
print(f1.__slots__) # ['name', 'age'],只能通过slots查看
print(Foo.__slots__) # ['name', 'age']
# f1.gender = '18' # 不能再自定义属性,只能创建或修改已经创建好的key对应的value
f1.name = 'egon' # 只能修改已有的name和age的value,不能再创建
print(f1.name) # egon 修改成功
f1.age = '28'
print(f1.age) # 28
# 再实例化一个也同样
f2 = Foo()
print(f2.__slots__)
f2.name = 'alex'
f2.age = 18
print(f2.name)
print(f2.age)
# 注意,此前的getattr,itemattr等方法都是建立在__dict__之上的,用了此方法虽然省内存,但之前的方法都将无法使用
__doc__
class Foo:
'我是描述信息'
pass
print(Foo.__doc__) # 我是描述信息
class Bar(Foo):
pass
# print(Bar.__doc__) # 该属性无法继承给子类
# print(Bar.__doc__) # 该属性无法继承给子类
index
# 可以查看从远方调用来的兄弟是来自哪一个模块和哪一个类的
from lib.aa import C
c1=C()
print(c1.name)
print(c1.__module__)
print(c1.__class__)
__call__
# 实例()是执行所在类的__call__方法,而类本身也是对象,类()实际也在执行类所在的一个大类的__call__
class Foo:
def __call__(self, *args, **kwargs):
print('实例执行啦 obj()')
f1=Foo()
f1() #f1的类Foo 下的__call__
Foo() #Foo的类 xxx下的__call__
析构
自动回收内存
class Foo:
def __init__(self,name):
self.name=name
def __del__(self):
print('我执行啦')
f1=Foo('alex')
# del f1 #删除实例会触发__del__
del f1.name #删除实例的属性不会触发__del__
print('--------------------->')
#程序运行完毕会自动回收内存,触发__del__
迭代器
class Foo:
def __init__(self,n):
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.n == 15:
raise ('迭代中止')
self.n += 1
return self.n
f1 = Foo(10)
print(f1.__next__()) # 11
print(f1.__next__()) # 12
print(f1.__next__()) # 13
print(f1.__next__()) # 14
# 迭代器会触发iter和next方法,使类中的实例迭代,就必须创建逻辑
# for循环能帮我们自动迭代并寻找到中止命令
for i in f1: # obj=iter(f1)------------>f1.__iter__()
print(i) # obj.__next_()
# 斐波那契数列
class Fib:
def __init__(self):
self._a = 1
self._b = 1
def __iter__(self):
return self
def __next__(self):
if self._a > 100:
raise StopIteration('就到这停止') # 这样停止是不会抛出错误的
self._a, self._b = self._b, self._a + self._b
return self._a
f1 = Fib()
print(next(f1))
print(next(f1))
print(next(f1))
print('我累了,歇会儿待会儿继续') # 中间暂停的话也是从上一个迭代对象开始继续迭代,不会从头开始
print(next(f1))
print(next(f1))
for i in f1:
print(i)
数据描述符
分为两种:
1、数据描述符————至少实现了__get__和__set__这两个方法
2、非数据描述符————没有实现__set__
class Foo:
def __get__(self, instance, owner):
print('get方法触发了')
def __set__(self, instance, value): # instance就是传过来的实例
print('set方法触发了', instance, '修改为%s' % value)
instance.__dict__['x'] = value # b1.__dict__,这样就可以完成修改操作和打印出属性字典了
def __delete__(self, instance):
print('delete方法触发了')
class Bar:
x = Foo() # 将一个类赋值给另一个类的实例属性
def __init__(self,n):
self.x = n # b1.x = 10
b1 = Bar(10) # set方法触发了
# print(b1.__dict__) # {}
b1.x = 123 # set方法触发了
print(b1.__dict__) # {}
# 现在处理任何关于x时,系统都将其扔给Foo类处理,触发了get但属性字典为空
b1.y = 6789 # 并没有跟x沾边,只在自己的类中操作就不会触发描述符
print(b1.__dict__) # {'y': 6789}
print(Bar.__dict__) # 'x': <__main__.Foo object at 0x00000298C3BB9390>
del b1.x # 删除这个实例属性,delete方法触发了
print(b1.__dict__)
b1.x # get方法触发了
b1.x = 1 # set方法触发了
描述符优先级
class Foo:
def __get__(self, instance, owner):
print('get方法触发了')
def __set__(self, instance, value): # instance就是传过来的实例
print('set方法触发了', instance, '修改为%s' % value)
instance.__dict__['x'] = value # b1.__dict__,这样就可以完成修改操作和打印出属性字典了
def __delete__(self, instance):
print('delete方法触发了')
class Bar:
x = Foo() # 将一个类赋值给另一个类的实例属性
def __setattr__(self, key, value):
self.__dict__['key'] = value
print('setattr运行了')
# 类属性 > 数据描述符
# Bar.x = 1
# print(Bar.x)
# print(Bar.__dict__) # 当类属性重新定义x时,会覆盖之前的Foo()
# 数据描述符 > 实例属性
# b1 = Bar()
# b1.x = 1 # set方法触发了 <__main__.Bar object at 0x0000021E94769390> 修改为1
# b1.x = 1 # setattr运行了。将x = Foo()注释之后,就会触发自己类中的修改函数
# 实例属性 > 非数据描述符
class Foo:
def __get__(self, instance, owner):
print('get方法触发了')
class Bar:
x = Foo() # 将一个类赋值给另一个类的实例属性
b1 = Bar()
b1.x = 1 # 定义到自己的属性字典中了
print(b1.__dict__) # {'x': 1} 不会触发非数据描述符(get)
上下文管理
with obj as f:
'代码块'
1.with obj ----》触发obj.__enter__(),拿到返回值
2.as f----->f=返回值、
3.with obj as f 等同于 f=obj.__enter__()
4.执行代码块
一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None
二:有异常的情况下,从异常出现的位置直接触发__exit__
a:如果__exit__的返回值为True,代表吞掉了异常
b:如果__exit__的返回值不为True,代表吐出了异常
c:__exit__的的运行完毕就代表了整个with语句的执行完毕
class Foo:
def __init__(self, name):
self.name = name
def __enter__(self):
print('运行enter')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('执行__exit__')
print('执行exc_type')
print('执行exc_val')
print('执行exc_tb')
# 先执行enter,接着执行逻辑,若无错误,全部执行结束后,执行exit,程序结束
# 相当于Foo实例化一个a.txt的类
# with Foo('a.txt') as f: # with触发enter,f为返回值
# print(f) # <__main__.Foo object at 0x00000262642998D0>
# print(f.name) # a.txt
# print('---')
# print('---')
# print('---')
# # 执行__exit__
# # <class 'NameError'>
# # name 'jkl' is not defined
# # <traceback object at 0x0000026758BACD48>
# print('qwer') # qwer
# 先执行enter,执行逻辑到有错误的那一步停止,报错,执行exit,程序结束
with Foo('a.txt') as f: # 运行enter with触发enter,f为返回值
print(f) # <__main__.Foo object at 0x00000262642998D0>
print(jkl) # 执行__exit__ 报错
# <class 'NameError'>
# name 'jkl' is not defined
# <traceback object at 0x0000026758BACD48>
print(f.name) # 不执行
print('---') # 不执行
print('---') # 不执行
print('---') # 不执行
print('qwer') # 不执行
def __exit__(self, exc_type, exc_val, exc_tb):
print('执行__exit__')
print(exc_type)
print(exc_val)
print(exc_tb)
return True # 返回值设置成True,不会报错
# 先执行enter,执行逻辑到有错误的那一步停止,但不报错,执行exit
with Foo('a.txt') as f: # 运行enter with触发enter,f为返回值
print(f) # <__main__.Foo object at 0x00000262642998D0>
print(jkl) # 执行__exit__
# <class 'NameError'>
# name 'jkl' is not defined
# <traceback object at 0x0000026758BACD48>
print(f.name) # 不执行
print('---') # 不执行
print('---') # 不执行
print('---') # 不执行
print('qwer') # qwer 因为是在程序外边,且无报错程序,所以正常运行
数据描述符的应用
class People:
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
p1 = People('harvey', 18, 98)
# 如果乱输入也会正常实例化,如果我们想限制输入的类型呢?
p2 = People('123', 'qwe', 98)
# 如果只有get方法只是非数据描述符,优先级在实例属性之后,无法在输入参数前对其进行判断
# 所以此处我们需要让它变成优先级高于实例属性的数据运算符,也就是需要定义set方法
class Type:
def __init__(self, key):
self.key = key
def __get__(self, instance, owner): # instance此处为p1,owner为p1的类
print('get执行了')
# print(owner)
return instance.__dict__[self.key]
def __set__(self, instance, value):
print('set执行了')
if not isinstance(value, str):
print('你输入的%s不是字符串类型' % value)
return
instance.__dict__[self.key] = value
class People:
name = Type('name') # 用type实例化一个p1,此处相当于p1.__set__() 即self.__set__()
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
p1 = People('harvey', 18, 98) # set执行了
print(p1.name) # get执行了 harvey
p2 = People(20, 18, 98) # set执行了 你输入的20不是字符串类型
class Type:
# 上述方法只能限定是字符串类型,那如果我们还想限制age参数是int类型呢?
def __init__(self, key, expected_type):
self.key = key
self.expected_type = expected_type
def __get__(self, instance, owner): # instance此处为p1,owner为p1的类
print('get执行了')
# print(owner)
return instance.__dict__[self.key]
def __set__(self, instance, value):
print('set执行了')
if not isinstance(value, self.expected_type):
raise TypeError('你输入的%s不是[%s]类型' % (value, self.expected_type))
return
instance.__dict__[self.key] = value
def __delete__(self, instance):
print('delete执行了')
return instance.__dict__.pop(self.key)
class People:
name = Type('name', str)
age = Type('age', int)
def __init__(self, name, age, salary):
self.name = name
self.age = age
self.salary = salary
p1 = People('harvey', 18, 98) # set执行了 set执行了(name,age 两次)
print(p1.name) # get执行了 harvey
p2 = People(123, 18, 98) # set执行了 TypeError: 你输入的123不是[<class 'str'>]类型
p2 = People(123, 'jkl', 98) # TypeError: 你输入的123不是[<class 'str'>]类型
类的装饰器
# 函数装饰器
def deco(func):
print('......')
return func
@deco # test = deco(test)
def test():
print('test函数运行')
test()
# 类的装饰器
@deco
class Foo(): # Foo = deco(Foo)
pass
# 函数装饰器
def deco(obj):
print('......', obj)
obj.x = 1
obj.y = 2
obj.z = 3
return obj
@deco # Foo = deco(Foo)
class Foo:
pass
print(Foo.__dict__)
# 一切皆对象之验证
# @deco # test = deco(test)
# def test():
# print('运行test')
# test.x = 1
# test.y = 2
# print(test.__dict__)
类的装饰器强化版
def typed(**kwargs):
def deco(obj):
for key, value in kwargs.items(): # 将字典组成元组,将值赋给key和value
# obj.key = value # 相当于新增一个名为key的属性
# obj.__dict__[key] = value # 相当于覆盖obj属性字典
setattr(obj, key, value)
return obj
return deco # 只要运行typed函数,就会运行deco
# 这样程序不会被写死,也可以随时增加数据属性
@typed(x = 1, y = 2) # typed()-->return deco-->deco(Foo)
class Foo:
pass
print(Foo.__dict__) # 添加成功
# 下面我们再定义一个类,加一个属性
@typed(name = 'harvey')
class Bar:
pass
print(Bar.__dict__) # 添加成功
类的装饰器 + 数据描述符
# 数据描述符用来限制key的type
class Type:
def __init__(self, key, expected_type):
self.key = key
self.expected_type = expected_type
def __get__(self, instance, owner): # instance此处为p1,owner为p1的类(People)
print('get执行了')
# print(owner)
return instance.__dict__[self.key]
def __set__(self, instance, value):
print('set执行了')
if not isinstance(value, self.expected_type):
raise TypeError('你输入的%s不是[%s]类型' % (value, self.expected_type))
return
instance.__dict__[self.key] = value
def __delete__(self, instance):
print('delete执行了')
return instance.__dict__.pop(self.key)
# 装饰器用来避免重复代码,例如此处的‘name = Type('name',str)\age = Type('age',int)’
def deco(**kwargs): # kwargs = {'name':str, 'age':18}
def wrapper(obj): # obj = People
for key, val in kwargs.items(): # (('name',str),('age',int))
setattr(obj, key, Type(key,val))
# setattr(People,'name',Typed('name',str)) #People.name=Typed('name',str)
return obj
return wrapper
@deco(name = str, age = int) # @wrapper-->People = wrapper(People)
class People:
def __init__(self, name, age, salary, gender):
self.name = name
self.age = age
self.salary = salary
self.gender = gender
p1 = People('harvey', 18, 98, 'male')
print(People.__dict__)
property的定制版
# print(r1.area()) # 这样会运行,如果我们想封装起来呢
# print(r1.area) # 这样无法得到运行结果,需要借助property
# 但系统的properpy并不能出发area的运行,要想r1.area直接触发运行,我们可以定制自己的property
class My_property:
def __init__(self,func):
self.func = func
# 此时的功能只是跟默认的相同,并没有实现我们期望的效果
def __get__(self, instance, value):
print('get运行了')
print(instance) # <__main__.Room object at 0x000001B96027E240> 即r1
print(value) # <class '__main__.Room'> 即r1所在的类
if instance is None: # 此处目的在于如果Room.area的话,instance会缺失,为避免报错,我们直接返回My_property的产生的实例
return self
res = self.func(instance)
return res
class Room:
def __init__(self,name,width,length):
self.name = name
self.width = width
self.length = length
@My_property # area = My_property(area)
def area(self):
return self.width * self.length
r1 = Room('厕所',1,1)
print(r1.area) # 1,完美!
最终版
# 如果要多次使用r1.area,每次都得运行这么一堆代码才可以得到的话,太浪费内存
# 所以新的需求来了,把第一次运算的结果存下来,以后再使用就可以直接拿了
class My_property:
def __init__(self,func):
self.func = func
# 此时的功能只是跟默认的相同,并没有实现我们期望的效果
def __get__(self, instance, value):
# print('get运行了')
# print(instance) # <__main__.Room object at 0x000001B96027E240> 即r1
# print(value) # <class '__main__.Room'> 即r1所在的类
if instance is None:
return self
res = self.func(instance)
setattr(instance,self.func.__name__,res) #r1,area,area(r1)
# 此步骤是在操作instance的属性字典,key为area,即函数名,value为area的计算结果
return res
class Room:
def __init__(self,name,width,length):
self.name = name
self.width = width
self.length = length
@My_property # area = My_property(area)
def area(self):
return self.width * self.length
r1 = Room('厕所',2,2)
print(r1.area) # 1,完美!
print(r1.__dict__) # {'name': '厕所', 'width': 1, 'length': 1, 'area': 1}
property干货!!!
# 第一种方法
class Foo:
@property
def get_AAA(self):
print('这样get才能运行')
# 3、需要添加此步才可以进行修改和设置
@AAA.setter
def set_AAA(self,value):
print('这样set才可以运行')
# 6、
@AAA.deleter
def del_AAA(self):
print('这样del才可以运行')
f1 = Foo()
f1.AAA # 1、此时可以正常调用属性
# f1.AAA = 1 2、但设置或修改缺会报错
f1.AAA = 1 # 4、现在就不会报错了
del f1.AAA # 5、删除也是一样
# 注意,它们的顺序在def时不可以颠倒
# 只有在AAA属性进行@property之后才可以用AAA.setter,AAA.deleter
# 第二种方法
class Foo:
def get_AAA(self):
print('这样get才能运行')
def set_AAA(self,value):
print('这样set才可以运行')
def del_AAA(self):
print('这样del才可以运行')
AAA = property(get_AAA,set_AAA,del_AAA) #此处的顺序还是不可以颠倒
f1 = Foo()
f1.AAA
f1.AAA = 1
del f1.AAA
元类
Python所谓一切皆对象,既然是对象就会有他的所属类,通常我们所创建的类,它们都属于元类,即Type类。
class Foo:
pass
f1 = Foo() # f1是通过Foo类实例化的对象
print(type(f1)) # <class '__main__.Foo'>
print(type(Foo)) # <class 'type'>
class Bar:
pass
print(type(Bar)) # <class 'type'>
# 通常的方法创建类
class Foo:
x = 1
def __init__(self,name,age):
self.name=name
self.age=age
def test(self):
print('=====>')
print(Foo)
print(Foo.__dict__)
# 通过元类实例化出一个类
def __init__(self,name,age):
self.name=name
self.age=age
def test(self):
print('=====>')
Bar = type('Bar',(object,),{'x':1 , '__init__':__init__,'test':test})
b1 = Bar('harvey',18)
print(Bar)
print(Bar.__dict__)
b1.test()
自定制一个类
class Mytype(type):
def __init__(self,a,b,c):
print('构造函数的运行')
def __call__(self, *args, **kwargs):
obj = object.__new__(self) # f1 = object.__new__(Foo),创造出一个没有任何属性的实例
self.__init__(obj, *args, **kwargs) # Foo.__init__(f1,'harvey')将实例的属性封装给obj
return obj # 将封装完毕的f1返回给调用他的地方
class Foo(metaclass=Mytype): # Foo = Mytype(Foo,'Foo',(),{})即实例化的过程,会出发My的__init__
def __init__(self,name):
self.name = name # f1.name = name
f1 = Foo('harvey') # 会触发__call__
f1.name
print(f1.__dict__)
异常处理
用if语句也可以实现异常处理,但可读性差并且代码重复太多
def test():
print('test')
choice_dic = {'1' : test()}
while True:
choice = input('请输入:').strip()
if not choice or choice not in choice_dic:
continue # 这就是一种异常处理的方式
choice_dic[choice]
又或者是
age = input('--> ')
if age.isdigit():
int(age)
elif age.isspace():
print('用户输入的是空格')
elif len(age) == 0:
print('不可输入为空')
else:
print('其他非法输入')
##########################################
num = input('--> ')
if num.isdigit():
int(age)
elif num.isspace():
print('用户输入的是空格')
elif len(num) == 0:
print('不可输入为空')
else:
print('其他非法输入')
# 如果两个属性的判断条件相同,那将会有太多重复代码。
# 你可能会说,那我这样不就行了?
if age.isdigit() and num.isdigit():
int(age)
int(num)
# 那如果有age1age2age3呢?那岂不是要很多的'and'?
所以,最合理的异常处理方法来了!
try: # 首先是两段判断条件相同的逻辑
age = input('-->')
int(age)
num = input('-->')
int(num)
except ValueError as V: # 可以简化异常名称,也可以不用as
print(V)
# 需要注意的是,if语句是先捕捉异常,而try是运行之后再捕捉异常
异常也属于一个类,即异常类,例如ValueError是Value类的异常,也只能处理Value的异常,IndexError只能处理索引的异常,遇到处理不了的异常或者说超出我们定义的异常范围,同样会报错。总有想不到的异常,但我们有万能方法,相当于if中的else。
try:
age = input('输入age--> ')
int(age)
num = input('输入num--> ')
int(num)
l = [2]
l[3]
dic = {}
dic['name']
except ValueError as V: # 此处的多个except就相当于elif
print(V)
except IndexError as V:
print(V)
except KeyError as V:
print(V)
# 万能方法
except Exception as V:
print(V)
print('不影响我')
当然我们会有疑问那既然有了万能的方法,还要记住别的类异常干嘛?那我们可以分两种情况:
1、如果我们的理想效果是无论有什么异常,只要有异常了,都统一执行Exception以下的代码,这个时候适合用我们万能的方法,大胆的去做吧。
2、如果我们期望的效果是碰到不同的异常,我们有不同的执行逻辑去等着它们,那我么需要将异常分类处理。
当然,当我们把需要特殊处理的异常都分别区分之后,也可以再用Exception
异常的其他机构(else,finally)
不要混淆与if中的else,完全不同的概念
s1 = 'harvey'
try:
int(s1)
except ValueError as V:
print(V)
except IndexError as V:
print(V)
except KeyError as V:
print(V)
except Exception as V:
print(V)
else:
print('如果没有触发异常,会触发我')
finally:
print('无论异常与否都会触发我,通常做清理工作')
print('不影响我')
自定义异常
class My_exception(BaseException):
def __init__(self,msg):
self.msg = msg
raise My_exception ('自己的异常') # __main__.My_exception: 自己的异常
断言
def test():
# 此处有一万行代码
# ...
# ...
x = 1
return x
res = test()
assert res == 2 # AssertionError
# 作用相当于:
# if res != 1:
# raise AssertionError
# 此处有五万行代码全指望着res == 1的时候他们才能运行