【魔术方法】


特殊属性


__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)