未设置对象变量或with块变量_27-面向对象高级-魔法方法

本文详细讲解了Python中`__slots__`节省内存、控制属性,`__all__`导入模块限制,`__iter__`和`__next__`实现迭代,以及`__enter__`和`__exit__`实现上下文管理。涵盖了对象属性管理、内存优化和编程模式应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

e82333233aa3c74b1a129edff0a666ca.png

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值