Python 面向对象(三) 魔术方法
__getitem__ 在对实例或对象使用索引访问时调用,self[key]
__dir__ 收集当前模块的信息,包括继承自其它基类(包括object类)的属性和方法
__new 定义如何创建实例
__init__ 构造方法,实例创建时如何初始化
__del__ 析构方法,对象在内存中被释放时,自动触发执行
__file__ 当前文件在系统中的绝对路径
__hash__ hash值相同表示hash冲突,但并不代表对象值相同。列表不可hash,元组可hash。
内建类型集合(set)的源码中就用到了"__hash__ = None",来表示set是不可hash类型。
hash()等同于调用__hash__()方法
isinstance(1,hashable) 是否可hash
__equal__ == 判断是否相等
a == b 等价于
a.__equal__(b)
__bool__ 返回bool()布尔值,__bool__() --> __len__() 搜索顺序。
如果没有__bool__(),则调用__len__()返回长度,非0就为真。
如果__len__也没有定义,则所有实例都为真
可视化相关的魔术方法:
__repr__ 重写表现形式,__str__ --> __repr__ --> object 查找顺序。至少有个__repr__
__str__ format(),print(),str()函数调用时使用的方法,如果没有__str__,则调用__repr__方法,所以这两个方法尽量写一样。
__bytes__ 在对象使用bytes()函数时调用此方法
__ascii__
——————————————————————————————————————
运算符重载
容器
__len__ 元素个数,必须大于等于0
size有可能是容器的大小
__iter__ 迭代容器时调用,返回一个新的迭代器对象
__contains__ in成员运算符 __contains__ --> __iter__顺序
__getitem__ 返回self[key] 的值,不存在返回KeyError异常
__setitem__
__missing__ 调用__getitem__时,key不存在时触发,以免抛异常
链式编程
深入学习综合症
__repr__ = __str__
——————————————————————————————————————————————
运算符重载,+= 举例:
class A: def __init__(self,x): self.x = x def __repr__(self): return str(self.x) def __iadd__(self, other): # return A(self.x + other.x) #生成新对象 self.x = self.x + other.x #就地修改 return self a1 = A(4) a2 = A(10) a3 = A(5) print(id(a1),id(a2),id(a3)) a1 += a2 print(id(a1),a1) ~~~~~~ 4362338144 4362338200 4362338256 4362338144 14 #对比可以看到前后的id(a1)内存地址一致,为就地修改
上面例子是就地修改,下面这个例子是生成新对象,对比id(a1)内存地址。
class A: def __init__(self,x): self.x = x def __repr__(self): return str(self.x) def __iadd__(self, other): return A(self.x + other.x) #生成新对象 # self.x = self.x + other.x #就地修改 # return self a1 = A(4) a2 = A(10) a3 = A(5) print(id(a1),id(a2),id(a3)) a1 += a2 print(id(a1),a1) ~~~~~~~~~~~~~~~~~~ 4362338088 4362338144 4362338200 4362604616 14 #id(a1)值不等,生成的是新对象
练习: 设计二维坐标类Point,使其成为可hash类型,并比较2个坐标的实例是否相等。 class Point: def __init__(self,x,y): self.x = x self.y = y def __str__(self): return '{},{}'.format(self.x,self.y) def __eq__(self, other): return (self.x == other.x) and (self.y == other.y) def __iadd__(self, other): return Point((self.x + other.x),(self.y + other.y)) p1 = Point(4,5) p2 = Point(6,5) print(p1 == p2) p1 += p2 print(p1)
练习: 购物车支持索引的方式检索商品: class Color: RED = 0 BLACK = 1 WHITE = 2 BLUE = 3 class Item: def __init__(self,**kwargs): self.spec = kwargs def __repr__(self): return str(sorted(self.spec.items())) def __str__(self): return str(sorted(self.spec.items())) def __getitem__(self, item): return self.spec.items()[item] class Cart: def __init__(self): self.items = [] def addItems(self,item): self.items.append(item) def getAllItems(self): return self.items def __add__(self, other): # + pass def __len__(self): return len(self.getAllItems()) def __iter__(self): return iter(self.items) # for item in self.getAllItems(): # yield item def __getitem__(self, index): #index return self.items[index] def __missing__(self, key): #只支持字典 print(key) def __setitem__(self, key, value): #索引不可以超界 self.items[key] = value return self.items mycart = Cart() mycar = Item(mark='tesla',color=Color.WHITE,price='100w',speed='400km/h',year=2017) myphone = Item(mark='Nokia',color=Color.BLACK,price=5000,memory='4G') mymac = Item(mark='MacPro',color=Color.WHITE,price=19999,memory='16G',ssd='512G') mycart.addItems(mycar) mycart.addItems(myphone) mycart.addItems(mymac) for item in mycart.getAllItems(): # for item in mycart: print(item.__dict__) print(mycart.__len__()) print(mycart[2])
练习:斐波那契数列支持索引方式查找第n个数字。索引和调用两种方式: class Fib: """0,1,1,2,3,5,8,13,21""" def __init__(self): self.lst = [] def __call__(self, n, *args, **kwargs): i = 0 prev, next = 0, 1 while True: self.lst.append(prev) if i == n: return prev prev, next = next, prev + next i += 1 def __getitem__(self, item): return self.__call__(item) f1 = Fib() # print(f1(7)) # print(f1(8)) print(f1[8])
回顾下普通装饰器 import datetime import time def timer(fn): def wrapper(x, y): start = datetime.datetime.now() print('start:',start) print(fn(x, y)) time.sleep(1) stop = datetime.datetime.now() # print('stop: ',stop) return 'stop: {}'.format(stop) return wrapper @timer def add(x, y): return x + y num1 = add(3, 4) print(num1)
上下文管理 类作为上下文,进入上下文时如果有定义__enter__方法,则做该方法的动作。 __enter__ 进去了帮我做某事, __exit__ 离开时候帮忙 必须同时存在 import sys sys.exit() 强制退出脚本 异常退出时处理 exc_type exc_val exc_tb traceback 类装饰器 两个装饰器同时装饰 with TimeIt() as f: pass from functools import wrapås @wraps(fn) def self().... == wraps(fn)(self) 复制所有属性 类装饰是调用到__init__ 和__call__ --—————————————————————————————————————————————— ————————————————————————————————————————————————
用类做装饰器,调用的是__enter__和__exit__: import datetime import time class TimeIt: def __init__(self, fn): print('__init__...') self.fn = fn def __enter__(self): self.start = datetime.datetime.now() print('Enter:', self.start) return self def __call__(self, *args, **kwargs): print('__call__....') self.start = datetime.datetime.now() print('Enter:', self.start) print(self.fn(*args, **kwargs)) self.stop = datetime.datetime.now() # print('Exit:', self.stop) return 'Exit: {}'.format(self.stop) def __exit__(self, exc_type, exc_val, exc_tb): self.stop = datetime.datetime.now() print('Exit:', self.stop) return self @TimeIt def add(x, y): time.sleep(1) return x + y # with TimeIt(add) as foo: # print(foo(3,4)) print(add(3, 4))
使用wraps的方式,拷贝所有属性: import time import datetime from functools import wraps class TimeIt: def __init__(self, fn): # self.__doc__ = fn.__doc__ print('init') self._fn = fn wraps(fn)(self) #@wraps(fn) 等同于 #def self()...... def __enter__(self): print('enter') self.start = datetime.datetime.now() return self def __call__(self, *args, **kwargs): print('__call__') start = datetime.datetime.now() ret = self._fn(*args, **kwargs) delta = (datetime.datetime.now() - start).total_seconds() print("dec {} took {}".format(self._fn.__name__, delta)) return ret def __exit__(self, exc_type, exc_val, exc_tb): print('exit') delta = (datetime.datetime.now() - self.start).total_seconds() print("context {} took {}".format(self._fn.__name__, delta)) return # def logger(fn): # @wraps(fn) # def wrapper(*args, **kwargs): # start = datetime.datetime.now() # ret = fn(*args, **kwargs) # delta = (datetime.datetime.now() - start).total_seconds() # print("dec {} took {}".format(fn.__name__, delta)) # return ret # return wrapper @TimeIt def add(x,y): # add = TimeIt(add) """This is a add function.~~~~~~~~~~~~~~~""" time.sleep(2) return x + y print(add(10,11)) print(add.__doc__) print(add.__name__) print(add.__dict__) # with TimeIt(add) as foo: # print(foo(5, 16))