【魔术方法】
特殊属性
__name__ 类,函数,方法等的名字
__module__ 类定义所在的模块名
__class__ 对象或类所属的类
__bases__ 类的基类元组,顺序为它们在基类列表中出现的顺序
__doc__ 类 函数的字符串,如果没有定义则为None
__mro__ 类的mro ,class.mro() 的 结果保存在__mro__中
__dict__ 类或者实例的属性,可写的字典
查看属性
__dir__ 返回类或者对象的所有成员名称,dir( )函数就是调用__dir__,如果提供__dir__(),则返回属性的列表,否则会尽量从__dict__属性中收集信息
如果dir( )参数包含方法__dir__()方法将被调用,如果不包含__dir__(),改方法将最大限度的收集参数信息
dir()对于不同类型的对象具有不同的行为
如果对象是模块对象,列表包含模块的属性名
如果对象是类型或者类对象,列表包含类的属性名,及它的基类的属性名
否则,列表包含对象的属性名,它的类的属性名,和类的基类的属性名
class Animal: x = 123 def __init__(self,name): self.name = name self.__age = 10 self.weight = 20 print('animal Module\'s name ={}'.format(dir(Animal))) #指定模块名词空间内的属性 print('object's__dict ={}'.format(sorted(object.__dict__.keys())) #object的字典 print('Animal's dir() = {}'.format(dir(Animal))) 类Animal的dir()
hash
散列,哈希就是得到散列值,哈希可能一样,但是对象不是一个,称为哈希冲突
使用哈希取模法,来得到一个个不同的哈希值
__hash__ 内建函数hash()调用的返回值,返回一个整数,如果定义这个方法该类的实例就可hash
使用 hash() 相当于调用 **.__hash__( )
__hash__方法只能判断两个值的引用或者值是否相等,但是去重得用__eq__来判断是否他们的内存地址是否一样
__eq__ 对应== 操作符,判断2个对象是否相等,返回bool值
#a == b 等价于 a.__eq__(b) 运算符的实现方法
set集合 会先使用__eq__方法判断值是否一样,如果一样就会去重,即使内存地址一样
__hash__方法只是返回一个hash值作为set的key,但是去重,还需要__eq__来判断2个对象是否相等
hash 值相等,只是hash冲突,不能说明两个对象是相等的
因此,一般来说提供__hash__方法是为了作为set或者dict的key的,所以去重要同时提供__eq__方法
可hash对象必须提供__hash__方法,没有提供的话,isinstance(p1,collections.Hashable)一定为False
去重要提供__eq方法
__bool__ 内建函数bool()或者对象放在逻辑表达式的位置,调用这个函数返回布尔值
没有定义__bool__(),就找__len__()返回长度,非0为真,如果__len__()也没有定义,那么所有实例都返回真
可视化:
方法:
除了print ,str , format之外其他大部分调用的都是__repr__
__repr__ 内建函数repr()对一个对象获取字符串表达式,如果一个类定义了__repr__( )但没有定义__str__,那么在请求该类的实例的‘ 非正式 ’的字符串时也将调用__repr__()
__str__ str()函数,内建函数format、print()函数调用,需要返回对象的字符串表达
运算符重载
operator模块提供以下的特殊方法,可以将实例使用下面的操作符来操作
运算符 | 特殊方法 | 含义 |
<,<=,==,>,>=,!= | __lt__,__le__,__eq__,__gt__,__ge__,__ne__ | 比较运算符 |
+,-,*,/,%,//,**,divmod | __add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__ | 算数运算符,移位,位运算也有对应的方法 |
+=,-=,*=,/=,%=,//=,**= | __iadd__,__isub__,__mul__,iturediv__,__imod__,__ifloordiv__,__ipow__ |
返回类型,
实现连加(链式编程),返回实例本身
class A: def __init__(self,x): self.x = x def __sub__(self, other): return self.x - other.x def __isub__(self, other): tmp = self.x - other.x return tmp def __str__(self): return str(self.x) x = A(4) y = A(5) print(x - y ,x.__sub__(y)) x-=y
习题1:
完成point类设计,实现判断点相等的方法,并完成向量的加法
class Point: def __init__(self,x,y): self.x = x self.y = y def __eq__(self, other): return self.x == other.x and self.y == other.y def add(self,other): #提供格式化后的追加打印 return Point(self.x+other.x,self.y+other.y) def __add__(self, other): #运算符重载,会提供 + 号的方法 return (self.x + other.x , self.y + other.y) def __str__(self): #可视化 return '{},{}'.format(self.x,self.y) a =Point(1,3) b =Point(1,1) point = (a,b) #print(point[0].add(point[1])) print(point[0]+point[1]) point(Point(*(point[0]+ point[1])))
print(a==b)
运算符重载应用场景
往往使用面向对象实现的类,需要做大量的运算,而运算符是这种运算在数学上最常见的表达方式,例如,上例中的对 + 进行了运算符重载,实现了Point类的二元操作,重新定义为Point+Point
提供运算符重载,比直接提供加法方法要更加适合该领域内使用者的习惯
int类,基本实现了所有的操作符,可以参考
容器相关方法
方法 | 意义 |
__len__ | 内建输入法len( ),返回对象的长度(>=0的正数),其实即使把对象当做容器类型来看,就如同list或者dict,bool()函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在返回非0为真 |
__iter__ | 迭代容器时,调用,返回一个新的迭代器对象 |
__contains__ | in成员运算符,没有实现,就调用__iter__方法遍历 |
__getitem__ | 实现self[key]访问,序列对象,key接受整数位索引,或者切片,对于set和dict,key位hashable,key不存在引发KeyError异常 |
__setitem__ | 和__getitem__的访问类似,是设置值的方法 |
__missing__ | 字典使用__getitem__()调用时,key不存在执行该方法 |
练习,将购物车改造成方便操作的容器类
# 将购物车类改造成方便的操作的容器 class Cart: def __init__(self): self.__items = [] def additem(self, item): self.__items.append(item) def __len__(self): return len(self.__items) def __iter__(self): # return iter(self.__items) yield from self.__items def __getitem__(self, index): # 使用索引访问 print(index, '#############') return self.__items[index] def __setitem__(self, index, value): # 索引赋值 self.__items[index] = value def __add__(self, other): # 实现购物车加的功能 self.__items.append(other) return self cart = Cart() cart.additem(1) cart.additem('abc') cart.additem(3) cart.additem(4) # print(len(cart.items)) print(len(cart)) for x in cart: print(x) # print(cart['1']) # cart[1] = 2 cart + 11 + 12 # 链式编程 cart.__add__(13).__add__(14) print(list(cart))
可调用对象
python中一切皆对象,函数也不例外
def foo():
print(foo.__module__,foo.__name__)
foo()
等价于 foo.__call__()
函数即对象,对象foo加上(),就等于调用对象的__call__( )方法
可调用对象
方法 | 意义 |
__call__ | 类中的第一个方法,实例就可以向函数一样调用 |
可调用对象,应以一个类,并实例化得到其实例,就可以将其向函数一样调用
class Point: def __init__(self,x,y): self.x = x self.y = y def __call__(self, *args, **kwargs): #1-1 return self.x + self.y a = Point(4,5)
ret =0 for x in args: ret += x self.ret = ret return ret a =Adder() print(a(4,5,6))
定义一个斐波那契数列方便调用
class Fib: def __init__(self): """使用缓冲,减少计算""" self.items = [0, 1, 1] def __call__(self, index): # if index >= len(self.items): for i in range(3, index + 1): self.items.append(self.items[i - 1] + self.items[i - 2]) return self.items[index] def __getitem__(self, index): # return self.items[item] return self(index) def __len__(self): return len(self.items) - 1 """可迭代""" def __iter__(self): return iter(self.items) fib = Fib() print(fib[10]) for x in fib: print(x)
练习题:
# 用链表实现linkedlist 链表 # 单向链表实现append,literes方法 # 双向链表实现append . pop, insert. remove. iternodes方法 class Link: """创建一个节点""" def __init__(self, val, next=None, prev=None): self.val = val self.next = next self.prev = prev def __repr__(self): return str(self.val) def __str__(self): return str(self.val) class Linkist: """容器类,某种方式存储一个个节点""" def __init__(self): self.nodes = [] # 不需要插入的列表来说,检索方便,但是insert、remove不合适 self.head = None self.tail = None def __len__(self): return len(self.nodes) def __getitem__(self, item): return self.nodes[item] def append(self, val): node = Link(val) # 实例化的一个新节点 prev = self.tail if prev is None: self.head = node else: prev.next = node self.nodes.append(val) self.tail = node def iternodes(self, reverse=False): current = self.head while current: yield current current = current.next ll = Link() ll.append(5) ll.append(7) for node in ll.iternodes(): print(node) print(ll[0]) print(ll[1])
双链表
# 双向链表实现append . pop, insert. remove. iternodes方法 # 双向链表: # 0) append尾部追加 # 1) pop尾部弹出 # 2) insert 头部插入、中间插入、尾部插入 # 3) remove 头部删除、中间删除、尾部删除 class SingleNode: def __init__(self, val, next=None, prev=None): self.val = val self.next = next self.prev = prev def __repr__(self): return str(self.val) def __str__(self): return str(self.val) class LinkedList: def __init__(self): # self.nodes = [] 加上链表二不想 self.head = None self.tail = None def append(self, val): node = SingleNode(val) # prev = self.tail # if prev is None: if self.head is None: # only one self.head = node else: # >1 self.tail.next = node node.prev = self.tail # self.nodes.append(node) self.tail = node def iternodes(self, reverse=False): current = self.tail if reverse else self.head while current: yield current current = current.prev if reverse else current.next def pop(self):#尾巴移除 if self.tail is None: # 0个数据 raise Exception('Empty')#该链表为空 报异常 tail = self.tail# node prev = tail.prev# item # next = tail.next #尾的下一个恒定为None if prev is None: #当移走一个里面只有一个元素,表示为空 """清空元素""" self.head = None self.tail = None else: # >1 self.tail = prev #修正尾巴 prev.next = None#后一个元素的没有不用修改 return tail.val def getitem(self, index): # index不考虑传入str类型,全当做int类型 if index < 0: return None current = None for i, node in enumerate(self.iternodes()): if i == index: current = node break if current is not None: return current def insert(self, index, val): if index < 0: # 不考虑负索引 raise Exception('Error') current = None for i, node in enumerate(self.iternodes()):#迭代元素 if i == index:#判断索引是否超界 current = node #当找到当前节点 break # else : 在不遇到break 时不返回 #找到元素插入点和元素 if current is None: # 考虑元素为空、为一个 self.append(val) return # 尾部、头部、中间追加 prev = current.prev# 前一个节点 next = current.next#后一个节点 node = SingleNode(val)#待加入节点对象 if prev is None: # 头部插入 self.head = node #加入新的元素时 改变头 # node.next = current # current.prev = node else: #中间加入 node.prev = prev#中间加入比较特殊 修改四个箭头 # node.next = current #和前面条件共同部分抽出来放后面 # current.prev = node prev.next = node node.next = current current.prev = node def remove(self, index): if self.tail is None:# 全空 raise Exception('Empty') if index < 0:#全空 raise ValueError('Wrong Index {}'.format(index)) current = None """超界""" for i, node in enumerate(self.iternodes()): if i == index: current = node break if current is None: # Not found raise ValueError('Wrong Index {}. Out of boundary'.format(index)) #找到了要移除的结点 prev = current.prev next = current.next if prev is None and next is None: # only one node self.head = None# 头尾修改 self.tail = None elif prev is None: # 删头部 self.head = next next.prev = None elif next is None: # 删尾部 self.tail = prev prev.next = None else: # 中间 """中间的俩边 手拉手""" prev.next = next next.prev = prev del current # def __getitem__(self, item): # return self.nodes[item] ll = LinkedList() ll.append('abc') ll.append(1) ll.append(2) ll.append(3) ll.append(4) ll.append(5) ll.append('def') ll.insert(6, 6) # 边界 ll.insert(7, 7) # 尾部 ll.insert(8, 8) # 尾部 ll.insert(0, 123) # 头部 ll.insert(100, 100) # 超界 # ll.remove(100) # ll.pop() # ll.pop() for node in ll.iternodes(False): # False print(node) print('~' * 30) # print(ll[0]) # print(ll[1]) ll.remove(5) ll.remove(6) ll.remove(0) ll.remove(1) for node in ll.iternodes(False): # False print(node)