zoukankan      html  css  js  c++  java
  • Python()-类的专有方法之双下划线方法

    1. __call__() 方法

    对象+() 可以直接调用__call__()方法 , 类似普通函数的调用

    class CallTest(object):
        def __init__(self):
            print('I am __init__')
    
        def __call__(self):
            print('I am __call__')
            return True
    
        def run(self):
            print('I am run')
            return True
    
    
    obj = CallTest()
    obj.run()  # 调用普通方法        对象.func_name()
    obj()  # 调用__call__()方法, 直接    对象()

    打印结果:

    I am __init__

    I am run

    I am __call__

    可以看到,obj这个对象被实例化出来,如果要调用__call__方法的话,直接obj(),即可调用并返回结果。obj就类似一个函数地址,obj()即执行这个函数。

    2. __init__() 方法

    构造函数,在生成对象时调用

    ===========================

    __getattr__, __setattr__, __delattr__

    1. 调用对象的一个不存在的属性时会触发__getattr__方法 

    2. 删除对象的一个属性的时候会触发__delattr__方法 

    3. 设置对象(增加/修改)属性会触发__setattr__方法

    设置对象属性和删除对象属性会触发__setattr__ 和 __delattr__ 方法,但要注意的是,在调用这两个方法时,方法内部必须操作类的属性字典,否则会造成无限递归

    3. __getattr__() 方法

    ----调用(获取)对象属性

    class Foo:
        a = 1
    
        def __getattr__(self, item):
            print('run __getattr__')
    
    
    f = Foo()
    print(f.a)  # 属性存在,就不会触发__getattr__()方法
    # >> 输出: 1
    
    print(f.b)  # 只有在使用点调用属性且属性不存在的时候才会触发,并且返回None
    # >> 输出: run __getattr__
    # >> 输出: None

    4. __delattr__() 方法

    ----删除对象属性

    class Foo:
        def __delattr__(self, item):
            print('run __delattr__')
            # del self.item             # 这样会造成无限递归
            self.__dict__.pop(item)
    
    
    f = Foo()
    f.a = 3
    print(f.__dict__)  # >> 输出: {'a': 3}
    print(f.a)  # >> 输出: 3
    del f.a  # >> 输出: run __delattr__
    print(f.a)  # >> 报错: AttributeError: 'Foo' object has no attribute 'a'

    5. __setattr__() 方法

      ----设置属性: 增加对象属性, 修改对象属性

    class Foo:
        a = 1
    
        def __setattr__(self, key, value):
            print("run __setattr__")
    
    
    f = Foo()
    # 没有赋值,什么都不会发生
    
    f.c = 200  # 如果增加类属性, 触发触发__setattr__()方法
    # >> 输出: run__setattr__
    
    f.a = 2  # 如果修改类属性, 触发触发__setattr__()方法
    # >> 输出: run __setattr__

    实例化对象传参,会触发__getattr__方法

    class Foo:
        a = 1
    
        def __init__(self, b):
            self.b = b  # 赋值属性操作
    
        def __setattr__(self, key, value):
            print("run __setattr__")
    
    
    f = Foo(100)  # 如果实例化的时候传入参数进行赋值属性操作,  触发__setattr__()方法
    # >> 输出: run __setattr__

    设置属性时, 方法内部必须操作类的属性字典

    class Foo:
        a = 1
    
        def __setattr__(self, key, value):
            # self.key = value  # 增加/修改类属性,会触发__setattr__()方法,如果这个操作在setattr方法内部,会造成无限递归
            self.__dict__[key] = value  # 使用这种方法会完成增加/修改类属性的操作
            print("run __setattr__")
    
    
    f = Foo()
    f.y = 3  # 增加/修改类属性,调用__setattr__()方法
    # >> 输出: run __setattr__
    
    print(f.__dict__)
    # >> 输出: {'y': 3}

    当我们重写__setattr__()方法后,方法内部如果不进行属性字典的操作,那么除非直接操作属性字典,否则永远无法完成赋值

    class Foo:
    
        def __setattr__(self, key, value):
            print("run __setattr__")
    
    
    f = Foo()
    f.y = 3  # 设置对象属性,调用__setattr__()方法,而__setattr__()方法什么都没干,所以完成不了对象的设置属性操作
    # >> 输出: run __setattr__
    
    print(f.__dict__)
    # >> 输出: {}
    
    print(f.y)  # 完成不了赋值
    # >> 报错: AttributeError: 'Foo' object has no attribute 'y'

    理解了__setattr__()方法的原理,我们就可以利用 __setattr__()方法 实现我们自定义的功能

    class Foo:
        a = 1
        dic = {}  # 自定义一个空字典
    
        def __setattr__(self, key, value):
            self.dic[key] = value
            print("run __setattr__")
    
    
    f = Foo()
    f.y = 3
    # >> 输出: run __setattr__
    
    print(f.dic)  # 给类变量dic添加键值对
    # >> 输出: {'y': 3}
    
    print(f.__dict__)  # 类属性不发生变化
    # >> 输出: {}

    一个小示例:

    class Foo:
        def __init__(self, dic):
            self._dic = dic
    
        def __getattr__(self, item):
            val = self._dic[item]
            if isinstance(val, dict):
                a = Foo(val)
                return a  # 重点: 又返回一个对象
            else:
                return val
    
    
    dic = {'k1': 'v1', 'k2': 'v2'}
    dic = Foo(dic)
    
    print(dic.k1)
    # >>输出: v1
    
    print(dic.k2)
    # >>输出: v2
    
    dic = {'k1': {'k2': 'v2'}}
    dic = Foo(dic)
    print(dic.k1)
    # >>输出: 一个对象 <__main__.Foo object at 0x00000000024D7F98>
    
    print(dic.k1.k2)  # 对象可以继续点(.)取属性操作
    # >>输出: v2

    原理:
      Foo(dic)实例化一个对象, dic.k1触发__getattr__()方法, val={'k2': 'v2'},当val值为一个字典对象时,if条件成立, 返回一个以val字典为参数的对象,就是说: dic.k1 == Foo({'k2': 'v2'}),这个对象可以继续通过点(.)调用对象的属性,如果有多层嵌套,一直循环下去

    接着上面的例子继续:

    def v2(arg):
        return arg
    
    
    dic = {'k1': {'k2': v2}}
    dic = Foo(dic)
    ret = dic.k1.k2(100)
    print(ret)
    # >> 输出: 100

    6. __getattribute__() 方法

    长得和__getattr__那么像,那么__getattribute__与之有什么关系呢?

    class Foo:
        a = 1
    
        def __init__(self, x):
            self.x = x
    
        def __getattribute__(self, item):
            print('不管属性[%s]是否存在,我都会执行' % item)
    
    
    f = Foo(100)
    print(f.a)
    # >>输出: 不管属性[a]是否存在,我都会执行
    # >>输出: None
    
    print(f.b)
    # >>输出: 不管属性[b]是否存在,我都会执行
    # >>输出: None
    
    print(f.x)
    # >>输出: 不管属性[x]是否存在,我都会执行
    # >>输出: None

    当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

    class Foo:
    
        def __getattr__(self, item):
            print('run __getattr__')
    
        def __getattribute__(self, item):
            print('不管属性[%s]是否存在,我都会执行' % item)
            # raise AttributeError('啦啦啦啦')
    
    
    f = Foo()
    # print(f.a)
    # >>输出: 不管属性[a]是否存在,我都会执行
    # >>输出: None
    
    print(f.a)  # 打开注释,手动抛错: raise AttributeError('q')
    # >>输出: 不管属性[a]是否存在,我都会执行
    # >>输出: run __getattr__
    # >>输出: None

    7. super()

    super 的工作原理如下:

    def super(cls, inst):
        mro = inst.__class__.mro()
        return mro[mro.index(cls) + 1]

    其中 cls 代表类, inst 代表实例, super 函数做了两件事:

    1. 获取实例对象 inst 的类的 MRO 列表

    2. 查找 cls 在当前 MRO 列表中的 index ,并返回它的下一个类,即 mro[index + 1]

    当使用 super(cls, inst) 时, Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类. 可以看出, 事实上 super 函数和父类没有实质性的关联.

  • 相关阅读:
    Manacher算法(一个字符串中找到最长回文子串)
    tomcat之负载均衡(apache反响代理tomcat)
    【转】Tomcat集群Cluster实现原理剖析
    负载均衡集群之LVS持久链接
    负载均衡集群之LVS的DR模型详解(Diretor Routing)
    负载均衡集群之LVS算法和模型
    负载均衡集群之LVS配置命令
    mysql之6备份恢复
    mysql之主从复制
    haproxy之配置文件解析
  • 原文地址:https://www.cnblogs.com/zhzhlong/p/12854255.html
Copyright © 2011-2022 走看看