zoukankan      html  css  js  c++  java
  • Python学习笔记之面向对象编程(三)Python类的魔术方法

    python类中有一些方法前后都有两个下划线,这类函数统称为魔术方法。这些方法有特殊的用途,有的不需要我们自己定义,有的则通过一些简单的定义可以实现比较神奇的功能

    我主要把它们分为三个部分,下文也是分为这三个部分来讲解的

    • 简单,功能性,一般不修改直接调用查看结果
    • 实现简单功能,定义后使用
    • 实现比较复杂功能,常常是一些我们常见类的特性的实现原理,具体分为
      • 实例成为可迭代对象(列表的特性)
      • 实例索引取值(列表的特性)
      • 上下文管理(文件的特性)等

    简单功能

    这部分包括上面列出的前两个点

    不需要修改直接调用的,主要有如下几个

    • __dict__ 类和实例都可以调用这个方法
      • 类调用返回这个类中已经定义了的属性和方法,包括特殊方法
      • 实例调用返回属性的字典
    • __module__ 类和实例都可以调用,类所在的模块,比如aa文件夹下的bb.py文件中则返回 aa.bb
    • __class__ 只有实例可以调用,表明实例属于哪个类,内容包括了__module__的信息
    • dir()函数 作用于类和实例上,返回它所有的属性和方法,实际上相当于调用了__dir__()函数

    一般进行简单定义之后使用的方法

    • __doc__ 返回定义类时标注的字符串,标注位置必须在定义的方法属性之前,默认是None
    • __slots__ = ('name', 'age') 加一句这个可以只允许定义这两个属性,无法在实例中添加,这条命令只对当前类起作用,对子类无效
    • __init__ 用于定义实例属性
    • __call__ 输入 实例() 或者 类()() 触发,只有定义了这个,实例才可以像函数一样后面加括号
    • __str__ print实例时打印出来的内容,不定义可以调用,重新定义后则可以定制打印内容
    • __repr__ 直接输出实例名打印出来的内容,不定义可以调用,重新定义后则可以定制打印内容
    • __new__ 创建实例时自动调用 __del__ 对象在内存中被释放时触发(后面不设置例子)
    • __len__ 定义 len(实例) 返回的内容,比如字符串类就定义的是字符串的字符个数,当然也可以改成其他的
    • __eq__ 改变==运算符的行为,定义实例使用==number时,是拿实例的什么与number相比
    • 和eq类似的还有ne lt le gt ge
    • 还有一些用于实例之间数值计算的 __add__ __abs__等方法,定义方式和__eq__差不多,这些应用的典型类就是数值类,定义这些其实定义了某些运算符的行为,比如__iadd__定义了+=的行为一样。除此之外还有转化为整数、浮点,
    • __getattr__ 当实例访问的属性未被定义时,原来会报错,定义了这个之后就会按照这里定义的来输出. setattr 则可以设置属性 delattr则删除属性

    下面是上面方法的使用展示

    class Special:
        '''描述类的信息,__doc__返回放在这里的字符串结果'''
        
        __slots__ = ('name', 'age','__weight') # 限制属性的取值
        
        def __init__(self, name, age, weight):
            self.name = name
            self.age = age
            self.__weight = weight
        
        def __call__(self, content): # 定义 实例() 会返回什么结果
            print(content)
            
        def __str__(self): # 定义打印实例会返回什么结果
            return 'Special object (name:%s)' % self.name
        
        __repr__ = __str__  # 这样简单赋值即可
        
        def __len__(self): # 定义len函数返回的结果
            return len(self.name)
        
        def __eq__(self, num): # 实例和数字比
            return len(self.name) == num
        
        def __gt__(self, other): # 这样可以实现实例和实例比
            return len(self.name) >= len(other)
        
        def __getattr__(self, attr):
            if attr=='score':
                return "You can't see the score"

    创建实例

    s = Special("Bob", 5, 4)
    # s.hobby = "running" # 报错,因为__slots__限制了属性取值
    s.__doc__ # 返回类定义时下方写的字符串描述内容
    # '描述类的信息,__doc__返回放在这里的字符串结果'
    s("__call__ is used") # 调用了__call__
    # __call__ is used
    print(s) # 调用__str__
    # Special object (name:Bob)
    s # 调用__repr__
    # Special object (name:Bob)
    len(s) # 调用了__len__
    # 3
    s==3 # 调用了__eq__
    # True
    Special("Mary", 4,5) > Special("Bob",5,4) # 调用__gt__
    # True
    s.score # 无score参数,调用__getattr__
    # "You can't see the score"
    
    s.__dict__ # 要把__slots__注释掉才能看见属性的字典
    # {'_Special__weight': 4, 'age': 5, 'name': 'Bob'}
    s.__module__ # 查看所属模块
    # '__main__'
    s.__class__ # 查看所属类
    # __main__.Special
    dir(s) # 返回实例的所有属性和方法,调用了__dir__()方法

    可迭代对象

    使实例成为可迭代对象(可以被for循环的)

    • 实现 __iter__ __next__ 方法
    • 如果只实现__iter__则这个方法的返回值必须是一个迭代器
    • 也可以__iter__返回一个self,再定义__next__方法,接受self为参数,在里面具体实现如何取得下一个值以及迭代器何时结束
    • 原理是当对实例调用for循环时,相当于每次对__iter__的返回结果作用一次next()函数,所以要想迭代必须定义__iter__方法。第一种就是next每次正常调用__iter__返回的值,这就要求它的返回值是一个迭代器;第二种则是把next函数改掉,使其功能不再是找到下一个,而是定制我们想要的一些操作

    第一种

    class Ite1:
        
        def __init__(self, a):
            self.a = a
            
        def __iter__(self):
            return iter(range(2*self.a)) # iter函数将一个可迭代对象变成迭代器
         
    i1 = Ite1(3)
    for i in i1:
        print(i) # 0到 5

    第二种

    class Ite2:
        
        def __init__(self, a, b):
            self.a = a
            self.b = b
            
        def __iter__(self):
            return self
        
        def __next__(self):
            self.a += 1
            if self.a > self.b+1: # 条件成立则迭代器结束
                raise StopIteration()
            return self.a-1
        
    for i in Ite2(2,5):
        print(i) # 返回2-5

    索引取值

    这里实现使用中括号索引取值,或者像字典一样操作

    • 实现 __getitem__方法
    • 这个方法的参数除了self,还可以指定一个index,之后return一个和index相关的结果,其实相当于把实例定义成了一个函数,但是是用中括号调用的
    • 结合 __setitem__ __delitem__ 即可让实例像字典一样操作

    只定义__getitem__

    class Index1:
        
        def __getitem__(self, index):
            return 2*index # 如果定义和__next__中内容类似则实现既可以循环又可以[]取值了
        
    i = Index1()
    i[2] # 4

    全部定义

    class Index2:
        
        def __init__(self,**kw):
            self.dict = kw 
                
        def __getitem__(self, key):
            return self.dict[key]
        
        def __setitem__(self, key, value):
            self.dict[key] = value
            
        def __delitem__(self, key):
            del self.dict[key]
            
    i = Index2(name="Bob")
    i['name'] # 'Bob'
    i['age'] = 13
    i['age'] # 13
    del i['age']
    # i['age'] #报错,删掉就没有这个属性了

    上下文管理

    实现上下文管理,即可以和with结合使用

    • 要实现 __enter__ __exit__ 两个方法
    • __enter__会返回一个值,并赋值给as关键词之后的变量
    • __exit__ 定义了处理结束后要做的事情,比如文件的关闭,socket的断开等
    • 更深入地使用:__exit__中可以处理异常。
      • 在上下文管理中运行的代码如果报错,会将三个值自动传入__exit__方法中,分别为 异常的类型,异常的值,异常的追踪栈
      • 通过定义__exit__的返回值可以进行不同的处理,共有两种返回形式,返回True则这个异常忽略,返回None则正常抛出异常

    简单实现上下文管理

    class Manager:
        
        def __init__(self, text):
            self.text = text
        
        def __enter__(self):
            self.text = "It has started " + self.text
            return self # 还是让这个类的实例进行下面的处理
            
        def __exit__(self, ex_type, ex_value, ex_tb):
            self.text = "It is over"
            
            
    with Manager("machine") as m:
        print(m.text)
        
    print(m.text) # as定义的m仍然可以调用
    # It has started machine
    # It is over
    # 发现最后执行了__exit__ 把这个属性改变了

    异常处理

    class DemoManager:
    
        def __enter__(self):
            pass
    
        def __exit__(self, ex_type, ex_value, ex_tb):
            if ex_type is IndexError:
                print(ex_value.__class__)
                return True
            if ex_type is TypeError:
                print(ex_value.__class__)
                return  # return None
    
    # 下面故意制造两种错误
    with DemoManager() as nothing:
        data = [1, 2, 3]
        data[4]  # raise IndexError, 该异常被__exit__处理了
    
    with DemoManager() as nothing:
        data = [1, 2, 3]
        data['a']  # raise TypeError, 该异常没有被__exit__处理

    参考

  • 相关阅读:
    es3的语法来模拟es5的bind方法
    js判断变量的类型(使用闭包来玩一把)
    获取一组时间中的最近的日期
    可以设置超时版的的fetch
    错误排查
    如何查看Linux的CPU负载
    shell脚本操作mysql数据库,使用mysql的-e参数可以执行各种sql的(创建,删除,增,删,改、查)等各种操作
    Swoole server函数列表(转载)
    php的反射
    PHP共享内存
  • 原文地址:https://www.cnblogs.com/JetpropelledSnake/p/8953368.html
Copyright © 2011-2022 走看看