python面对对象核心之魔术方法 - - ->dir(),  运算符重载,可视化,容器

__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()
 
 
分类:
     创建与销毁
          __init__ 与__del__
     hash
     bool
     可视化
     运算符重载
     容器和大小
     可调用对象
     上下文管理
     反射
     描述器
     其他
 
hash
散列,哈希就是得到散列值,哈希可能一样,但是对象不是一个,称为哈希冲突
 
使用哈希取模法,来得到一个个不同的哈希值
h%n = 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
print(x)
 
习题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.lst = []

    def __len__(self):
        return len(self.lst)        # 获取长度
 
    def add(self,items):            # 追加数据
        self.lst.append(self.items)
 
    def __iter__(self):          #迭代和in操作
        return  iter(self.lst)
 
    def __getitem__(self, item):    # 根据索引拿数据
        return  self.lst[item]
 
    def __setitem__(self, key, value):          #索引赋值操作
        self.lst[key] = value
 
a =Cart()
print(2 in a)     #in
a.lst.append(5)     #追加
for i in a:          #迭代
     print(i)
print(a.lst[0],10)     #赋值
print(a.lst[0])     #拿索引取值
 
 
可调用对象
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)
print(a())      # a() = a.__call__(self.x+self.y) 等于调用1-1
 
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class Adder:
    def __call__(self, *args):          #  (*接受参数)
        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, x):
if x < 2:
raise Exception('Error')

else:
for i in range(3,x+1):
self.items.append(self.items[i-1]+self.items[i-2]) #直接append进去,这样外部的列表就会保存所有迭代的数据,减少计算次数
return self.items #注意return的位置,等迭代完在return,不然只能迭代一次

def __getitem__(self, index): #设置一个可根据索引拿数据的魔术方法,很方便调用
return self.items[index]

def __len__(self):
return len(self.items)

def __iter__(self):
return iter(self.items)

def __str__(self):
return str(self.items)

a= Fib()
print(a(500))
print(a[8])
print(a.items[8])
print(len(a.items))
print(a)

posted on 2017-11-21 14:37  pythonerLau  阅读(367)  评论(0编辑  收藏  举报

导航