一 面向对象编程
1.什么是面向对象
面向过程与面向对象
面向过程编程:解决问题从过程出发,解决问题步
面向对象编程:解决问题从对象出发,解决问题找
对象与类
类:对象的类型 => 数字
具有相同特征与行为集合的
对象:类的具体表现 => 数字10
类的实例化,就是具有特征与行为实际存在的个体(每一个对象都是唯一的)
2.为什么要面向对象编程
面向过程:开发成本高,解决问题局限性小
面向对象:开发成本低,解决问题局限于对象
问题:’abc’ => {‘a’, ‘b’, ‘c’}
面向过程: 手撸
面向对象:str => list => set
开发:优选面向对象(找解决问题的对象),
再考虑找多个对象(面向对象与面向过程的结合),
最后自己去封装一个可以解决问题的对象(对外是面向对象的体现,内部解决问题的核心是面向过程)
# 需求
s = 'abc' # => ['a', 'b', 'c']
# 解决方案
# re.findall() list初始化方法
# for循环append
# 面向过程:解决问题步骤化
res = []
for c in s:
res.append(c)
print(res)
# 面向对象:解决问题找对象
# -- 对象如何解决问题:对象.解决问题的方法()
# 找re对象
import re
res = re.findall(r'[a-z]', s)
print(res)
# 找list对象
res = list(s)
print(res)
3.名称空间操作
import re
print(re.__dict__)
def fn():
pass
print(fn.__dict__)
二 类的相关知识
类与对象的声明
class People:
name = '人'
p1 = People()
p2 = People()
# 结论1:类与每一个对象的名称空间都是独立的
print(p1.__dict__) # {}
print(p2.__dict__) # {}
print(People.__dict__) # {'name': '人', ...系统的}
# 结论2:类与每一个对象都可以使用类中的名字
print(People.name) # 人
print(p1.name) # 人
print(p2.name) # 人
# 结论3:对象访问名字,优先访问自己的,自己没有再访问类的
p1.name = '张三'
p2.user = '李四'
print(People.name) # 人
print(p1.name) # 张三
print(p2.user) # 李四
print(p2.name) # 人
# 重点:
# 对象操作名字,操作的是对象的,类操作名字操作的是类的,之间相互不干预
# 类只能访问类的名字
# 对象访问名字,优先访问自身的,自身没有再访问类的
类的初始化方法
# 可以快速为类实例化出的每一个对象,产生对象名称空间中的多个名字
class NewTeacher:
def __init__(self, name, sex, age):
# print(id(self)) # self就是实例化产生的对象(nt1)
# print('init 被调用了')
self.name = name
self.sex = sex
self.age = age
pass
# 类()就是在调用类的__init__方法
nt1 = NewTeacher('王大锤', '男', 58)
# print(id(nt1))
print(nt1.name, nt1.sex, nt1.age)
nt2 = NewTeacher('王小锤', '男', 48)
print(nt2.name, nt2.sex, nt2.age)
类的方法分类
对象方法:直接定义的方法,建议由对象调用,类中内部需要使用对象的数据时的方法要定义为对象方法
1.对象方法对象调用,默认传入对象给第一个形参
class 类名:
def fn(self, *args, **kwargs): pass
类方法:被classmethod修饰的方法,建议由类调用,类中内部需要使用类的数据时的方法要定义为类方法
2.类方法由类调用,默认传入类给第一个形参
class 类名:
@classmethod
def fn(cls, *args, **kwargs): pass
静态方法:被staticmethod修饰的方法,建议由类调用,类中内部不需要类相关数据时的方法要定义为静态方法
3.静态方法建议由类调用,默认不传入调用者
@staticmethod
def fn(*args, **kwargs): pass
案例
class Book:
name = '书'
def __init__(self, name, price):
self.name = name
self.price = price
# 书的详情信息 => 一定需要知道哪本书
# @classmethod # 类调用cls就是类,对象调用处理成 对象.__class__
def detail(self):
# print(cls.name)
print("%s的价格为:%s元" % (self.name, self.price))
book1 = Book('西游记', 38.8)
book2 = Book('金瓶梅', 88.8)
book1.detail()
book2.detail()
# print(book1.__class__)
# 静态方法:方法的内部不需要对象及类的参与,所以定义为静态方法,但是方法必须由调用者,建议用类就可以了
class NumTool: # 工具类 => 模块
def max_two(self, n1, n2):
max_num = n1 if n1 > n2 else n2
print('大数是%s' % max_num)
@staticmethod
def new_max_two(n1, n2):
max_num = n1 if n1 > n2 else n2
print('大数是%s' % max_num)
n1 = NumTool()
n2 = NumTool()
n1.max_two(10, 20)
n2.max_two(10, 20)
NumTool.new_max_two(10, 20)
n1.new_max_two(10, 20)
# 类方法:方法的内部需要类的参与,所以定义为类方法,第一个参数默认传类
class NewNumTool:
PI = 3.14
@classmethod
def new_max_two(cls, n1, n2):
max_num = n1 if n1 > n2 else n2
return max_num
@classmethod
def new_max_three(cls, n1, n2, n3):
# max_num = "想去复用new_max_two"
max_num = cls.new_max_two(n1, n2)
max_num = cls.new_max_two(max_num, n3)
return max_num
@classmethod
def is_PI(cls, num):
if num == cls.PI:
return True
return False
res = NewNumTool.new_max_three(1, 5, 3)
print('大数是%s' % res)
print(NewNumTool.is_PI(3.149))
三 面向对象三大特性
封装
1、什么是封装
“装”的意思就往一个容器中放入一系列属性
“封”的意思就是藏起来,在内部可以看到,但对外部是隐藏的
2、为什么要用封装
将复杂的操作封装成简单的接口,并严格控制接口的调用过程
3、如何用封装
但凡是双下划线开头(不能是双下划线结尾)的属性,会被隐藏起来,类内部可以直接使用
而类外部无法直接使用,即封装是对外不对内的
这种隐藏的特点:
1、只是一种语法上的变形,会将开头的属性变形为:_自己的类名属性名(n=1 #_Foon=1)
2、该变形只在类定义阶段发生一次,在类定义阶段之后新增的__开头的属性并不会发生变形
3、隐藏是对外不对内的
4、在继承中,父类如果不想让子类覆盖自己的同名方法,可以将方法定义为私有的
class Teacher:
def __init__(self,name,age):
# self.__name=name
# self.__age=age
self.set_info(name,age)
def tell_info(self):
print('姓名:%s,年龄:%s' %(self.__name,self.__age))
def set_info(self,name,age):
if not isinstance(name,str):
raise TypeError('姓名必须是字符串类型')
if not isinstance(age,int):
raise TypeError('年龄必须是整型')
self.__name=name
self.__age=age
t=Teacher('egon',18)
t.tell_info()
t.set_info('egon',19)
t.tell_info()
# 什么是封装:将类的一下属性和方法对外隐藏,对内可见
# 为什么要封装:为属性和方法的操作添加权限,具体权限都是通过自定义逻辑来处理
# 封装的手段:在类属性方法,对象属性方法,静态方法名字前添加 __
# 只要是通过 __名字 这种命名规范,就是对外隐藏
# 本质:__名字 封装隐藏变量的本质是 将名字修饰成 _类名__名字
# 对外解决封装的方式
# 1.如果真的不想让外界访问,就不对外提供访问数据的方法
# 2.如果想让外界访问,可以对外提供访问数据的方法,方法具有逻辑,使用可以添加操作权限
class Test:
def __init__(self, name):
# __name只是对外隐藏,对内可见
self.__name = name
def get_name(self):
return self.__name
def set_name(self, name):
if 'sb' not in name: # 对数据的修改可能会产生数据的安全性问题,可以添加限制条件
self.__name = name
# 重点:封装的对外访问语法的优化
class User:
def __init__(self, name):
self.__name = name
@property # 将方法伪装成属性
def name(self):
return self.__name
@name.setter # 能为有伪装get方法的(方法)属性,再伪装set方法
def name(self, value):
self.__name = value
@name.deleter
def name(self):
del self.__name
# 总结:
# 1.对象没了,对象的属性也就没了,所以不需要属性 @名字.deleter
# 2.对外提供get方法是基础,@property,如果没有,外界不可读不可写
# 3.如果有@property,则可以 @名字.setter,有set,为可读可写,无set为只读
@property # 伪装的属性方法,不需要一定有 __开头 的名字与之对应
def pwd(self):
return '123456'
u1 = User('Owen')
print(u1.name) # 如果一个方法伪装成属性,对象.方法名 就会自动调用该方法
u1.name = 'Zero'
print(u1.name)
# del u1.name
# print(u1.name)
print(u1.pwd)
继承
1.什么是继承
继承就是一种新建类的方式,新建的类称为子类或者派生类,被继承的类称之为父类或者基类或者超类
子类会继承所有父类的属性和方法,即可以直接使用这些属性和方法
2.为什么要用继承
减少代码冗余
3.如何使用
class Parent1:
pass
class Parent2:
pass
class Son1(parent1):
pass
# python支持多继承,一个类可以有多个父类,但在java中只能有一个
class Son2(parent1,parent2):
pass
print(Son1.__bases__) # 查看当前类的所有的基类
print(Son2.__bases__)
# 那你是否会好奇想看看我们定义的Parent类它有没有偷偷摸摸的继承谁呢?
print(Parent1.__bases__)
print(Parent2.__bases__)
# 切换python解释器3.x >>> 2.x得出一个结论
"""
在python3中,如果没有显示地继承任何类,那默认继承object类
在python2中,如果没有显示地继承任何类,也不会继承object类
在python中类分为两种:
新式类:
但凡继承object的类,或者该类的子类都是新式类
>>>:在python3中所有的类都是新式类
经典类
没有继承object类,以及该类的子类都是经典类
也就意味着经典类和新式类这对儿概念只在python2中有
python3中只有新式类
4.基于继承减少代码冗余的案例+派生/衍生
对象与对象之间总结相似的特征与技能得到类
类与类之间总结相似的属性和特征得到父类
代码实例
import pickle
class OldboyStudent:
school = 'oldboy'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def choice_course(self):
print('%s is choosing course'%self.name)
def save(self):
with open(self.name,'wb') as f:
pickle.dump(self,f)
class OldboyTeacher:
school = 'oldboy'
def __init__(self,name,age,sex,level):
self.name = name
self.age = age
self.sex = sex
self.level = level
def score(self):
print('%s is score'%self.name)
def save(self):
with open(self.name,'wb') as f:
pickle.dump(self,f)
stu = OldboyStudent('alex',30,'male')
stu.save()
tea = OldboyTeacher('egon',18,'male',10)
tea.save()
# 回过头来看,上面的代码是否存在相似的部分。我们刚好学过解决类之间解决代码冗余的方式
class OldboyPeople:
school = 'oldboy'
def save(self):
with open(self.name, 'wb') as f:
pickle.dump(self, f)
# 初步继承类抽取,思考继承之后对象查找属性和方法的顺序
stu.save()
stu.school
# 刚刚只是讲属性和方法抽成了父类,但是init里面也有重复的代码,应该也可以抽取
class OldboyPeople:
school = 'oldboy'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def save(self):
with open(self.name, 'wb') as f:
pickle.dump(self, f)
# 先不考虑老师和学生init中不同的,先全部继承这个父类统一的init,发现也可以使用到父类的init
# 派生概念
# 在子类能够使用父类所有的属性和方法的同时,自身还有一些额外的属性和方法
# 小思考:如果派生的属性和方法恰巧和父类的一样,那在查找属性和方法的时候先找到谁呢? >>> 还是按查找顺序来
# 再回过头来看老师的init中有额外的level参数,不应该在父类中添加默认参数,只能自己重写init方法,但是又有重复的代码出现
def __init__(self,name,age,sex,level):
OldboyPeople.__init__(self,name,age,sex)
self.level = level
"""
在子类派生出的新方法中重用父类的方法
方式1:指名道姓访问,与继承没有任何关系
OldboyPeople.__init__(self,name,age,sex)
言外之意还有一种跟继承有关系的能够重用父类的方法的方式,先不着急说
5.继承原理
刚刚我们一直在讨论的是单继承下属性查找的顺序,那如果是多继承情况呢?
单继承测试
class Foo:
def f1(self):
print('Foo.f1')
def f2(self): #self=obj
print('Foo.f2')
self.f1() #obj.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj=Bar()
obj.f2()
多继承
# F(A,B,C) 无论是新式类还是经典类,都是从左往右挨个走到底 画图 切换python版本演示,记得加文件头coding:utf-8
# 2、多继承的属性查找“:对象自己-》对象的类-》从左往右一个一个的分支找下去
class D:
# def test(self):
# print('D')
pass
class E:
def test(self):
print('E')
class F:
def test(self):
print('F')
class A(D):
# def test(self):
# print('A')
pass
class B(E):
def test(self):
print('B')
class C(F):
def test(self):
print('C')
class G(A,B,C):
# def test(self):
# print('G')
pass
obj=G()
obj.test()
如果是菱型继承的话就不一样了(不考虑object)>>>广度优先!
#3、新式类:广度优先
class A(object):
def test(self):
print('from A')
class B(A):
# def test(self):
# print('from B')
pass
class C(A):
# def test(self):
# print('from C')
pass
class D(B):
# def test(self):
# print('from D')
pass
class E(C):
# def test(self):
# print('from E')
pass
class F(D,E):
# def test(self):
# print('from F')
pass
obj=F()
print(F.mro()) # 查找属性的顺序遵循mro列表(只有新式类中才有mro方法)
obj.test()
回过头再来看通过继承关系调用父类的方法
# 方式二:super(自己的类名,self).父类中的方法名()
# 调用super会得到一个特殊的对象,该对象是专门用来引用父类中的方法的,
# 具体的:该对象会严格按照当前类的MRO列表从当前类的父类中依次查找属性,即这种方式是严格依赖于继承的
# ps:在python3中可以简写为super()
class OldboyPeople:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,level):
# OldboyPeople.__init__(self,name,age,sex)
super(OldboyTeacher,self).__init__(name,age,sex)
self.level=level
tea1=OldboyTeacher('egon',18,'male',10)
print(tea1.name,tea1.level)
class A:
def f1(self):
print('A')
super().f2() # super()会基于当前所在的查找位置继续往后查找
def f2(self):
print('A')
class B:
def f2(self):
print('B')
class C(A,B):
def f2(self):
print('C')
obj=C()
print(C.mro())
obj.f1()
实际工作中要谨慎使用多继承,编程是解耦合,而继承则是强耦合
多态
1.什么是多态
同一种事物的多种形态(动物:人,猫,狗)
2.为何要用多态
多态性:指的是可以在不用考虑对象具体类型的前提下,直接调用对象的方法
3.如何使用多态
class Animal:
def talk(self):
pass
class People(Animal):
def talk(self):
print('say hello')
class Dog(Animal):
def talk(self):
print('汪汪汪')
class Pig(Animal):
def talk(self):
print('哼哼哼')
peo1=People()
dog1=Dog()
pig1=Pig()
# 不用考虑对象具体类型的前提下,直接调用对象的方法
peo1.talk()
dog1.talk()
pig1.talk()
"""
再来想车是不是有很多牌子,你去学车需要说专门学哪个牌子的车的驾驶方式吗?
"""
# 来你之前也一直在用多态性:不用考虑对象具体类型的前提下,直接调用对象的方法
l=list([1,2,3])
s=str('hello')
t=tuple((4,5,6))
l.__len__()
s.__len__()
t.__len__() # 我不需要考虑这三个具体是什么类型,只要是容器类型就都能调用len这个方法
# 再来看多态性能够实现的条件是什么?父类有的方法名子类也必须叫这个方法才行
class Animal:
def talk(self):
pass
class People(Animal):
def jiao(self):
print('say hello')
class Dog(Animal):
def han(self):
print('汪汪汪')
class Pig(Animal):
def hou(self):
print('哼哼哼')
# 多态性能实现的条件就是父类给子类定义了一个标准,动物都必须会叫,并且叫的方法都必须是talk
# 但是你现在能约束我说子类必须叫这个方法吗?
# 那有没有一种情况能够做到说子类必须按照父类定义的标准
import abc
class Animal(metaclass=abc.ABCMeta): # 父类存在的意义就是用来定义规范
@abc.abstractmethod
def talk(self):
pass
# Animal() # 抽象基类不能被实例化!!!
class People(Animal):
def jiao(self):
print('say hello')
class Dog(Animal):
def han(self):
print('汪汪汪')
class Pig(Animal):
def hou(self):
print('哼哼哼')
# 上面三个类 一实例化都会报错
# 但是python推崇的是自由,简约并不希望限制程序员的开发
# 鸭子类型:只要你长得像鸭子,说话像鸭子,那你就是鸭子!
class People:
def talk(self):
print('say hello')
class Dog:
def talk(self):
print('汪汪汪')
class Pig:
def talk(self):
print('哼哼哼')
# 再来看linux中:一切皆文件!
class Disk:
def read(self):
print('disk read')
def write(self):
print('disk write')
class Process:
def read(self):
print('process read')
def write(self):
print('processes write')
class Memory:
def read(self):
print('memory read')
def write(self):
print('memory write')
四 类中的装饰器
property
# 人体的BMI指数
"""
成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
体质指数(BMI)=体重(kg)/ 身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
"""
class People:
def __init__(self,name,height,weight):
self.name=name
self.height=height
self.weight=weight
@property
def bmi(self):
return self.weight / (self.height ** 2)
egon=People('egon',1.80,75)
egon.height=1.82
# print(egon.bmi())
print(egon.bmi)
了解
class People:
def __init__(self,name):
self.__name=name
@property
def name(self):
return self.__name
@name.setter
def name(self,val):
# print('=====>准备修改名字的值:',val)
if type(val) is not str:
raise TypeError('名字的值必须为str类型')
self.__name=val
@name.deleter
def name(self):
# del self.__name
print('不让删啊老铁')
# classmethod
# staticmethod
五 isinstance和issubclass
isinstance(obj,cls)检查是否obj是否是类 cls 的对象
class Foo(object):
pass
obj = Foo()
isinstance(obj, Foo)
issubclass(sub, super)检查sub类是否是 super 类的派生类
class Foo(object):
pass
class Bar(Foo):
pass
issubclass(Bar, Foo)
六 反射
1 什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
通过字符串来获取类或对象的属性或方法
反射:指的是通过字符串来操作类或者对象的属性
class People:
country='China'
def __init__(self,name):
self.name=name
obj=People('egon')
涉及四个内置函数
# hasattr
print('country' in People.__dict__)
print(hasattr(People,'country'))
# getattr
print(People.__dict__['country'])
print(getattr(People,'country'))
print(getattr(People,'country1111',None))
# setattr
People.__dict__['x']=111
print(People.x)
setattr(People,'x',111)
print(People.__dict__)
# delattr
delattr(People,'country')
print(People.__dict__)
应用
class Ftp:
def get(self):
print('get...')
def put(self):
print('put...')
def auth(self):
print('auth...')
def run(self):
while True:
cmd=input('>>: ').strip() #cmd='get'
if hasattr(self,cmd):
method=getattr(self,cmd)
method()
else:
print('输入的方法不存在')
obj=Ftp()
obj.run()
七 _str_和_repr_
改变对象的字符串显示str,repr
自定制格式化字符串format
#_*_coding:utf-8_*_
format_dict={
'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
}
class School:
def __init__(self,name,addr,type):
self.name=name
self.addr=addr
self.type=type
def __repr__(self):
return 'School(%s,%s)' %(self.name,self.addr)
def __str__(self):
return '(%s,%s)' %(self.name,self.addr)
def __format__(self, format_spec):
# if format_spec
if not format_spec or format_spec not in format_dict:
format_spec='nat'
fmt=format_dict[format_spec]
return fmt.format(obj=self)
s1=School('oldboy1','北京','私立')
print('from repr: ',repr(s1))
print('from str: ',str(s1))
print(s1)
'''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
print(format(s1,'nat'))
print(format(s1,'tna'))
print(format(s1,'tan'))
print(format(s1,'asfdasdffd'))
class B:
def __str__(self):
return 'str : class B'
def __repr__(self):
return 'repr : class B'
b=B()
print('%s'%b)
print('%r'%b)
%s和%r
八 item系列
__getitem__\__setitem__\__delitem__
class Foo:
def __init__(self,name):
self.name=name
def __getitem__(self, item):
print(self.__dict__[item])
def __setitem__(self, key, value):
self.__dict__[key]=value
def __delitem__(self, key):
print('del obj[key]时,我执行')
self.__dict__.pop(key)
def __delattr__(self, item):
print('del obj.key时,我执行')
self.__dict__.pop(item)
f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)
九 __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo:
def __del__(self):
print('执行我啦')
f1=Foo()
del f1
print('------->')
#输出结果
执行我啦
------->
十 __new__
class A:
def __init__(self):
self.x = 1
print('in init function')
def __new__(cls, *args, **kwargs):
print('in new function')
return object.__new__(A)
a = A()
print(a.x)
class Singleton:
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
cls._instance = object.__new__(cls)
return cls._instance
one = Singleton()
two = Singleton()
two.a = 3
print(one.a)
# 3
# one和two完全相同,可以用id(), ==, is检测
print(id(one))
# 29097904
print(id(two))
# 29097904
print(one == two)
# True
print(one is two)
单例模式
十一 __call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
十二 with和__enter__,__exit__
with语句
class A:
def __enter__(self):
print('before')
def __exit__(self, exc_type, exc_val, exc_tb):
print('after')
with A() as a:
print('123')
with和文件操作
class Myfile:
def __init__(self,path,mode='r',encoding = 'utf-8'):
self.path = path
self.mode = mode
self.encoding = encoding
def __enter__(self):
self.f = open(self.path, mode=self.mode, encoding=self.encoding)
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
with Myfile('file',mode='w') as f:
f.write('wahaha')
with和pickle
import pickle
class MyPickledump:
def __init__(self,path):
self.path = path
def __enter__(self):
self.f = open(self.path, mode='ab')
return self
def dump(self,content):
pickle.dump(content,self.f)
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
class Mypickleload:
def __init__(self,path):
self.path = path
def __enter__(self):
self.f = open(self.path, mode='rb')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
def load(self):
return pickle.load(self.f)
def loaditer(self):
while True:
try:
yield self.load()
except EOFError:
break
# with MyPickledump('file') as f:
# f.dump({1,2,3,4})
with Mypickleload('file') as f:
for item in f.loaditer():
print(item)
with和pickle和iter
import pickle
class MyPickledump:
def __init__(self,path):
self.path = path
def __enter__(self):
self.f = open(self.path, mode='ab')
return self
def dump(self,content):
pickle.dump(content,self.f)
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
class Mypickleload:
def __init__(self,path):
self.path = path
def __enter__(self):
self.f = open(self.path, mode='rb')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
def __iter__(self):
while True:
try:
yield pickle.load(self.f)
except EOFError:
break
# with MyPickledump('file') as f:
# f.dump({1,2,3,4})
with Mypickleload('file') as f:
for item in f:
print(item)
十三 __len__
class A:
def __init__(self):
self.a = 1
self.b = 2
def __len__(self):
return len(self.__dict__)
a = A()
print(len(a))
十四 __hash__
class A:
def __init__(self):
self.a = 1
self.b = 2
def __hash__(self):
return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))
十五 __eq__
class A:
def __init__(self):
self.a = 1
self.b = 2
def __eq__(self,obj):
if self.a == obj.a and self.b == obj.b:
return True
a = A()
b = A()
print(a == b)
十六 元类
1、什么是元类?
基于python的宗旨:一切皆对象。而对象都是由类实例化得到的
class OldboyTeacher(object):
school = 'oldboy'
def __init__(self,name):
self.name = name
def run(self):
print('%s is running'%self.name)
t1 = OldboyTeacher('jason')
# 对象t1是由类OldboyTeacher实例化得到
那么类也是对象,它又是谁实例化得到的呢?
# 分别查看对象t1和OldboyTeacher的类型
print(type(t1))
print(type(OldboyTeacher))
# 结果为:
<class '__main__.OldboyTeacher'>
<class 'type'>
结论1:元类就是产生类的类,默认情况下type就是所有类的元类
2、不依赖于class关键字创建类
根据第一个结论我们能理出两条对应关系
1.调用元类得到自定义的类
2.调用自定义的类得到自定义的类的对象
现在我们来看第一对关系,调用元类来得到自定义的类,都需要哪些参数(OldboyTeacher=type(…),括号内传什么?)
我们自定义一个类的时候都有哪些关键的组成部分:
1.类名
2.类的父类
3.类的名称空间
就以第一阶段的OldboyTeacher类为例,calss关键字创建自定义类的步骤
1.获取类名(OldboyTeacher)
2.获取类的父类(object,)
3.执行类体代码获取产生的名称空间(如何获取???)
4.调用元类得到自定义类OldboyTeacher = type(class_name,class_bases,{...})
知识点补充:
如何执行一段字符串内部的代码并将产生的名称空间交给对应的参数? >>> exec()
class_body = """
school = 'oldboy'
def __init__(self,name):
self.name = name
def run(self):
print('%s is running'%self.name)
"""
class_dic = {}
class_global = {}
exec(class_body,class_global,class_dic)
# class_global一般情况下都为空,除非在字符串代码内部用global关键字声明,才会将产生的名字丢到class_global全局名称空间中
print(class_dic)
{'school': 'oldboy', '__init__': <function __init__ at 0x000000B5D2771EA0>, 'run': <function run at 0x000000B5DB5B7400>}
有了这个exec方法后,我们就可以不依赖于calss关键字创建自定义类
# 类名
class_name = 'OldgirlTeacher'
# 类的父类
class_bases = (object,) # 注意必须是元祖,逗号不能忘
# 名称空间
class_body = """
school = 'oldgirl'
def __init__(self,name):
self.name = name
def run(self):
print(self.name)
"""
class_dic = {}
exec(class_body,{},class_dic)
#调用元类创建自定义类
OldgirlTeacher = type(class_name,class_bases,class_dic)
print(OldgirlTeacher)
# 结果为:<class '__main__.OldgirlTeacher'>
# 并且它可以访问自身的属性和方法,并实例化产生对象
print(OldgirlTeacher.school)
print(OldgirlTeacher.run)
# 结果为:
"""
oldgirl
<function run at 0x000000229B157378>
"""
obj = OldgirlTeacher('jason')
print(obj.school)
obj.run()
"""
oldgirl
jason
"""
3、自定元类控制类的创建过程
3.1如何自定义元类
class Mymeta(type): # 必须是继承了type的类才是自定义元类
pass
class oldboyTeacher(metaclass=Mymeta): # 通过metaclass可以指定类的元类
school = 'oldboy'
def __init__(self,name):
self.name = name
def run(self):
print('%s is running'%self.name)
3.2 __call__
思考:一个类的对象加括号调用会执行该对象父类中的__call__
方法,那么类也是对象,它在加括号实例化对象的时候,是不是也应该走它父类的__call__
方法?
class Mymeta(type):
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
class OldboyTeacher(object,metaclass=Mymeta):
school = 'oldboy'
def __init__(self,name):
self.name = name
def run(self):
print('%s is running'%self.name)
obj = OldboyTeacher('jason')
"""
打印结果:
<class '__main__.OldboyTeacher'>
('jason',)
{}
"""
思考:类加括号实例化对象的时候,有哪几个步骤?
1.创建一个该类的空对象
2.实例化该空对象
3.将实例化完成的空对象返回给调用者
# 也就是说__call__里面需要做三件事
class Mymeta(type):
def __call__(self, *args, **kwargs):
# 1.产生空对象
# 2.初始化对象
# 3.返回该对象
# 那我先做最后一件事,返回一个123,发现
return 123
obj = OldboyTeacher('jason')
print(obj)
# 结果就是123
那接下来就需要我手动去干这三件事了
class Mymeta(type):
def __call__(self, *args, **kwargs):
# 1.产生一个空对象
obj = self.__new__(self)
# 2.实例化该对象
self.__init__(obj,*args,**kwargs)
# 3.返回该对象
return obj
# 关于这个__new__,我们是不是不知道是个啥,我这里直接告诉你,它就是用来创建空对象的
思考:这是类加括号产生对象的过程,那么我元类加括号产生类的过程是不是也应该是这个三步
1.产生一个空对象(指类)
2.实例化该空对象(实例化类)
3.将实例化完成的类对象返回
那依据上面的推导,self.__new__
就是关键了,我可以在我的自定义元类里面定义一个__new__
方法,看看它到底是个啥
class Mymeta(type):
def __new__(cls, *args, **kwargs):
print(cls)
print(args)
print(kwargs)
class OldboyTeacher(object,metaclass=Mymeta):
school = 'oldboy'
def __init__(self, name):
self.name = name
def run(self):
print('%s is running' % self.name)
"""
<class '__main__.Mymeta'>
('OldboyTeacher', (object,), {'__module__': '__main__', '__qualname__': 'OldboyTeacher', 'school': 'oldboy', '__init__': <function OldboyTeacher.__init__ at 0x000000323CEB9510>, 'run': <function OldboyTeacher.run at 0x000000323CEE7158>})
{}
"""
我们发现__new__
里面的*args
参数接收到了三个位置参数,并且很容易辨认它们对应的就是类名,类的父类,类体代码执行后的名称空间
那么我们可不可以将__new__()
的形参换一种写法
class Mymeta(type):
def __new__(cls, class_name,class_bases,class_dic):
print(class_name)
print(class_bases)
print(class_dic)
# 这里需要记住的是,必须在最后调用元类type中的__new__方法来产生该空对象
return type.__new__(cls,class_name,class_bases,class_dic)
class OldboyTeacher(metaclass=Mymeta):
school = 'oldboy'
def __init__(self,name):
self.name = name
def run(self):
print('%s is running'%self.name)
验证:
class Mymeta(type):
def __new__(cls, class_name,class_bases,class_dic):
print(class_name)
print(class_bases)
print(class_dic)
class_dic['xxx'] = '123'
if 'school' in class_dic:
class_dic['school'] = 'DSB'
return type.__new__(cls,class_name,class_bases,class_dic)
class OldboyTeacher(metaclass=Mymeta):
school = 'oldboy'
def __init__(self,name):
self.name = name
def run(self):
print('%s is running'%self.name)
print(OldboyTeacher.xxx) # 发现可以打印出来 123
print(OldboyTeacher.school) # DSB
结论:
由此我们就可以通过自定义元类,并重写new方法来拦截类的创建过程,在类被创建出来之前进行一系列其他操作