程序员必备接口测试调试工具:立即使用
Apipost = Postman + Swagger + Mock + Jmeter
Api设计、调试、文档、自动化测试工具
后端、前端、测试,同时在线协作,内容实时同步
【相关推荐:Python3视频教程 】
前言
大家在写 Python 代码的时候有没有这样的疑问。
为什么数学中的+
号,在字符串运算中却变成拼接功能,如'ab' + 'cd'
结果为abcd
;而*
号变成了重复功能,如'ab' * 2
结果为abab
。
为什么某些对象print
能输出数据,而print
自定义的类对象却输出一堆看不懂的代码<__main__.MyCls object at 0x105732250>
。
不是因为系统做了特殊定制,而是 Python 中有一类特殊的方法,在某些特定的场合会自动调用。如,在字符串类str
中定义了__add__
方法后,当代码遇到字符串相加'ab' + 'cd'
时,就会自动调用__add__
方法完成字符串拼接。
因为这类特殊方法的方法名都是以双下划线开始和结束,所以又被称为双下方法。
Python 中的双下方法很多,今天我们对它做个详解。
Python中的双下方法
1. init方法
__init__
的方法是很多人接触的第一个双下方法
。
class A:
def __init__(self, a):
self.a = a
登录后复制
当调用A()
实例化对象的时候,__init__
方法会被自动调用,完成对象的初始化。
2. 运算符的双下方法
在类中定义运算符相关的双下方法
,可以直接在类对象上做加减乘除、比较等操作。
这里,定义一个尺子类Rule
,它包含一个属性r_len
代表尺子的长度。
class Rule:
def __init__(self, r_len):
self.r_len = r_len
登录后复制
2.1 比较运算符
如果想按照尺子的长度对不同的尺子做比较,需要在Rule
类中定义比较运算符。
class Rule:
def __init__(self, r_len):
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
登录后复制
这里定义了<
、<=
、>
和>=
四个比较运算符,这样就可以用下面的代码比较Rule
对象了。
rule1 = Rule(10)
rule2 = Rule(5)
print(rule1 > rule2) # True
print(rule1 >= rule2) # True
print(rule1 < rule2) # False
print(rule1 <= rule2) # False
登录后复制
当用>
比较rule1
和rule2
的时候,rule1
对象会自动调用__gt__
方法,并将rule2
对象传给other
参数,完成比较。
下面是比较运算符的双下方法
比较运算符双下方法
2.2 算术运算符
可以支持类对象加减乘除。
def __add__(self, other):
return Rule(self.r_len + other.r_len)
登录后复制
这里定义了__add__
方法,对应的是+
运算符,他会把两个尺子的长度相加,并生成新的尺子。
rule1 = Rule(10)
rule2 = Rule(5)
rule3 = rule1 + rule2
登录后复制
下面是算术运算符的双下方法
2.3 反向算术运算符
它支持其他类型的变量与Rule
类相加。以__radd__
方法为例
def __radd__(self, other):
return self.r_len + other
登录后复制
rule1 = Rule(10)
rule2 = 10 + rule1
登录后复制
程序执行10 + rule1
时,会尝试调用int
类的__add__
但int
类类没有定义与Rule
类对象相加的方法,所以程序会调用+
号右边对象rule1
的__radd__
方法,并把10
传给other
参数。
所以这种运算符又叫右加运算符。它所支持的运算符与上面的算术运算符一样,方法名前加r
即可。
2.4 增量赋值运算符
增量赋值运算符是+=
、-=
、*=
、/=
等。
def __iadd__(self, other):
self.r_len += other
return self
登录后复制
rule1 = Rule(10)
rule1 += 5
登录后复制
除了__pmod__
方法,其他的跟算数运算符一样,方面名前都加i。
2.4 位运算符
这部分支持按二进制进行取反、移位和与或非等运算。由于Rule
类不涉及位运算,所以我们换一个例子。
定义二进制字符串的类BinStr
,包含bin_str
属性,表示二进制字符串。
class BinStr:
def __init__(self, bin_str):
self.bin_str = bin_str
登录后复制
x = BinStr('1010') #创建二进制字符串对象
print(x.bin_str) # 1010
登录后复制
给BinStr
定义一个取反运算符~
# ~ 运算符
def __invert__(self):
inverted_bin_str = ''.join(['1' if i == '0' else '0' for i in self.bin_str])
return BinStr(inverted_bin_str)
登录后复制
__invert__
方法中,遍历bin_str
字符串,将每位取反,并返回一个新的BinStr
类对象。
x = BinStr('1011')
invert_x = ~x
print(invert_x.bin_str) # 0100
登录后复制
下面是位运算符的双下方法
这部分也支持反向位运算符和增量赋值位运算符,规则跟算数运算符一样,这里就不再赘述。
3.字符串表示
这部分涉及两个双下方法__repr__
和__format__
,在某些特殊场景,如print
,会自动调用,将对象转成字符串。
还是以BinStr
为例,先写__repr__
方法。
def __repr__(self):
decimal = int('0b'+self.bin_str, 2)
return f'二进制字符串:{self.bin_str},对应的十进制数字:{decimal}'
登录后复制
x = BinStr('1011')
print(x)
# 输出:二进制字符串:1011,对应的十进制数字:11
登录后复制
当程序执行print(x)
时,会自动调用__repr__
方法,获取对象x
对应的字符串。
再写__format__
方法,它也是将对象格式化为字符串。
def __format__(self, format_spec):
return format_spec % self.bin_str
登录后复制
print('{0:二进制字符串:%s}'.format(x))
# 输出:二进制字符串:1011
登录后复制
当.format
方法的前面字符串里包含0:
时,就会自动调用__format__
方法,并将字符串传给format_spec
参数。
4.数值转换
调用int(obj)
、float(obj)
等方法,可以将对象转成相对应数据类型的数据。
def __int__(self):
return int('0b'+self.bin_str, 2)
登录后复制
x = BinStr('1011')
print(int(x))
登录后复制
当调用int(x)
时,会自动调用__int__
方法,将二进制字符串转成十进制数字。
数值转换除了上面的两个外,还有__abs__
、__bool__
、__complex__
、__hash__
、__index__
和__str__
。
__str__
和__repr__
一样,在print
时都会被自动调用,但__str__
优先级更高。
5.集合相关的双下方法
这部分可以像集合那样,定义对象长度、获取某个位置元素、切片等方法。
以__len__
和__getitem__
为例
def __len__(self):
return len(self.bin_str)
def __getitem__(self, item):
return self.bin_str[item]
登录后复制
x = BinStr('1011')
print(len(x)) # 4
print(x[0]) # 1
print(x[0:3]) # 101
登录后复制
len(x)
会自动调用__len__
返回对象的长度。
通过[]
方式获取对象的元素时,会自动调用__getitem__
方法,并将切片对象传给item
参数,即可以获取单个元素,还可以获取切片。
集合相关的双下方法还包括__setitem__
、__delitem__
和__contains__
。
6.迭代相关的双下方法
可以在对象上使用for-in
遍历。
def __iter__(self):
self.cur_i = -1
return self
def __next__(self):
self.cur_i += 1
if self.cur_i >= len(self.bin_str):
raise StopIteration() # 退出迭代
return self.bin_str[self.cur_i]
登录后复制
x = BinStr('1011')
for i in x:
print(i)
登录后复制
当在x
上使用for-in
循环时,会先调用__iter__
方法将游标cur_i
置为初始值-1
,然后不断调用__next__
方法遍历self.bin_str
中的每一位。
这部分还有一个__reversed__
方法用来反转对象。
def __reversed__(self):
return BinStr(''.join(list(reversed(self.bin_str))))
登录后复制
x = BinStr('1011')
reversed_x = reversed(x)
print(reversed_x)
# 输出:二进制字符串:1101,对应的十进制数字:13
登录后复制
7.类相关的双下方法
做 web 开发的朋友,用类相关的双下方法会更多一些。
7.1 实例的创建和销毁
实例的创建是__new__
和__init__
方法,实例的销毁是__del__
方法。
__new__
的调用早于__init__
,它的作用是创建对象的实例(内存开辟一段空间),而后才将该实例传给__init__
方法,完成实例的初始化。
由于__new__
是类静态方法,因此它可以控制对象的创建,从而实现单例模式。
__del__
方法在实例销毁时,被自动调用,可以用来做一些清理工作和资源释放的工作。
7.2 属性管理
类属性的访问和设置。包括__getattr__
、__getattribute__
、__setattr__
和__delattr__
方法。
__getattr__
和__getattribute__
的区别是,当访问类属性时,无论属性存不存在都会调用__getattribute__
方法,只有当属性不存在时才会调用__getattr__
方法。
7.3 属性描述符
控制属性的访问,一般用于把属性的取值控制在合理范围内。包括__get__
、__set__
和__delete__
方法。
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 = n
obj = MyCls(10)
obj.x = 101
print(obj.x) # 抛异常:Exception: x不能小于0,不能大于100
登录后复制
上述例子,通过类属性描述符,可以将属性x的取值控制在[0, 100]
之前,防止不合法的取值。
8.总结
虽然上面介绍的不是所有的双下方法,但也算是绝大多数了。
虽然双下方法里可以编写任意代码,但大家尽量编写与方法要求一样的代码。如,在__add__
方法实现的不是对象相加而是相减,虽然也能运行,但这样会造成很大困惑,不利于代码维护。
【相关推荐:Python3视频教程 】
以上就是完全掌握Python中的双下方法的详细内容,更多请关注自由互联其它相关文章!