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

  

 

posted @ 2017-11-16 15:26  ihoneysec  阅读(430)  评论(0编辑  收藏  举报