
1、__slots__
和__dict__
1.1、什么是slots
(1)slots是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性) (2)使用点来访问属性本质就是在访问类或者对象的dict属性字典(类的字典是共享的,而每个实例的是独立的)
1.2、为什么使用slots
(1)字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用slots取代实例的dict,对象的dict不可用
(2)当你定义slots后,slots就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在slots中列出的属性名在内部被映射到这个数组的指定小标上。使用slots一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在slots中定义的那些属性名。
对象只拥有name和age属性,其它放不进去
class Person:
__slots__ = ['name', 'age'] # 对象只拥有name和age属性
#__slots__ = 'name'
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name
p = Person('lqz', 18)
print(p.__dict__)#AttributeError: 'Person' object has no attribute '__dict__'
#一旦使用了slots,对象就没有自己的dict了
print(p.age) #18
p.sex = '男' #报错
Person.sex = '男' #放到类的字典中
print(p.sex)
# slots的实际应用场景
(1)限制对象的属性
(2)节省内存(对象很多,但属性很少,使用它,节省内存)
2、__all__
和__len__
导入模块使用
__all__导入模块时,限制能够使用的属性
#test.py
__all__=['age','name']
age=10
name='longge'
sex='男'
#run.py
print(age)
print(name)
print(sex) #NameError: name 'sex' is not defined
__len__:计算对象长度,返回几,len(对象),结果就是几
class Foo:
def __init__(self,name,age):
self.name=name
self.age=age
def __len__(self):
return len(self.__dict__)
# return 5 返回了一个固定的长度
f=Foo('lqz',19)
# print(f.__dict__)
print(len(f)) #2
3、__iter__
和__next__
链式调用(实现迭代器)
链式调用(跟语言无关)
f.test().test2().test3() # java,js:中广泛应用,设计程序的模式
class Foo:
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def print_name(self):
print(self.name)
return self
def print_age(self):
print(self.age)
return self
def print_sex(self):
print(self.sex)
return self
f=Foo('lqz',16,'男')
f.print_name()
f.print_sex()
f.print_age()
# # 链式调用
f.print_name().print_sex().print_age()
给一个类,重写__iter__
和__next__
,死循环
# 自定制可迭代对象(自己写的对象,可以迭代,可以被for循环)
class Foo:
def __init__(self):
self.__x = 1
def __iter__(self):
return self
def __next__(self):
self.__x += 1
return self.__x
f = Foo() # f就是一个可迭代对象,所以他就能够被for 循环
for i in f: # f.__iter__()----->每循环一次,调用一下 f.__next__()
print(i)
加入StopIteration异常,for循环该对象,可以停止
# 加入异常,for循环该对象,可以停止
class Foo:
def __init__(self,start,end):
self.__start = start
self.__end=end
def __iter__(self):
return self
def __next__(self):
self.__start+=1
if self.__start>=self.__end:
raise StopIteration('') # 抛出一个异常,程序就结束了
return self.__start
for i in Foo(1,20):
print(i)
模拟range
class Range:
def __init__(self, n, stop, step):
self.n = n
self.stop = stop
self.step = step
def __next__(self):
if self.n >= self.stop:
raise StopIteration
x = self.n
self.n += self.step
return x
def __iter__(self):
return self
for i in Range(1, 7, 3):
print(i)
斐波那契数列
# 斐波那契数列(通过__iter__和__next__实现)
class Fib:
def __init__(self, count):
self.start = 0
self.second = 1
self.count = count
def __iter__(self):
return self
def __next__(self):
self.start,self.second=self.second,self.start+self.second
if self.start >= self.count:
raise StopIteration()
self.f.close()
for i in Fib(100):
print(i)
4、__hash__
哈希运算
__hash__ ,hash(对象)触发执行,返回什么就是什么
(1) 可变类型list、dict、set,不可hash
(2)不可变类型int、float、complex、str、tuple,可以hash
#print(object.__hash__([1,2,3,4])) 可以被hash
#如果一个对象不可hash,通过重写 __hash__让它变得可hash
class Foo:
def __init__(self):
self.name='lqz'
self.l=['1',3,4]
def __hash__(self):
return hash(self.l)
return 55 #hash返回的值是必须是一个整数,就可以实现
f=Foo()
print(hash(f.l)) #TypeError: unhashable type: 'list'
5、__eq__
比较两个值是否相等
两个对象 == 比较的时候,触发 __eq__的执行,在内部,自定制比较规则即可
#注意其中只要有一个class对象都会触发__eq__的执行
l=[1,3,4]
l2=[1,3,4]
print(l==l2) #True
print(l is l2) #False
class Foo:
def __str__(self):
return self.name
def __init__(self):
self.name='lqz'
def __eq__(self, other):
# print(self)
# print(other)
# 自己定制比较规则
if self.name ==other.name:
return True
else:
return False
f1=Foo()
print(f1) #lqz
# f1.name='longge'
f2=Foo()
print(f2) #lqz
print(f1==f2) #True 比较的是两个值
print(f1 is f2) #False
6、__enter__
和__exit__
实现文件上下文管理
with open() as f:
with 上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明enter和exit方法
上下文管理协议
class Open:
def __init__(self, name):
self.name = name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
# return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
with Open('a.txt') as f:
print('=====>执行代码块') #触发__exit__的执行
# print(f,f.name)
#输出结果
出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
=====>执行代码块
with中代码块执行完毕时执行我啊
exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行(除非return True)
1、exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
2、如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
(1)exc_type--->异常类型
(2)exc_val --->异常值
(3)exc_tb --->追溯信息
class Open:
def __init__(self, name):
self.name = name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') #1、打印输出信息
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊') #3、打印__exit__print信息
print(exc_type) #4、<class 'AttributeError'>
print(exc_val) #5、***着火啦,救火啊***
print(exc_tb) #6、<traceback object at 0x00000219822BD080>
return True
try:
with Open('a.txt') as f:
print('=====>执行代码块') #2、打印print信息
raise AttributeError('***着火啦,救火啊***') #7、***着火啦,救火啊***
print('请叫我longge') #8、return True打印输出结果
except Exception as e:
print(e)
模拟open
class Open:
def __init__(self, filepath, mode='r', encoding='utf-8'):
self.filepath = filepath
self.mode = mode
self.encoding = encoding
def __enter__(self):
# print('enter')
self.f = open(self.filepath, mode=self.mode, encoding=self.encoding)
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
# print('exit')
self.f.close()
return True
def __getattr__(self, item):
return getattr(self.f, item)
with Open('a.txt', 'w') as f:
print(f)
f.write('aaaaaa')
f.wasdf #抛出异常,交给__exit__处理
# 后期会使用with ,处理数据库链接,redis链接
优点
(1)使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预 (2)在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在exit中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
7、本节总结
1 __slots__:
-控制对象的属性(__dict__没了)
-对象很多,对象属性比价少
-本质:对象没有__dict__,统一被类的__dict__管理起来了,在dict内部使用更紧凑 的存储方式,节约内存空间
2 __all__:
-在使用from xx import * 导入模块时,只能导入__all__指定的属性
-__all__=['name','age']
3 __iter__和__next__
-可迭代对象:对象有__iter__方法,这个对象就是一个可迭代对象
-for循环本质就是在循环可迭代对象
-for i in 对象(必须是可迭代对象)
-for循环的本质:先调用对象的__iter__得到一个迭代器对象,然后调用迭代器
对象的__next__ 方法,每次获得一个值(next这种获取值的方式不依赖于索引,又叫迭代循环)
-迭代器对象:
-既有__iter__又有__next__的对象,就是迭代器对象
-迭代器对象一定是可迭代对象
-定制自己的可迭代对象
-重写类中的__iter__和__next__
-让它支持for循环直接循环(做出一种不依赖于索引取值的方式)
-不停的return 值,当要停下时,抛出异常
4 __len__
-len(对象)----》触发对象的__len__()
5 __hash__
-字典类型---》hash类型---》key值必须可hash
-可变类型(字典,列表),不可变类型(数字,字符串,元组)
-不可变类型可hash,可以作为字典的key
-重写自己定制的类的hash方法,使对象可以hash
6 上下文管理器
-with:上下文管理协议
-with 对象 as f: # 处理异常,不被with管理后,会清理资源
代码
代码
-咱们学过的 open,支持上下文管理协议
-我们自定制一些对象,支持with管理
-在类中重写 __enter__和__exit__
-with 你写的对象 as f:
# 触发对象的__enter__ 方法执行,做一些初始化的工作,返回一个对象, 会给f
# 写代码,可以有异常,不用咱们手动处理异常
-脱离了with的管理,会执行对象的__exit__,处理异常,资源清理工作
-数据库操作,redis操作
扩展阅读:(字典底层是如何存储,为什么有序)https://www.cnblogs.com/xiaoyuanqujing/articles/12008689.html