请选择 进入手机版 | 继续访问电脑版
查看: 154|回复: 0

一文带你了解Python中的双下方法

[复制链接]

2198

主题

0

回帖

7027

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
7027
发表于 2022-10-9 02:19:28 | 显示全部楼层 |阅读模式
前言

大家在写 Python 代码的时候有没有这样的疑问。
为什么数学中的
  1. +
复制代码
号,在字符串运算中却酿成拼接功能,如
  1. 'ab' + 'cd'
复制代码
结果为
  1. abcd
复制代码
;而
  1. *
复制代码
号酿成了重复功能,如
  1. 'ab' * 2
复制代码
结果为
  1. abab
复制代码

为什么某些对象
  1. print
复制代码
能输出数据,而
  1. print
复制代码
自定义的类对象却输出一堆看不懂的代码
  1. <__main__.MyCls object at 0x105732250>
复制代码

不是因为系统做了特殊定制,而是 Python 中有一类特殊的方法,在某些特定的场所会自动调用。如,在字符串类
  1. str
复制代码
中定义了
  1. __add__
复制代码
方法后,当代码遇到字符串相加
  1. 'ab' + 'cd'
复制代码
时,就会自动调用
  1. __add__
复制代码
方法完成字符串拼接。
因为这类特殊方法的方法名都是以双下划线开始和结束,所以又被称为双下方法。
Python 中的双下方法很多,今天我们对它做个详解。

Python中的双下方法


1. init方法
  1. __init__
复制代码
的方法是很多人接触的第一个
  1. 双下方法
复制代码
  1. class A:
  2.     def __init__(self, a):
  3.         self.a = a
复制代码
当调用
  1. A()
复制代码
实例化对象的时候,
  1. __init__
复制代码
方法会被自动调用,完成对象的初始化。

2. 运算符的双下方法

在类中定义运算符相关的
  1. 双下方法
复制代码
,可以直接在类对象上做加减乘除、比力等操纵。
这里,定义一个尺子类
  1. Rule
复制代码
,它包罗一个属性
  1. r_len
复制代码
代表尺子的长度。
  1. class Rule:
  2.     def __init__(self, r_len):
  3.         self.r_len = r_len
复制代码
2.1 比力运算符

如果想按照尺子的长度对差异的尺子做比力,需要在
  1. Rule
复制代码
类中定义比力运算符。
  1. class Rule:
  2.     def __init__(self, r_len):
  3.         self.r_len = r_len    # < 运算符    def __lt__(self, other):        return self.r_len < other.r_len    # <= 运算符    def __le__(self, other):        return self.r_len <= other.r_len    # > 运算符    def __gt__(self, other):        return self.r_len > other.r_len    # >= 运算符    def __ge__(self, other):        return self.r_len >= other.r_len
复制代码
这里定义了
  1. <
复制代码
  1. <=
复制代码
  1. >
复制代码
  1. >=
复制代码
四个比力运算符,这样就可以用下面的代码比力
  1. Rule
复制代码
对象了。
  1. rule1 = Rule(10)rule2 = Rule(5)print(rule1 > rule2)  # Trueprint(rule1 >= rule2)  # Trueprint(rule1 < rule2)  # Falseprint(rule1 <= rule2)  # False
复制代码
当用
  1. >
复制代码
比力
  1. rule1
复制代码
  1. rule2
复制代码
的时候,
  1. rule1
复制代码
对象会自动调用
  1. __gt__
复制代码
方法,并将
  1. rule2
复制代码
对象传给
  1. other
复制代码
参数,完成比力。
下面是比力运算符的双下方法

比力运算符双下方法


2.2 算术运算符

可以支持类对象加减乘除。
  1. def __add__(self, other):
  2.     return Rule(self.r_len + other.r_len)
复制代码
这里定义了
  1. __add__
复制代码
方法,对应的是
  1. +
复制代码
运算符,他会把两个尺子的长度相加,并生成新的尺子。
  1. rule1 = Rule(10)
  2. rule2 = Rule(5)
  3. rule3 = rule1 + rule2
复制代码
下面是算术运算符的双下方法


2.3 反向算术运算符

它支持其他类型的变量与
  1. Rule
复制代码
类相加。以
  1. __radd__
复制代码
方法为例
  1. def __radd__(self, other):
  2.     return self.r_len + other
复制代码
  1. rule1 = Rule(10)
  2. rule2 = 10 + rule1
复制代码
步伐执行
  1. 10 + rule1
复制代码
时,会实验调用
  1. int
复制代码
类的
  1. __add__
复制代码
  1. int
复制代码
类类没有定义与
  1. Rule
