5 魔法方法 5-1init 和 del 【重点】
为什么叫魔法方法? 只有在某种条件下才触发,就像魔法一样
__init__
:当你定义的类,被实例化的时候,自动执行,给对象初始化属性,这个方法里面放self.xxx属性=xxx 然后这个xxx由init方法入参,所以你在实例化对象的时候,就需要传这个参数进去,这个参数就是对象的属性
这个 __init__
默认是返回None的,默认不写
这里要说一下__del__
析构方法,当对象被回收的时候出发执行(程序结束、对象引用技术为0称为垃圾时) 这里要回想下python的垃圾回收机制的一种“引用计数”,就是当引用计数为0的时候,会被定义为垃圾,会被回收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Student : def __init__ (self, name, age ): print ("init..." ) self.name = name self.age = age return class Student : def __init__ (self, name, age ): self.name = name self.age = age def __del__ (self ): print ("del..." ) stu1 = Student("jack" , 19 )del stu1 def foo (): print ("----- 函数内" ) stu2 = Student("jack" , 19 ) print (stu2.__dict__) foo()
示例:执行程序,猜一猜打印结果。
1 2 3 4 5 class Foo : def __del__ (self ): print ('执行我啦' )print ('------->' )
5-2 new方法【重点】
当类被调用实例化对象时第一个被处罚的函数,用来实例化并返回一个空对象
__new__
方法在init方法前执行,__new__
函数会新建一个空对象,然后这个空对象会交给__init__
函数初始化。这个方法结束的时候,把这个空对象return出去
这里说说实例化对象背后发生的三件事:
1、__new__
函数先会新建一个空对象,然后返回出去;给__init__
函数
2、该空对象执行__init__
函数,就初始化了对象的属性;
3、返回初始化完成后的对象,即实例化后的对象。
__new__
魔法方法的一些应用场景:
实现单例模式 (Singleton):使用__new__
方法来确保只有一个实例,并在需要时返回同一个实例。这个可以在多线程环境下确保只有一个全局实例,
定制不可变类型:如果你希望创建以恶搞不可变的自定义对象,可以通过在__new__
方法中重写对象创建的逻辑来实现(不了解)
继承不可变类型:当继承python内置的不可变类型(如元组、字符串)时,你可以通过重写__new__
方法来创建定制过程(不了解)
定制元类(Metaclass):在元类中可以重写__new__
方法,用于控制类的创建和实例化过程。(不了解,这个可以了解一下)
对象池管理:在某些情况下,你可能需要使用对象池来重用已有的对象实例,可以通过new方法来实现对象池的管理。(不了解,这个可以了解一下)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Singleton : _instance = None def __new__ (cls ): if cls._instance is None : cls._instance = super ().__new__(cls) return cls._instance obj1 = Singleton() obj2 = Singleton() print (obj1 is obj2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Student : def __init__ (self, name, age ): print ("init:" , id (self)) self.name = name self.age = age def __new__ (cls, *args, **kwargs ): obj = object .__new__(cls) print ("new:" , id (obj)) return obj stu1 = Student("jack" , 19 )
5.3 call【重点】
类中的__call__
在对象被调用时出发。就是当对象加括号被调用时触发
对象或者变量,只有实现了__call__
方法,才是可调用对象,才可以被执行,否则就会报错, object is not callable
定义了__call__
后,你实例化的对象可以像函数一样被调用,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class CallableClass : def __init__ (self, value ): self.value = value def __call__ (self, x ): return self.value * x callable_obj = CallableClass(5 ) result = callable_obj(10 )print (result)
总结就是,可以把类当方法直接调用
场景呢?
当你希望某个对象能像函数一样调用且获得返回值时
__call__
方法并结合with语句和__enter__
和__exit__
方法。实现上下文管理
__call__
实现装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class MyDecorator : def __init__ (self, func ): self.func = func def __call__ (self, *args, **kwargs ): print ("Before function is called" ) result = self.func(*args, **kwargs) print ("After function is called" ) return result@MyDecorator def my_function (x, y ): return x + y result = my_function(3 , 5 )print ("Result:" , result) Before function is called After function is called Result: 8
装饰器实现,两种方式
第一个是常规的,闭包,嵌套函数
第二个就是定义带__call__
的类。讲函数作为__init__
入参传进来
5.4 str和repr
__str__
:当对象被访问打印时触发执行,他必须有一个字符串类型的返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 class Person : def __init__ (self, name, age ): self.name = name self.age = age def __str__ (self ): return f"Person(name={self.name} , age={self.age} )" person = Person("Alice" , 30 )print (person) sut = Student("name" ,"age" )print (sut)
好这个我知道了,那应用的场景呢?
总之,__str__
魔法方法适用于需要自定义对象的可读性、用户友好性和调试信息的情况。它使你能够以更有意义的方式呈现对象,并提供有关对象状态和属性的重要信息。
repr,本质和srt一样,都是对象被打印时显示的内容,但是
与__str__
的区别: __str__
应该返回更友好、易读的字符串,而 __repr__
则应该返回更详细、可复制的字符串,通常包含足够的信息来重新创建对象。
5.5 比较系列 eq nq it 两个对象是可以比较的,但是比较的结果可能不是我们预期的,此时可以重写比较系列的魔法方法,实现自定义比较逻辑
eq
__eq__
方法是 Python 中的一个魔法方法,用于定义对象的相等性比较操作。当你在自定义类中实现了 __eq__
方法时,你可以自定义对象之间的相等性判断规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Person : def __init__ (self, name, age ): self.name = name self.age = age def __eq__ (self, other ): if isinstance (other, Person): print ("eq被执行了" ) return self.name == other.name and self.age == other.age return False person1 = Person("Alice" , 25 ) person2 = Person("Bob" , 30 ) person3 = Person("Alice" , 25 )print (person1 == person2) print (person1 == person3)
同样的道理,其他比较系列的放啊
nq,实现的是是够不相等的逻辑。如果没有实现,则默认是eq的结果取反。
该系列其他魔法方法:__lt__、__gt__、__le__、__ge__
分别表示小于、大于、小于等于和大于等于。
5.6 attr系列 【了解】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Person : def __init__ (self, name ): self.name = name def __getattr__ (self, item ): print ('调用不存在的属性会触发我' ) return self.__dict__.get(item) def __setattr__ (self, key, value ): print ('设置修改对象属性时触发我' ) self.__dict__[key] = value def __delattr__ (self, item ): print ('删除对象属性时触发我' ) self.__dict__.pop(item) p = Person('jack' ) p.name = 'mack' print (p.age) p.age = 18 print (p.age) del p.age
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class MyDict (dict ): abc = "abc" def __getattr__ (self, item ): print ("字典对象通过点访问不存在的属性时触发" ) return self.get(item) def __setattr__ (self, key, value ): if not isinstance (value, str ): raise ValueError('值必须是字符串类型' ) self[key] = value d = MyDict({'name' : 'jack' })print (d.name) print (d.abc)
5.7 item系列 ,和attr一样用【重点】 实现普通对象像字典一样通过 [ ] 操作 和attr一样理解
item:项目
attr: attribute: 属性的英文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Person (object ): def __init__ (self, name ): self.name = name def __setitem__ (self, key, value ): print ("[]设置值时触发" ) setattr (self, key, value) def __getitem__ (self, item ): print ("[]取值时触发" ) return getattr (self, item) def __delitem__ (self, key ): print ("del p[key]时触发" , key) p = Person('jack' ) p['name' ] = 'mack' print (p['name' ]) print (p.__dict__)del p["name" ]
5.8 enter和exit【了解】
上下文管理器
:
with :顺着
with open(file , mode操作模式) mode就是r读取和w写入的模式选择
这里的with open中的open( )函数所返回出来的对象就是一个上下文管理器,这个对象实现了enter方法和exit方法 用于对文件的自动打开和关闭,
如果没有open,那我们的代码应该是怎么写的?
就像数据库一样
file = open ( ‘file’ , mode )
file.write(“hello,world”)
file.close( )
但是这样写不好看很麻烦,with open 就自动帮我们实现了打开和写入和关闭的操作
好,说到这里,就能理解enter和exit了
这个代码里的MyOpen就是我们的自己定义的上下文管理器,这个类所返回出来的对象就是一个上下文管理器,实现了enter和exit方法打开和关闭
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class MyOpen : def __init__ (self, file_name: str , mode="r" ): self.file = open (file_name, mode) def __enter__ (self ): print ("进入with语句块时触发" ) return self.file def __exit__ (self, exc_type, exc_val, exc_tb ): print ("退出with语句块时触发,不论with语句块内是够有异常报错,__exit__都会被执行" ) self.file.close() with MyOpen("text.txt" , "w" ) as f: f.write("hello world" )
5.9 iter和next【重点】 见名知意,这两个方法是实现迭代器功能的,比如实现一个range
这样我们就知道了range原理
1 2 3 for i in range (10 ): print (i)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import timeclass MyRange (): def __init__ (self,total: int ,step: int =1 ): self.count = 0 self.total = total self.step = step def __iter__ (self ): print ("1111" ) return self def __next__ (self ): time.sleep(0.5 ) self.count += self.step if self.count == self.total: reise StopIteration return self.count for i in MyRange(10 ,2 ): print (i)
总结一下,
iter 当一个类,定义了iter魔法方法,这个类的实例,就被认为是可迭代的(iterable)
iter方法应返回一个迭代器对象,可以是self,也可以是另一个实现了next方法的对象
next 方法:这个方法用于获取迭代中的下一个值。
如果没有更多的值可迭代,应该要引发StopIteration异常,表示迭代结束
在 Python 中,迭代器(Iterator)和生成器(Generator)是用于处理可迭代对象的重要概念。它们提供了一种有效的方式来逐个访问和处理大型数据集或无限序列,同时节省内存和提高性能。
迭代器(Iterator) 是一个实现了迭代协议的对象。它具有两个核心方法:
__iter__()
方法:返回迭代器对象自身。用于支持迭代协议,使迭代器可以在 for
循环等上下文中使用。
__next__()
方法:返回迭代器的下一个元素。如果没有更多元素可供迭代,抛出 StopIteration
异常。
迭代器的工作原理是通过迭代协议来逐个返回元素,只在需要时生成和提供数据,从而避免一次性加载和处理整个数据集或序列。
生成器(Generator) 是一种特殊类型的迭代器,可以使用函数和 yield
语句来创建。生成器函数在被调用时返回一个生成器对象,它可以被迭代,每次迭代都会执行函数体中的代码,直到遇到 yield
语句。
生成器的优点在于它们的定义更简洁,并且它们以惰性的方式生成数据,只在需要时才生成和提供数据。这使得生成器非常适用于处理大型数据集或无限序列,因为它们可以逐步生成数据,而不会一次性占用大量内存。
以下是一个简单的示例,展示了迭代器和生成器的使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class MyIterator : def __init__ (self, data ): self.data = data self.index = 0 def __iter__ (self ): return self def __next__ (self ): if self.index >= len (self.data): raise StopIteration value = self.data[self.index] self.index += 1 return value my_iterator = MyIterator([1 , 2 , 3 , 4 , 5 ])for num in my_iterator: print (num)def my_generator (data ): for num in data: yield num my_generator = my_generator([1 , 2 , 3 , 4 , 5 ])for num in my_generator: print (num)
5.10其他魔法方法 getattribute系列 attribute就是属性的意思
调用对象不存在的属性的时候,会访问getattr
访问对象的属性不管存在不存在都会触发getattribute方法
除非getattribute抛出AttributeError异常才会触发getattr
所以当你访问对象的属性不管存在不存在都会触发getattribute异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Foo : def __init__ (self,x ): self.x = x def __getattr__ (self, item ): print ('执行的是我' ) def __getattribute__ (self, item ): print ('不管是否存在,我都会执行' ) raise AttributeError('哈哈' ) f1=Foo(10 ) f1.x f1.y
module、class、name
__module__
:模块,表示当前错爱早的对象在哪个模块
__class__
:类 表示当前操作对象的类是什么
__name__
: 表示当前对象的名字是什么
slot、all
__slot__
:控制对象在实例化后可以持支持哪些属性
是一个用于限制对象属性的特殊属性,它是一种用于优化内存占用和属性访问速度的机制。通过在类中定义 __slots__
属性,你可以指定一个属性名称的列表,从而限制类的实例只能具有指定列表中的属性。
all:
在模块文件中设置 __all__
变量,当其它文件以“from 模块名 import *”的形式导入该模块时,该文件中只能使用 __all__
列表中指定的成员。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def add (a: int , b: int ): return a + bdef mul (a: int , b: int ): return a * bdef xsum (nums: list ): return sum (nums) __all__ = ["add" , "mul" ]