zoukankan      html  css  js  c++  java
  • 面向对象之魔法方法和单例模块

    ## 经常用到的双下方法

    	凡是在类内部定义,以“__开头__结尾”的方法都称之为魔法方法,又称“类的内置方法”,  这些方法会在某些条件成立时触发。
    
    __init__: 在类加括号调用时触发。
        
    __getattr__: 会在对象.属性时,“属性没有”的情况下才会触发;对象.__dict__[属性]不会触发__getattr__,会报keyerror。
        
    __setattr__: 会在 “对象.属性 = 属性值” 时触发。即:设置(添加/修改)属性会触发它的执行;
        
    __delarttr__:在删除对象属性(用del 对象.属性)时,执行该方法。
    
        
    __getattribute__:会在对象.属性时触发,不管有没有该属性都会触发;在获取属性时如果存在__getattribute__则先执行该函数,如果没有拿到属性则继续调用 
    
    __del__: 手动删除对象时立马执行,或是程序运行结束时也会自动执行(运行结束垃圾回收执行) 
    
    
    class A:
    
    
        # def __setattr__(self, key, value):
        #     # print(key)
        #     # print(value)
        #     print("__setattr__")
        #     self.__dict__[key] = value
        #
        # def __delattr__(self, item):
        #     print("__delattr__")
        #     print(item)
        #     self.__dict__.pop(item)
        #     pass
        def __getattr__(self, item):
            print("__getattr__")
            return 1
    
        def __getattribute__(self, item):
            print("__getattribute__")
            # return self.__dict__[item]   # 递归调用__getattribute__方法
            return super().__getattribute__(item)
    
    #
    a = A()
    # a.name = "jack"
    # # print(a.name)
    #
    # # del a.name
    # print(a.name)
    # print(a.xxx)
    # a.name = "xxx"
    print(a.name)
    
    
    # b =A()
    # b.__dict__["name"] = "jack"
    # print(b.name)
    
    __str__  会在对象被转换为字符串时,转换的结果就是这个函数的返回值 
    使用场景:我们可以利用该函数来自定义,对象的打印格式
    
    __call__: 会在对象被调用时触发。
    __new__: 会在__init__执行前触发。
    
    class Uderline_func():
    
        x = 100
        # def __new__(cls, *args, **kwargs):
        #
        #     print('在__init__执行之前触发我,造一个空对象!')
    
        def __init__(self):
            print('类加括号调用的时候触发我!')
    
        def __call__(self, *args, **kwargs):
    
            print('对象加括号调用的时候触发我!')
    
        def __str__(self):
    
            print('对象被打印的时候触发我!')
    
            return '必须要写return返回一个字符串!不然报错"TypeError: __str__ returned non-string (type NoneType)"'
    
    
    u = Uderline_func()
    u()
    
    print(u)
    

    []的实现原理

    __getitem__ 当你用中括号去获取属性时 执行
    __setitem__  当你用中括号去设置属性时 执行
    __delitem__ 当你用中括号去删除属性时 执行
    
    
    class Foo:
        def __init__(self,name):
            self.name=name
    
        def __getitem__(self, item):
            print(self.__dict__[item])
    
        def __setitem__(self, key, value):
            self.__dict__[key]=value
    
            # self.age = value  # 也可以给对象添加属性
    
    
        def __delitem__(self, key):
            print('del obj[key]时,我执行')
            self.__dict__.pop(key)
        def __delattr__(self, item):
            print('del obj.key时,我执行')
            self.__dict__.pop(item)
    
    f1=Foo('sb')
    f1['age']=18
    # print(f1.__dict__)
    f1['age1']=19
    # del f1.age1
    # del f1['age']
    f1['name']='alex'
    f1.xxx = 111
    print(f1.__dict__)  # {'name': 'alex', 'age': 18, 'age1': 19, 'xxx': 111}
    
    __slots__是什么:是一个类属性,属性值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性),用于优化对象内存占用,优化的原理,将原本不固定的属性数量,变得固定了。这样的解释器就不会为这个对象创建名称空间,所以__dict__也没了,从而达到减少内存开销的效果 。
    
    
    注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。另外当类中出现了slots时将导致这个类的对象无法在添加新的属性
    
    
    # slots的使用
    class Person:
    
        __slots__ = ["name"]
        def __init__(self,name):
            self.name = name
    
    p =  Person("jck")
    
    # 查看内存占用
    # print(sys.getsizeof(p))
    # p.age = 20 # 无法添加
    
    # dict 没有了
    print(p.__dict__)
    
    __doc__:查看类中注释
    
    class Foo:
        '我是描述信息'
        pass
    print(Foo.__doc__)
    
    class Foo:
        '我是描述信息'
        pass
    
    class Bar(Foo):
        pass
    print(Bar.__doc__) #该属性无法继承给子类
    
     __module__和__class__
     
     __module__:表示当前操作的对象在那个模块
     __class__:表示当前操作的对象的类是什么
    
    class C:
    
        def __init__(self):
            self.name = ‘SB'
    
    from lib.aa import C
    
    obj = C()
    print obj.__module__  # 输出 lib.aa,即:输出模块
    print obj.__class__      # 输出 lib.aa.C,即:输出类
    

    上下文管理

    在python中,上下文可以理解为是一个代码区间,一个范围 ,例如with open 打开的文件仅在这个上下文中有效

    __enter__:表示进入上下文
    __exit__: 表示退出上下文
    

    我们知道在操作文件对象的时候可以这么写

     with open('a.txt') as f:
       '代码块'
    

    上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
            # return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
    
    with Open('a.txt') as f:
        print('=====>执行代码块')
        # print(f,f.name)
        
    '''
    出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
    =====>执行代码块
    with中代码块执行完毕时执行我啊
    '''
    

    exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

    class MyOpen:
    
        def __init__(self,path):
            self.path = path
    
        def __enter__(self):
            self.file = open(self.path,'r',encoding='utf8')
            print('enter...')
            return self
    
    
        def __exit__(self, exc_type, exc_val, exc_tb):
    
            print('exit...')
            # print('错误类型是:%s,错误的内容是:%s,错误的跟踪信息是:%s'%(exc_type,exc_val,exc_tb))
            self.file.close()
            # return True
    with MyOpen('a.txt') as m:
        print('start...')
        print(m.file.read())
        阿斯蒂芬斯蒂芬
        print('over...')
    
    
    '''
    enter...
    start...
    fsdfs
    啦啦啦
    哈哈哈哈!
    exit...
    Traceback (most recent call last):
    错误类型是:<class 'NameError'>,错误的内容是:name '阿斯蒂芬斯蒂芬' is not defined,错误的跟踪信息是:<traceback object at 0x000000000A008048>
      File "G:/Python代码日常/代码发布/day03/test.py", line 21, in <module>
        阿斯蒂芬斯蒂芬
    NameError: name '阿斯蒂芬斯蒂芬' is not defined
    
    '''
    

    如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
            print(exc_type)
            print(exc_val)
            print(exc_tb)
            return True
       with Open('a.txt') as f:
        print('=====>执行代码块')
        raise AttributeError('***着火啦,救火啊***')
    print('0'*100) #------------------------------->会执行
    
    class Open:
        def __init__(self,filepath,mode='r',encoding='utf-8'):
            self.filepath=filepath
            self.mode=mode
            self.encoding=encoding
    
        def __enter__(self):
            # print('enter')
            self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
            return self.f
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            # print('exit')
            self.f.close()
            return True 
        def __getattr__(self, item):
            return getattr(self.f,item)
    
    with Open('a.txt','w') as f:
        print(f)
        f.write('aaaaaa')
        f.wasdf #抛出异常,交给__exit__处理
    

    用途或者说好处:

    1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

    2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

    迭代器协议

    迭代器是指具有__iter__和__next__的对象,可以为对象增加这两个方法来让对象变成一个迭代器 
    
    # 案例
    
    class MyRange:
    
        def __init__(self,start,end,step):
            self.start = start
            self.end = end
            self.step = step
    
        def __iter__(self):
            return self
    
        def __next__(self):
            a = self.start
            self.start += self.step
            if a < self.end:
                return a
            else:
                raise StopIteration
                
    for i in MyRange(1,10,2):
        print(i)
    

    运算符重载

    当我们在使用某个符号时,python解释器都会为这个符号定义一个含义,同时调用对应的处理函数, 当我们需要自定义对象的比较规则时,就可在子类中覆盖 大于 等于 等一系列方法....

    案例:

    原本自定义对象无法直接使用大于小于来进行比较 ,我们可自定义运算符来实现,让自定义对象也支持比较运算符

    class Student(object):
        def __init__(self,name,height,age):
            self.name = name
            self.height = height
            self.age = age
    
        def __gt__(self, other):
            # print(self)
            # print(other)
            # print("__gt__")
            return self.height > other.height
        
        def __lt__(self, other):
            return self.height < other.height
    
        def __eq__(self, other):
            if self.name == other.name and  self.age == other.age and self.height == other.height:
                return True
            return False
    
    stu1 = Student("jack",180,28)
    stu2 = Student("jack",180,28)
    # print(stu1 < stu2)
    print(stu1 == stu2)
    

    上述代码中,other指的是另一个参与比较的对象,

    大于和小于只要实现一个即可,符号如果不同 解释器会自动交换两个对象的位置

    单例模式

    单例模式:多次实例化的结果指向同一个实例

    方式1

    # @classmethod(用类绑定方法)
    
    import settings
    
    class MySQL:
        __instance=None
        def __init__(self, ip, port):
            self.ip = ip
            self.port = port
    
        @classmethod
        def from_conf(cls):
            if cls.__instance is None:
                cls.__instance=cls(settings.IP, settings.PORT)
            return cls.__instance
    obj1=MySQL.from_conf()
    obj2=MySQL.from_conf()
    obj3=MySQL.from_conf()
    # obj4=MySQL('1.1.1.3',3302)
    print(obj1)
    print(obj2)
    print(obj3)
    # print(obj4)
    

    方式2

    # 用类装饰器
    import settings
    
    def singleton(cls):
      _instance=cls(settings.IP,settings.PORT)
      def wrapper(*args,**kwargs):
          if len(args) !=0 or len(kwargs) !=0:
              obj=cls(*args,**kwargs)
              return obj
          return _instance
      return wrapper
    
    @singleton #MySQL=singleton(MySQL) #MySQL=wrapper
    class MySQL:
      def __init__(self, ip, port):
          self.ip = ip
          self.port = port
    
    # obj=MySQL('1.1.1.1',3306) #obj=wrapper('1.1.1.1',3306)
    # print(obj.__dict__)
    
    obj1=MySQL() #wrapper()
    obj2=MySQL() #wrapper()
    obj3=MySQL() #wrapper()
    obj4=MySQL('1.1.1.3',3302) #wrapper('1.1.1.3',3302)
    print(obj1)
    print(obj2)
    print(obj3)
    print(obj4)
    

    方式3

    # 调用元类
    import settings
    
    class Mymeta(type):
        def __init__(self,class_name,class_bases,class_dic):
            #self=MySQL这个类
            self.__instance=self(settings.IP,settings.PORT)
    
        def __call__(self, *args, **kwargs):
            # self=MySQL这个类
            if len(args) != 0 or len(kwargs) != 0:
                obj=self.__new__(self)
                self.__init__(obj,*args, **kwargs)
                return obj
            else:
                return self.__instance
    
    class MySQL(metaclass=Mymeta): #MySQL=Mymeta(...)
        def __init__(self, ip, port):
            self.ip = ip
            self.port = port
    
    
    obj1=MySQL()
    obj2=MySQL()
    obj3=MySQL()
    obj4=MySQL('1.1.1.3',3302)
    print(obj1)
    print(obj2)
    print(obj3)
    print(obj4)
    

    方式4

    # 利用模块多次导入只产生一次名称空间,多次导入只沿用第一次导入成果。
    def f1():
        from singleton import instance
        print(instance)
    
    def f2():
        from singleton import instance,My
        SQL
        print(instance)
        obj=MySQL('1.1.1.3',3302)
        print(obj)
    
    f1()
    f2()
    
  • 相关阅读:
    Jquery使用live导致执行的内容会重复执行
    网上找的些tomact配置
    DatePicker 注意点 1.不用v-model 用:value 2.配合on-change进行回调 3.初始值 当天的用 (new Date()).toLocaleDateString().replace(///g, '-')
    Springboot框架 idea 工具 解决代码修改需要重新启动的方式
    合同签订
    ORACLE链接错误解决方案
    oracle中的split
    axis2和springboot冲突问题
    ActiveMQ中文乱码问题
    AXIS2的接口调用常见以及不常见问题
  • 原文地址:https://www.cnblogs.com/zhangchaocoming/p/11706501.html
Copyright © 2011-2022 走看看