面向对象常用魔法方法(内置方法)合集, 超级无敌宇宙详细

引入

众所周知,方法是需要调用执行的,而魔法方法则不一样,他无需你的调用,在特定的时候会自己执行, 例如我们之前所学的__init__, 在示例对象 ([类名]+()) 的时候触发执行它

1.什么是内置方法

  • 定义在类的内部, 以双下滑线开头__, 以双下滑线__结尾的方法
  • 特点 : 在某种情况下自动触发执行

2.为什么使用内置方法

  • 为了高度定制化我们使用的类或者实例

一.点拦截方法__getattr__,__setattr__,__delattr__

  • __setattr__ : 在 [对象].[属性] = [值] 设置属性值的时候就会触发它的执行
  • __getattr__ : 在 [对象].[属性] 获取属性不存在的时候会触发它的执行
  • __delattr__ : 在 del [对象].[属性] 删除属性的时候会触发它的执行
class Panda:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        print('通过点的方式找属性没有找到, 于是触发了我')

    def __setattr__(self, key, value):
        print('你通过点的方式修改属性值,触发了我')
        # 📛self.key=value
        # 📛如此设置值得时候(又通过点的方式设置), 于是又触发__setattr__的执行, 又开始设置,无限递归了
        self.__dict__[key]=value # 可以通过操作对象字典 key = value 方式设置

    def __delattr__(self, item):
        print('通过点的方式删除属性,触发了我')
        # 📛del self.item  # 与上面一样, 无限递归了
        self.__dict__.pop(item)  # 同样通过操作对象字典的方式去删属性

# 🔰实例对象的初始化方法其实也进行了设置属性值的操作
P = Panda("hangd")  # 你通过点的方式修改属性值,触发了我

# 🔰获取一个不存在的属性
P.age               # 通过点的方式找属性没有找到, 于是触发了我

# 🔰设置属性值
P.age = 99          # 你通过点的方式修改属性值,触发了我
print(P.__dict__)   # {'name': 'hangd', 'age': 99}

# 🔰删除属性
del P.name          # 通过点的方式删除属性,触发了我
print(P.__dict__)   # {'age': 99}
  • 应用小示例 : 重写字典, 实现字典可以通过点 . 取值赋值, 也可以 [] 取值赋值
class MyDict(dict):
    def __setattr__(self, key, value):
        self[key] = value

    def __getattr__(self, item):
        return self[item]

DD = MyDict()
print(DD)  # {}

# 🔰点取值赋值
DD.name = "shawn"
print(DD.name)    # shawn
print(DD)         # {'name': 'shawn'}

# 🔰[] 取值赋值
DD["age"] = 20
print(DD["age"])  # 20
print(DD)         # {'name': 'shawn', 'age': 20}

二. __getattribute__

1. 先看看 : __getattr__

  • 点 . 属性 没找到触发
class Bar:

    def __getattr__(self, item):
        print("没找到,触发了我")

bb = Bar()
bb.name    # 没找到,触发了我

2.__getattribute__

  • 点 . 属性 无论找没找到都触发
class Bar:
    def __init__(self,name):
        self.name = name

    def __getattribute__(self, item):
        print(f"无论找没找到,都触发了我-->{item}")

bb = Bar("shawn")
bb.name  # 无论找没找到,都触发了我-->name
bb.age   # 无论找没找到,都触发了我-->age

3.两者同时存在

🍔两者同时存在
class Bar:
    def __init__(self,name):
        self.name = name

    def __getattr__(self, item):
        print("没找到,触发了我")

    def __getattribute__(self, item):
        print(f"无论找没找到,都触发了我-->{item}")

bb = Bar("shawn")
bb.name  # 无论找没找到,都触发了我-->name
bb.age   # 无论找没找到,都触发了我-->age

🍔设置异常
class Bar:
    def __init__(self,name):
        self.name = name

    def __getattr__(self, item):
        print("没找到,触发了我")

    def __getattribute__(self, item):
        print(f"无论找没找到,都触发了我-->{item}")
        raise AttributeError('让小弟接管')  # 设置异常,直接交给__getattr__

