zoukankan      html  css  js  c++  java
  • 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))
    

      

  • 相关阅读:
    “访问”美术馆
    加分二叉树
    有线电视网
    二叉苹果树
    鬼子进村
    遍历问题
    最大子树和
    FBI树
    求前序遍历
    JS如何实现点击页面内任意的链接均加参数跳转?
  • 原文地址:https://www.cnblogs.com/i-honey/p/7844553.html
Copyright © 2011-2022 走看看