python 面向对象之魔法方法(双下方法)

面向对象之魔法方法(双下方法)

魔法方法指类中定义的一些双下方法,不需要人为调用,在特定的条件下回自动触发运行。

类中的双下方法常常是类产生的对象的一些行为所触发的。

类的常用魔法方法

双下方法 自动触发条件
__init__ 对象添加独有数据的时候自动触发
__str__ 对象被执行打印操作的时候自动触发
__call__ 对象加括号调用的时候自动触发
__getattr__ 访问对象不存在的属性时自动触发
__getattribute__ 只要访问对象的名字就会触发(不常用)
__setattr__ 给对象添加或者修改数据的时候自动触发(会使原本的属性添加或修改失效)
__enter__ 当对象被当做with上下文管理操作的开始自动触发 并且该方法返回什么 as后面的变量名就会接收到什么
__exit__ 与enter相对,在上下文管理结束时触发
  1. __init__

    class A:
        def __init__(self, a, b):
            self.a = a
            self.b = b
    obj = A(11, 22)
    print(obj.__dict__)
    

    我们可以简单的理解为,在用类名()调用时会触发双下init

  2. __str__

    class B:
        def __str__(self):
            return '我是类B'   # 这个方法必须得有一个返回值
        
    obj = B()
    print(obj)  # 我是类B
    

    这个方法必须有一个字符串返回值。

    注意这个返回值不能是对象本身或者包含对象的字符串,会导致无限递归。

  3. __call__

    class C:
        cid = 114514
        def __call__(self, *args, **kwargs):
            print(self)  # 可以接收调用的对象本身
            print(args, kwargs)  # 可以接收参数
            return self.cid  # 可以有返回值
        
    obj = C()
    print(obj(11, 22, name='leethon'))  # 当对象被加括号调用时自动触发
    """
    <__main__.C object at 0x0000023919566610>   #  print(self)
    (11, 22) {'name': 'leethon'}  # print(args, kwargs)
    114514  #  return self.cid --> print(返回追)
    """
    

    拥有call双下方法的类,产生的对象可以加括号被调用,调用时自动触发双下__call__来接收参数并返回返回值。

  4. __getattr__

    class D:
        cid = 114514
        def __getattr__(self, item):
            return f'没有{item}这个属性'
    
    obj = D()
    print(obj.cid)  # 114514       # 当有属性时正常拿值
    print(obj.ccid)  # 没有ccid这个属性    # 当没有属性时自动调用getattr双下方法,并得到它的返回值
    
  5. __getattribute__

    class E:
        cid = 114514
    
        def __getattr__(self, item):
            return f'没有{item}这个属性'
    
        def __getattribute__(self, item):
            return '我是老大'
    
    obj = E()  
    print(obj.cid)  # 我是老大
    print(obj.ccid)  # 我是老大
    

    无论有没有这个属性,在拿值时都会触发。

    而且会覆盖双下getattr的触发。一般不用,因为很容易造成递归。

  6. __setattr__

    class F:
        my_flow = []
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __setattr__(self, key, value):
            self.my_flow.append(f'某一个对象添加了键值对{key}:{value}')
    
    
    obj1 = F('leethon', 18)
    obj2 = F('jack', 19)
    print(obj1.__dict__)
    print(F.my_flow)
    """
    {}   # 原本添加属性的动作失效
    ['某一个对象添加了键值对name:leethon', '某一个对象添加了键值对age:18',
    '某一个对象添加了键值对name:jack', '某一个对象添加了键值对age:19']
    """
    

    上述程序中,我们在通过类名()触发了双下init,然后得到运行了self.属性的添加操作,从而触发了双下setattr,往我们的类属性列表中依次添加了几条信息。

  7. __enter____exit__

    class Context:
        def do_something(self):  # 6.开始执行do_something
            print('hehehe')  # 7.第二个被打印的
    
        def __enter__(self):  # 2.上下文管理触发来着
            print('hahaha')  # 3.最先打印
            return self  # 4.将产生的对象返回给f
    
        def __exit__(self, exc_type, exc_val, exc_tb):  # 9.上下文结束时执行
            print('enenen')  # 10.最后被打印
    
    with Context() as f:  # 1.先运行context()产生一个对象,对象再被上下文管理触发enter双下
        f.do_something()  # 5.对象调用绑定对象的动态方法do_something()
        # 8.子代码结束,触发双下exit方法
        
    """运行结果
    hahaha
    hehehe
    enenen
    """
    

    这是一道面试题的改版,代码中也注释了代码运行的顺序。

自定义字典类型并让字典能够通过句点符的方式操作键值对

class MyDict(dict):   # 继承原有的字典类
    def __init__(self, **kwargs):  # 派生原本产生字典对象的方法
        super().__init__(self, **kwargs)

    def __setattr__(self, key, value):  # 设置值的时候会触发这句(并且原本的属性添加失效)
        self[key] = value

    def __getattr__(self, item):  # 拿没有的属性时会触发这句
        return self[item]

d1 = {'name': 'leethon', 'age': '18'}  # 原本的字典定义方式
d = MyDict(**d1)  # 用关键字实参来赋值键值对
print(d.name)  # 可以用句点的方式来拿值
d.name = 'lee'  # 可以用句点的方式改值
print(d.name)

面向对象的更多双下属性和方法

双下方法 自动触发条件
__repr__方法 在解释器环境下输出的字符串(通__str__
__del__方法 当对象在内存中被释放时,自动触发执行
__format__方法 被格式化输出的字符串
__item__系列 对象进行类字典操作时触发
__doc__属性 定义类的描述信息,该信息不会被继承
__iter__和__next__方法 对象成为可迭代对象,在被迭代时自动触发
__len__方法 让对象可以被len(obj),返回长度
__hash__方法 让对象可以被hash(obj),返回重述值(hash意为混杂、拼凑)
__eq__方法 让对象可以定义相等判断
  1. __item__系列

    class Foo:
        def __init__(self, name):
            self.name = name
    
        def __getitem__(self, item):
            print(self.__dict__[item])
    
        def __setitem__(self, key, value):
            print('obj[key]=lqz赋值时,执行我')
            self.__dict__[key] = 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')
    print(f1.__dict__)
    f1['age'] = 18
    f1.hobby = '泡妞'
    del f1.hobby
    del f1['age']
    f1['name'] = 'lqz'
    print(f1.__dict__)
    
  2. __eq__

    class A:
        def __init__(self,x,y):
            self.x = x
            self.y = y
    
        def __eq__(self,obj):
            # 打印出比较的第二个对象的x值
            print(obj.x)
            if self.x +self.y == obj.x+obj.y:
                return True
            else:
                return False
    
    a = A(1,2)
    b = A(2,1)
    print(a == b)
    
posted @ 2022-11-08 15:44  leethon  阅读(45)  评论(0编辑  收藏  举报