bb = Bar("shawn")
bb.name
'''
无论找没找到,都触发了我-->name
没找到,触发了我
'''
bb.age
'''
无论找没找到,都触发了我-->age
没找到,触发了我
'''
  • [对象] . [属性] 的调用顺序 : 先执行 __getattribute__—>去类的名称空间找—>__getattr__(本质是去对象自己的名称空间找)
  • [对象] . [属性] 的查找顺序 : 对象自己**—>—>父类—>**父类

4.总结

  • __getattribute__方法优先级比__getattr__
  • 没有重写__getattribute__的情况下, 默认使用的是父类的__getattribute__方法
  • 只有在使用默认__getattribute__方法中找不到对应的属性时,才会调用__getattr__
  • 如果是对不存在的属性做处理,尽量把逻辑写在__getattr__方法中
  • 如果非得重写__getattribute__方法,需要注意两点:
    • 第一是避免.操作带来的死循环
    • 第二是不要遗忘父类的__getattribute__方法在子类中起的作用

三.item系列 __getitem__,__setitem__,__delitem__

  • __getitem__ : 通过中括号取值, 触发它的执行

  • __setitem__ : 通过中括号赋值, 触发它的执行

  • __delitem__ : 通过中括号删值, 触发它的执行

class Person:
    def __getitem__(self, item):
        print('取值操作,触发了执行')

    def __setitem__(self, key, value):
        print('赋值操作,触发了执行')

    def __delitem__(self, key):
        print('删值操作,触发了执行')

p=Person()
p['name'] = "派小星"  # 赋值操作,触发了执行
p['name']            # 取值操作,触发了执行
del p["name"]        # 删值操作,触发了执行
  • 小示例 : 在不继承字典类的情况下, 自定义一个字典, 支持 [ ] 取值赋值
class Mydict:
    def __init__(self,**kwargs):
        # 🔰方案一
        # for key,value in kwargs.items():
        #     setattr(self,key,value)

        # 🔰方案三
        # for key,value in kwargs.items():
        #     self.__dict__[key]=value

        # 🔰方案二
        self.__dict__.update(kwargs)

    def __getitem__(self, item):
        # 🔰方案一
        # return self.__dict__[item]

        # 🔰方案二
        return getattr(self,item)

    def __setitem__(self, key, value):
        # 🔰方案一
        # setattr(self,key,value)

        # 🔰方案二
        self.__dict__[key]=value

    def __delitem__(self, key):
        del self.__dict__[key]

dd = Mydict()
print(dd.__dict__)    # {}

dd["name"] = "shawn"
print(dd.__dict__)    # shawn

print(dd["name"])     # shawn

四. __format__

自定义格式化字符串

1.format( ) 函数

前面 字符串类型的内置方法 已经详细的介绍了 format( ) 函数的玩法, 下面简单回顾一下

🍉直接传变量名
res="my name is {name} my age is {age}".format(age=18,name="shawn")
print(res)  #my name is shawn my age is 18

🍉不放任何值, 让其按位置自动传值
res="my name is {} my age is {}".format(18,"shawn")
print(res)  #my name is 18 my age is shawn

🍉通过索引传值
res="my name is {0}{0}{0} my age is {1}{0}".format(18,"shawn")
print(res)  #my name is 181818 my age is shawn18

2.__format__ 方法

  • 其实我们使用format( ) 函数的时候触发的就是 __format__ 方法
class For:
    def __format__(self, format_spec):
        print(type(format_spec))  # 看看这个参数是什么类型
        print(f"使用format()函数,触发了我-->({format_spec})")
        return "aaa"   # 必须返回一个字符串(不然报错),可以是任意的字符串(后面可以使用此特性来操作自定义)

F = For()

🔰当第二个参数不传入时默认为空
format(F)
'''输出
<class 'str'>
使用format()函数,触发了我-->()
'''
🔰传参类型 "str"
format(F,"你好")
'''输出
<class 'str'>
使用format()函数,触发了我-->(你好)

3.小示例 : 制作一个输出日期类, 实例的时候传入年,月,日, 可以自定义格式

class Date:
    __format_dic = {
   
        "1": "{obj.year}-{obj.mon}-{obj.day}",
        "2": "{obj.year}:{obj.mon}:{obj.day}",
        "3": "{obj.year}/{obj.mon}/{obj.day}"
    }
    def __init__(self,Year,Mon,Day):
        self
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给你骨质唱疏松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值