复制代码
类对象相加的方法,所以步伐会调用
  1. +
复制代码
号右边对象
  1. rule1
复制代码
  1. __radd__
复制代码
方法,并把
  1. 10
复制代码
传给
  1. other
复制代码
参数。
所以这种运算符又叫右加运算符。它所支持的运算符与上面的算术运算符一样,方法名前加
  1. r
复制代码
即可。

2.4 增量赋值运算符

增量赋值运算符是
  1. +=
复制代码
  1. -=
复制代码
  1. *=
复制代码
  1. /=
复制代码
等。
  1. def __iadd__(self, other):
  2.     self.r_len += other
  3.     return self
复制代码
  1. rule1 = Rule(10)
  2. rule1 += 5
复制代码
除了
  1. __divmod__
复制代码
方法,其他的跟算数运算符一样,方面名前都加i。

2.4 位运算符

这部分支持按二进制进行取反、移位和与或非等运算。由于
  1. Rule
复制代码
类不涉及位运算,所以我们换一个例子。
定义二进制字符串的类
  1. BinStr
复制代码
,包罗
  1. bin_str
复制代码
属性,表现二进制字符串。
  1. class BinStr:
  2.     def __init__(self, bin_str):
  3.         self.bin_str = bin_str
复制代码
  1. x = BinStr('1010')  #创建二进制字符串对象
  2. print(x.bin_str) # 1010
复制代码
  1. BinStr
复制代码
定义一个取反运算符
  1. ~
复制代码
  1. # ~ 运算符
  2. def __invert__(self):
  3.     inverted_bin_str = ''.join(['1' if i == '0' else '0' for i in self.bin_str])
  4.     return BinStr(inverted_bin_str)
复制代码
  1. __invert__
复制代码
方法中,遍历
  1. bin_str
复制代码
字符串,将每位取反,并返回一个新的
  1. BinStr
复制代码
类对象。
  1. x = BinStr('1011')

  2. invert_x = ~x
  3. print(invert_x.bin_str) # 0100
复制代码
下面是位运算符的双下方法

这部分也支持反向位运算符和增量赋值位运算符,规则跟算数运算符一样,这里就不再赘述。

3.字符串表现

这部分涉及两个双下方法
  1. __repr__
复制代码
  1. __format__
复制代码
,在某些特殊场景,如
  1. print
复制代码
,会自动调用,将对象转成字符串。
还是以
  1. BinStr
复制代码
为例,先写
  1. __repr__
复制代码
方法。
  1. def __repr__(self):
  2.     decimal = int('0b'+self.bin_str, 2)
  3.     return f'二进制字符串:{self.bin_str},对应的十进制数字:{decimal}'
复制代码
  1. x = BinStr('1011')
  2. print(x)
  3. # 输出:二进制字符串:1011,对应的十进制数字:11
复制代码
当步伐执行
  1. print(x)
复制代码
时,会自动调用
  1. __repr__
复制代码
方法,获取对象
  1. x
复制代码
对应的字符串。
再写
  1. __format__
复制代码
方法,它也是将对象格式化为字符串。
  1. def __format__(self, format_spec):
  2.     return format_spec % self.bin_str
复制代码
  1. print('{0:二进制字符串:%s}'.format(x))
  2. # 输出:二进制字符串:1011
复制代码
  1. .format
复制代码
方法的前面字符串里包罗
  1. 0:
复制代码
时,就会自动调用
  1. __format__
复制代码
方法,并将字符串传给
  1. format_spec
复制代码
参数。

4.数值转换

调用
  1. int(obj)
复制代码
  1. float(obj)
复制代码
等方法,可以将对象转成相对应数据类型的数据。
  1. def __int__(self):
  2.     return int('0b'+self.bin_str, 2)
复制代码
  1. x = BinStr('1011')
  2. print(int(x))
复制代码
当调用
  1. int(x)
复制代码
时,会自动调用
  1. __int__
复制代码
方法,将二进制字符串转成十进制数字。
数值转换除了上面的两个外,还有
  1. __abs__
复制代码
  1. __bool__
复制代码
  1. __complex__
复制代码
  1. __hash__
复制代码
  1. __index__
复制代码
  1. __str__
复制代码
  1. __str__
复制代码
  1. __repr__
复制代码
一样,在
  1. print
复制代码
时都会被自动调用,但
  1. __str__
复制代码
优先级更高。

5.集合相关的双下方法

这部分可以像集合那样,定义对象长度、获取某个位置元素、切片等方法。
  1. __len__
复制代码
  1. __getitem__
