python 内置方法/魔法方法
什么是内置方法?
# 定义在类内部,以__开头并以__结果的方法 # 特点:会在某种情况下自动触发执行
为什么要用内置方法?
# 为了定制化我们的类or对象
python中常用魔法方法
# __init__:类实例化会触发 # __str__:打印对象会触发 # __call__:对象()触发,类也是对象 类(),类的实例化过程调用元类的__call__ # __new__:在类实例化会触发,它比__init__早(造出裸体的人,__init__穿衣服) # __del__:del 对象,对象回收的时候触发 # __setattr__,__getattr__:(.拦截方法),当对象.属性--》赋值会调用setattr,如果是取值会调用getattr # __getitem__,__setitem__:([]拦截) # __enter__和__exit__ 上下文管理器
如何使用内置方法?
__str__:在打印对象时会自动触发,然后将返回值(必须是字符串类型)当做本次打印的结果输出
class People: def __init__(self, name, age): self.name = name self.age = age def __str__(self): # print('运行了...') return "<%s:%s>" %(self.name,self.age) obj = People('辣白菜同学', 18) # print(obj.__str__()) print(obj) # <'辣白菜同学':18>
__del__:在清理对象时触发,会先执行该方法
class Point: def __init__(self, x=0, y=0): self.x = x self.y = y def __del__(self): class_name = self.__class__.__name__ print(class_name, "销毁") pt1 = Point() pt2 = pt1 print(id(pt1),id(pt2)) # 打印对象的id del pt1 #要等pt2执行完才会触发__del__ print(pt2) ''' 2074762969384 2074762969384 <__main__.Point object at 0x000001E311828128> Point 销毁 '''
__format__:自定义打印时间格式
date_dic={ 'ymd':'{0.year}:{0.month}:{0.day}', 'dmy':'{0.day}/{0.month}/{0.year}', 'mdy':'{0.month}-{0.day}-{0.year}', } class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day def __format__(self, format_spec): if not format_spec or format_spec not in date_dic: format_spec='dmy' fmt=date_dic[format_spec] return fmt.format(self) d1=Date(2016,12,29) print(format(d1)) print(format(d1,"mdy")) ''' 执行结果 29/12/2016 12-29-2016 '''
class Person: def __init__(self, name): self.name = name def __setitem__(self, key, value): print("obj['xxx']=yyy方式赋值调用!") setattr(self, key, value) # 反射 def __getitem__(self, item): print("obj['xxx']方式取值调用!") return getattr(self, item) # 反射取值 p = Person('lqz') # p.name = 'ppp' # 无打印 p['name'] = 10 # 本来这么赋值是不可以的,因为使用__setitem__魔法方法就可以,打印obj['xxx']=yyy方式赋值调用! # print(p.name) # ppp print(p['name']) # obj['xxx']方式取值调用
__setattr__、__getattr__
class Mydic(dict): def __setattr__(self, key, value): print("对象加点赋值,会触发我") self[key]=value def __getattr__(self, item): print("对象加点取值,会触发我") return self[item] # 不要加引号 mydic=Mydic(name='lqz',age=18) # print(mydic['name']) print(mydic.name) # mydic.name=99 # print(mydic.name)
补充:
__getattr__的执行机制: # 属性查找失败后,解释器会调用__getattr__方法。简单来说,对 my_obj.x 表达式, # Python会检查my_obj实例有没有名为x的属性, # 如果没有,到类(my_obj.__class__)中查找,如果还没有,顺着继承树继续查找。 # 如果依旧在找不到,调用my_obj 所属类中定义的__getattr__ 方法,传入self和属性名称的字符串形式
案例:
class Fun: def __init__(self, name, age, male): self.name = name self.age = age self.male = male def __setattr__(self, key, value): if key == "name": if isinstance(value, str): # RecursionError: maximum recursion depth exceeded报错,内部是这么执行的self.key=value, # setattr(self, key, value) # 解决方法 # 方法一 # self.__dict__[key] = value # 方法二 object.__setattr__(self, key, value) else: print("不能放非字符串类型") else: object.__setattr__(self, key, value) def __getattr__(self, item): print(self) return self.__dict__.get(item) f1 = Fun("lxx", 18, "male") f1.name = 'lqz' print(f1.age) print(f1.name)
class Person: def __enter__(self): print("我在with管理的时候,会触发") print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量') return 'oo' def __exit__(self, exc_type, exc_val, exc_tb): print('退出with代码块时执行此方法') print('1', exc_type) print('2', exc_val) print('3', exc_tb) with Person() as p: # 这句话执行,会触发类的__enter__,这个p就是上面__enter__中return的 print(p)
案例:上下文管理MySQL链接
import pymysql class Mysql: def __enter__(self): print("我在with管理的时候,会触发") print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量') # 链接 self.conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123456', database='book_user', charset='utf8', ) # 游标 self.cursor = self.conn.cursor() # 执行完毕返回的结果集默认以元组显示 return self def __exit__(self, exc_type, exc_val, exc_tb): self.cursor.close() self.conn.close() print('退出with代码块时执行此方法') # print('1', exc_type) # print('2', exc_val) # print('3', exc_tb) with Mysql() as self: # 这句话执行,会触发类的__enter__ print(self) sql = 'select * from app01_book;' rows = self.cursor.execute(sql) print(rows)
__eq__
class A: def __init__(self,x,y): self.x = x self.y = y # def __eq__(self,obj): # # 打印出比较的第二个对象的x值 # print(obj.x) # if self.x +self.y == obj.x+obj.y: # return True # else: # return False a=A(1,2) b=A(99,3) print(a==b) # 当执行==s时,会触发__eq__的执行,并且把b传进去,就是object # ==后只要是对象,就可以传进去,就是object