摘要:__new__ __del__ __call__ __len__ __eq__ __hash__
import json 序列化模块
import pickle 序列化模块 补充:
现在我们都应该知道一个对象的类型(type)就是创建这个对象的类,
而类也是被创建出来的,那么类的类型(type)也是创建这个类的类型,
创建类的是type元类,所以没有指定mateclass的类的type就是type,
指定了mateclass的类的type就是指定的mateclass的值。 例如:
from abc import ABCMeta class A:
pass class B:
pass class C(metaclass=ABCMeta):
pass print(type(A)) # <class 'type'>
print(type(B)) # <class 'type'>
print(type(C)) # <class 'abc.ABCMeta'> 一、内置方法
0、__dict__ 方法
class Foo:
f1 = "类属性"
def __init__(self):
self.obj = "对象属性" # 查看类的__dict__和对象的__dict__
obj = Foo()
print(Foo.__dict__) # {'__model__': '__main__', 'f1': '类属性', '__init__': <function Foo.__init__ at 0x00000018C6D3D59D8> ....}
print(obj.__dict__) # {'obj': '对象属性'}
由此可以看出:类的__dict__会把类的所有属性对应的值和方法的地址以字典的形式打印出来
对象的__dict__只把对象的属性以字典的形式打印出来,即只打印实例化对象时候__init__里面初始化的值
class BaseResponse(object): def __init__(self):
self.code = None
self.data = None
self.error = None @property
def dict(self):
return self.__dict__ # 这样构建的类,在写接口的时候就不需要频繁构建字典了
# 例如:返回错误的信息
res = BaseResponse()
res.code = 500 # 错误状态码
res.error = "错误的信息"
print(res.dict) # 在接口中实际上是 return Response(res.dict) # 返回成功处理的数据
res = BaseResponse()
res.code = 200
res.data = {'成功返回的数据': 'xxxx'}
print(res.dict) # 在接口中实际上是 return Response(res.dict)
应用于接口
1、__new__ 构造方法
__new__:在__init__执行之前,实例化对象的第一步是__new__创建一个对象空间。 继承自object的新式类才有__new__
__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供,
__new__必须要有返回值,返回的是实例化出来的实例,
可以return父类__new__出来的实例,或者直接是object的__new__出来的实例,
但是如果__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行。
return的类是谁,就创建谁的对象,但是在本类中如果return的不是本类的对象,那么不会执行__init__。
# 正常的写法:
class Father:
def __init__(self):
print('执行了init') def __new__(cls, *args, **kwargs):
print('执行了new')
return object.__new__(cls)
# return super().__new__(cls) f = Father() # 执行了new 执行了init # 不return本类的对象
class A:
name = 'a'
def __init__(self):
print("A--->init") class B:
name = 'b'
def __init__(self):
print("B--->init") def __new__(cls):
print("new %s" % cls)
# return object.__new__(cls) #正常返回当前类的实例,会执行当前类的init
# return super().__new__(cls) # 整个结果:
# new <class '__main__.B'>
# B--->init
# <class '__main__.B'>
# b # return object.__new__(A) # 返回A的实例,当前类和A的init都不会执行
# 整个结果:
# new <class '__main__.B'>
# <class '__main__.A'>
b = B() # a
print(type(b))
print(b.name) 单例模式:一个类只有一个实例的时候
class Singleton:
__instance = None
def __init__(self,name,age):
self.name = name
self.age = age def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = object.__new__(cls)
return cls.__instance f1 = Singleton('小宁',18)
print(f1) # <__main__.Singleton object at 0x000001ECCB1FFA90> f2 = Singleton('小白',25)
print(f2) # <__main__.Singleton object at 0x000001ECCB1FFA90> 都是第一次创建的f1的地址 print(f1.name,f1.age) # 小白 25
print(f2.name,f2.age) # 小白 25 2、__del__ 析构方法
析构方法 : 在删除这个类创建的对象的时候会先触发这个方法,再删除对象,
方法内应该做一些清理工作,比如说关闭文件,关闭网络的链接,数据库的链接 class Father:
def __init__(self):
self.file = open('file',encoding='utf-8',mode='w') def My_write(self):
self.file.write('哈哈哈') def __del__(self):
self.file.close()
print('执行了del') f = Father()
del f # 执行了del
注意:重写__del__后,即使不写 def f 在函数执行完毕后,也会自动执行__del__方法 3、__call____call__()
的作用是使实例(对象)能够像函数一样被调用,同时不影响实例本身的生命周期(__call__()
不影响一个实例的构造和析构)。但是__call__()
可以用来改变实例的内部成员的值。
class A:
def __init__(self,x,y):
self.x = x
self.y = y def __call__(self,a,b):
self.x = a
self.y = b a = A(1,2)
print(a.x,a.y) # 1 2 a(3,4)
print(a.x,a.y) # 3 4
4、__len__ len() # 内置函数len可以接收的数据类型:list dict set str tuple
print('__len__' in dir(list)) # True
print('__len__' in dir(dict)) # True
print('__len__' in dir(set)) # True
print('__len__' in dir(tuple)) # True
print('__len__' in dir(str)) # True print('__len__' in dir(int)) # False
print('__len__' in dir(float)) # False # 从上面可以看出,能使用内置函数len的数据类型,内部都有__len__方法
# 之前说过我们常用的数据类型,其实也是对象,那么可以得出结论:
# 只要类中有__len__方法,那么这个类的对象就可以使用内置函数len class A:
def __len__(self):
return 1
a = A()
print(len(a)) # 1 值是__len__的返回值 class Father:
def __init__(self,s):
self.s = s def __len__(self):
return len(self.s) f = Father('你好啊')
print(len(f)) # 5、__eq__ :重写了类的 == 方法,遇到 == 运算符则自动触发
默认的__eq__不重写的话,比较的是两个对象的内存地址是否相同: class Staff:
def __init__(self,name,sex):
self.name = name
self.sex = sex s1 = Staff('小明',18)
s2 = Staff('小明',18)
s3 = Staff('小东',18) print(s1==s2) # False
print(s1==s3) # False 可以自己重写,让其比较值是否相等
class Staff:
def __init__(self,name,sex):
self.name = name
self.sex = sex def __eq__(self, other):
return self.__dict__ == other.__dict__ #对象的所有内容相同,则认为值相同 s1 = Staff('小明',18)
s2 = Staff('小明',18)
s3 = Staff('小东',18) print(s1==s2) # True
print(s1==s3) # False 6、__hash__
类的对象可以哈希
class Foo():
pass obj1 = Foo()
obj2 = Foo()
print(hash(obj1)) # 内存地址 97649779257
print(hash(obj2)) # 内存地址 97649779271 哈希算法:
1.每次执行hash值都会变化(因为会根据地址进行哈希)
2.但是在一次执行的过程中对同一个值的hash结果总是不变的
3.对于相同的值在一次程序的运行中是不会变化的
4.对于不同的值在一次程序的运行中总是不同的 字典为什么寻址快
因为:
字典的键是不可变的数据类型,所以是可哈希的,
字典在存入内存的时候会将你的所有的key先进行哈希,再将哈希值存入内存中,
这样在查询的时候可以根据哈希值直接就可以找到,所以查询速度很快! set的去重机制
1.对每一个元素进行hash计算出一个地址
2.到这个地址对应的内存上查看
如果这块内存中没有值
将这个元素存到对应的内存地址上
如果这块内存中已经有值
判断这两个值是否相等
如果相等,就舍弃后面的值
如果不相等,就二次寻址再找一个新的空间来存储这个值 例如:100个对象,如果水果名相同的就只留一个
class Fruit:
def __init__(self,name,price):
self.name = name
self.price = price def __hash__(self):
return hash(self.name) def __eq__(self, other):
return self.name == other.name obj_lst = []
name_lst = ['苹果','香蕉','葡萄','榴莲']
for i in range(100):
name = name_lst[i % 4]
obj = Fruit(name,i)
obj_lst.append(obj)
#利用set的去重机制
ret = set(obj_lst) # 1、set对每一个元素进行hash计算出一个地址(此时触发__hash__)
# 2、查看每个地址的值是否相等(此时触发__eq__),相等的,set内部会自动去重。 for i in ret:
print(i.name,i.price) 二、序列化模块
模块:别人写好的功能放在一个文件里
内置模块 : 安装python解释器的时候一起装上的
第三方模块、扩展模块 :需要自己安装
自定义模块 : 你写的py文件 序列化模块
什么叫序列化
把一个数据类型转换成字符串、byets类型的过程就是序列化 为什么要把一个数据类型序列化?
{'name':'小明','sex':'male'}
当你需要把一个数据类型存储在文件中
当你需要把一个数据类型通过网络传输的时候 1、json序列化模块(换成字符串类型)
json的优点
所有的语言都通用,可以跨平台或跨语言进行数据交互
把数据类型(字典,列表)转成字符串形式,可以直接读写文件
转成的字符串都是以双引号的形式 缺点
只支持非常少的数据类型
只支持元组(会转成列表)、数字、字符串、列表、字典
应用场景
在网络操作中,以及多语言环境中,要传递字典、数字、字符串、列表等简单的数据类型的时候使用
对数据类型的约束很苛刻
字典的key必须是字符串或数字,但是如果key是数字,则会把数字转成字符串作为key
中文的值dumps后是字节形式的字符串 序列化:
dumps(dic/list) dic/list --> str 序列化方法
loads(str) str --> dic/list 反序列化方法
dump(dic/lst,f) dic/list --> 文件 序列化方法
load(f) 文件 --> dic/list 反序列化方法 多次dump进入文件中的数据load会报错
参数:ensure_ascii=False 希望序列化的中文能以中文的形式被显示并且写到文件中 import json
1-1、字典:
dic = {'name':'小明','sex':'male'}
ret = json.dumps(dic) # 序列化的过程:字典--->字符串
print(dic,type(dic)) # {'name': '小明', 'sex': 'male'} <class 'dict'>
print(ret,type(ret)) # {"name": "\u5c0f\u660e", "sex": "male"} <class 'str'> d = json.loads(ret) # 反序列化:字符串--->字典
print(d,type(d)) # {'name': '小明', 'sex': 'male'} <class 'dict'> 1-2、列表:
lst = [1,2,3,4,'aaa','bbb']
ret = json.dumps(lst) # 序列化的过程:列表--->字符串
print(lst,type(lst)) # [1, 2, 3, 4, 'aaa', 'bbb'] <class 'list'>
print(ret,type(ret)) # [1, 2, 3, 4, "aaa", "bbb"] <class 'str'> l = json.loads(ret) # 反序列化的过程:字符串--->列表
print(l,type(l)) # [1, 2, 3, 4, 'aaa', 'bbb'] <class 'list'> 1-3、元组:会被转成列表的形式,且转不回来
t1 = (1,2)
ret = json.dumps(t1)
print(ret,type(ret)) # [1, 2] <class 'str'> t2 = json.loads(ret)
print(t2,type(t2)) # [1, 2] <class 'list'> 1-4、key为数字的字典:会把数字转成字符串作为key,且转不回来
dic = {'name':'小明',1:(1,2)}
ret = json.dumps(dic)
print(dic,type(dic)) # {'name': '小明', 1: (1, 2)} <class 'dict'>
print(ret,type(ret)) # {"name": "\u5c0f\u660e", "1": [1, 2]} <class 'str'> d = json.loads(ret)
print(d,type(d)) # {'name': '小明', '1': [1, 2]} <class 'dict'> 1-5、单独的数字:无异常
a = 10
b = json.dumps(a)
print(b,type(b)) # 10 <class 'str'> ret = json.loads(b)
print(ret,type(ret)) # 10 <class 'int'> 1-6、字符串(等于没转,只是把单引号变成双引号):
a = 'aa'
b = json.dumps(a)
print(b,type(b)) # "aa" <class 'str'> ret = json.loads(b)
print(ret,type(ret)) # aa <class 'str'> 1-7、loads可以单独使用:
ret = '{"name": "hello", "sex": "male"}'
dic2 = json.loads(ret) # 反序列化
print(dic2,type(dic2)) # {'name': 'hello', 'sex': 'male'} <class 'dict'> 1-8、dumps的参数ensure_ascii:把中文正常显示出来,并不是字节形式
dic = {'name': '小明', 'sex': 'male'}
str1_dic = json.dumps(dic,ensure_ascii=False)
str2_dic = json.dumps(dic)
print(str1_dic) # {"name": "小明", "sex": "male"}
print(str2_dic) # {"name": "\u5c0f\u660e", "sex": "male"} 1-9、json读写文件:
普通的读写,dumps loads
简洁读写 dump load 普通的读写:写入时先dumps,读出时先loads
dic = {'name': '小明', 'sex': 'male'}
str = json.dumps(dic)
with open('json_file','w',encoding='utf-8') as f:
f.write(str) with open('json_file',encoding='utf-8',mode='r') as f:
content = f.read()
ret = json.loads(content)
print(ret) 读写方法:dump load,这两个方法是完全和文件打交道的
dump(内容,文件句柄,ensure_ascii) dic = {'name': '小明', 'sex': 'male'}
with open('json_file',encoding='utf-8',mode='w') as f:
json.dump(dic,f,ensure_ascii=False) load(文件句柄) with open('json_file',encoding='utf-8',mode='r') as f:
dic = json.load(f)
print(dic) 1-10、可以多次向一个文件中dump,但是不能多次load。
dic = {'name': '小明', 'sex': 'male'}
with open('json_file',encoding='utf-8',mode='w') as f:
json.dump(dic,f,ensure_ascii=False)
json.dump(dic,f,ensure_ascii=False)
json.dump(dic,f,ensure_ascii=False) # 可以多次dump with open('json_file',encoding='utf-8',mode='r') as f:
dic = json.load(f) # 但是不能多次load。
print(dic) 总结:
一次dump,可以用load或者loads读出内容
多次dump,无论是load或者loads都会报错(但是其实可以用for循环读出) 1-11、多次向一个文件写入
def my_dumps(dic):
with open('json_file',encoding='utf-8',mode='a') as f:
dic_str = json.dumps(dic)
f.write(dic_str + '\n')
dic1 = {'name':'小明','sex':'male'}
dic2 = {'name':'晓东','sex':'male'}
dic3 = {'name':'李三','sex':'male'}
my_dumps(dic1)
my_dumps(dic2)
my_dumps(dic3) with open('json_file',encoding='utf-8',mode='r') as f:
for line in f:
dic = json.loads(line.strip())
print(dic) 1-12、json格式化
import json # dumps
data = {'name':['小明','张三'],'sex':'male','age':19}
json_dic = json.dumps(data,sort_keys=True,indent=4,separators=(',',':'),ensure_ascii=False)
print(json_dic) # dump
data = {'name':['小明','张三'],'sex':'male','age':19}
with open('json_file',encoding='utf-8',mode='w') as f:
json.dump(data,f,sort_keys=True,indent=4,ensure_ascii=False)
# sort_keys=True:按照ASCII排序(a-z)
# indent=4:设置缩进格数,每次换行空四个格
# separators=(',',':'):设置分隔符,假如在dic = {'a': 1, 'b': 2, 'c': 3}这行代码里可以看到冒号和逗号后面都带了个空格,
# 这也是因为Python的默认格式也是如此,如果不想后面带有空格输出,那就可以设置成separators=(',', ':'),
# 如果想保持原样,可以写成separators=(', ', ': ')。
# ensure_ascii=False:中文以中文形式显示 2、pickle序列化模块(换成byets类型):
应用场景:
pickle模块实现了基本的数据序列和反序列化,和json的功能类似。
通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储,也可以简单的将字符进行序列化
通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象,也可以将字符进行反序列化。
和json不同的是:json 更适合跨语言 可以处理字符串,基本数据类型;pickle是python专有,更适合处理复杂类型的序列化 具体应用:
dumps 和loads 用于python对象和字符串间的序列化和反序列化
dumps 和json.dumps功能一样,但是以字节对象形式返回封装的对象
loads和json.loads功能一样,从字节对象中读取被封装的对象,并返回 2-1、dumps、loads
import pickle dic = {'name':'小明','sex':'male',1:('a','b')}
ret = pickle.dumps(dic) # 序列化的过程:字典--->bytes
print(ret,type(ret)) #b'\x80\x03}q\x00(X\x04\x00\'<class 'bytes'> dic2 = pickle.loads(ret) # 反序列化:bytes--->字典
print(dic2,type(dic2)) # {'name': '小明', 'sex': 'male', 1: ('a', 'b')} <class 'dict'> class Course():
def __init__(self,name):
self.name = name python = Course('python')
ret = pickle.dumps(python) # 序列化的过程:对象--->bytes
print(ret) # b'\x80\x03c__main__\nCourse\nq\x00)\x00pythonq\x04sb.' p = pickle.loads(ret) # 反序列化的过程:bytes--->对象
print(p) # <__main__.Course object at 0x0000028FE9583AC8>
print(p.name) # python # 读写文件的方法dump和load
with open('pickle',mode='wb') as f:
pickle.dump(python,f) with open('pickle',mode='rb') as f1:
print(pickle.load(f1)) pickle:dump、load
操作文件文件必须以b模式进行操作
在load的时候 如果这个要被load的内容所在的类不在内存中,会报错 *****
pickle支持多次dump和多次load(需要异常处理) import pickle
class Fruit:
def __init__(self,name):
self.name = name apple = Fruit('苹果')
banana = Fruit('香蕉') def my_dump(fruit):
with open('pickle','ab') as f:
pickle.dump(fruit,f)
my_dump(apple)
my_dump(banana) with open('pickle','rb') as f:
while True:
try:
content = pickle.load(f)
print(content.name)
except EOFError:
break