复制代码
为例
  1. def __len__(self):
  2.     return len(self.bin_str)

  3. def __getitem__(self, item):
  4.     return self.bin_str[item]
复制代码
  1. x = BinStr('1011')

  2. print(len(x))  # 4
  3. print(x[0])  # 1
  4. print(x[0:3])  # 101
复制代码
  1. len(x)
复制代码
会自动调用
  1. __len__
复制代码
返回对象的长度。
通过
  1. []
复制代码
方式获取对象的元素时,会自动调用
  1. __getitem__
复制代码
方法,并将切片对象传给
  1. item
复制代码
参数,即可以获取单个元素,还可以获取切片。
集合相关的双下方法还包罗
  1. __setitem__
复制代码
  1. __delitem__
复制代码
  1. __contains__
复制代码


6.迭代相关的双下方法

可以在对象上使用
  1. for-in
复制代码
遍历。
  1. def __iter__(self):    self.cur_i = -1    return selfdef __next__(self):    self.cur_i += 1    if self.cur_i >= len(self.bin_str):        raise StopIteration()  # 退出迭代    return self.bin_str[self.cur_i]
复制代码
  1. x = BinStr('1011')
  2. for i in x:
  3.     print(i)
复制代码
当在
  1. x
复制代码
上使用
  1. for-in
复制代码
循环时,会先调用
  1. __iter__
复制代码
方法将游标
  1. cur_i
复制代码
置为初始值
  1. -1
复制代码
,然后不绝调用
  1. __next__
复制代码
方法遍历
  1. self.bin_str
复制代码
中的每一位。
这部分还有一个
  1. __reversed__
复制代码
方法用来反转对象。
  1. def __reversed__(self):
  2.     return BinStr(''.join(list(reversed(self.bin_str))))
复制代码
  1. x = BinStr('1011')
  2. reversed_x = reversed(x)
  3. print(reversed_x)
  4. # 输出:二进制字符串:1101,对应的十进制数字:13
复制代码
7.类相关的双下方法

做 web 开发的朋友,用类相关的双下方法会更多一些。

7.1 实例的创建和销毁

实例的创建是
  1. __new__
复制代码
  1. __init__
复制代码
方法,实例的销毁是
  1. __del__
复制代码
方法。
  1. __new__
复制代码
的调用早于
  1. __init__
复制代码
,它的作用是创建对象的实例(内存开辟一段空间),而后才将该实例传给
  1. __init__
复制代码
方法,完成实例的初始化。
由于
  1. __new__
复制代码
是类静态方法,因此它可以控制对象的创建,从而实现单例模式
  1. __del__
复制代码
方法在实例销毁时,被自动调用,可以用来做一些清理工作和资源释放的工作。

7.2 属性管理

类属性的访问和设置。包罗
  1. __getattr__
复制代码
  1. __getattribute__
复制代码
  1. __setattr__
复制代码
  1. __delattr__
复制代码
方法。
  1. __getattr__
复制代码
  1. __getattribute__
复制代码
的区别是,当访问类属性时,无论属性存不存在都会调用
  1. __getattribute__
复制代码
方法,只有当属性不存在时才会调用
  1. __getattr__
复制代码
方法。

7.3 属性描述符

控制属性的访问,一般用于把属性的取值控制在合理范围内。包罗
  1. __get__
复制代码
  1. __set__
复制代码
  1. __delete__
复制代码
方法。
  1. class XValidation:    def __get__(self, instance, owner):        return self.x    def __set__(self, instance, value):        if 0 <= value <= 100:            self.x = value        else:            raise Exception('x不能小于0,不能大于100')    def __delete__(self, instance):        print('删除属性')class MyCls:    x = XValidation()    def __init__(self, n):        self.x = nobj = MyCls(10)obj.x = 101print(obj.x) # 抛异常:Exception: x不能小于0,不能大于100
复制代码
上述例子,通过类属性描述符,可以将属性x的取值控制在
  1. [0, 100]
复制代码
之前,防止不合法的取值。

8.总结

虽然上面介绍的不是所有的双下方法,但也算是绝大多数了。
虽然双下方法里可以编写任意代码,但大家尽量编写与方法要求一样的代码。如,在
  1. __add__
复制代码
方法实现的不是对象相加而是相减,虽然也能运行,但这样会造成很大困惑,倒霉于代码维护。
到此这篇关于一文带你了解Python中的双下方法的文章就介绍到这了,更多相关Python双下方法内容请搜索趣UU以前的文章或继续浏览下面的相关文章希望大家以后多多支持趣UU!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
打赏作者
  • 0
  • 0
  • 0
  • 0
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表