目录
- 用字典映射代替其他语言中switch case 语句
- 列表推导式
- None的特殊性与误区
- 自定义对象与bool的对应关系
- 装饰器的副作用
- 编程能力
- 海象运算符
- 用f关键字做字符串拼接
- 装饰器:@dataclass数据类
三、None 的特殊性与误区
1.None与空字符串、空列表、0、False的关系
None与空字符串、空列表、0、False均不相同。两层含义:
- 类型不相同
- 取值不相同
验证
a = ''
b = False
c = []
print(a == None)
print(b == None)
print(c == None) #值是否相等,比较运算符
-->
False
False
False #取值上,None与空字符串、空列表、0、False均不相同
a = ''
b = False
c = []
print(a is None)
print(b is None)
print(c is None) #身份是否相同,身份运算符
print(type(None))
-->
False
False
False #类型上,None与空字符串、空列表、0、False均不相同
<class 'NoneType'> #因为None的类型是 NoneType
2.判空操作
判空的时候,以下两者不能混为一谈:
a
if not a
if a is None
def fun():
return None #函数定义时,经常会返回None
a = fun()
if not a:
print('S')
else:
print('F')
if a is None:
print('S')
else:
print('F')
-->
S
S #上面的两个操作结果相同。
很多人误认为既然上面的两个操作结果相同,就以为两者是一个东西。其实,两者并不是一回事儿:
a = [] #空列表
if not a: # not a是逻辑运算符,第二章:and or not且或非
print('S') # not a就是True。
else:
print('F')
if a is None:
print('S')
else:
print('F')
-->
S #由此可见,两者不是一回事
F
很多同学的判空操作是if a is None
,老师推荐用以下方式:
if a:
if not a:
因为,当
a = None
a = ''
a = []
a = False
时候,都可以得到想要的结果
3.None与False的本质概念
None | False | |
---|---|---|
数据类型 | NoneType型 | bool型 |
本质概念 | 表示不存在 | 表示假 |
有时候,if None
与 if False
得到的结果可能是相同的,但是两者的本质概念是不同的。
同理:
[] #表示这个列表中没有任何元素
'' #表示这个字符串中没有任何元素
或:这个序列里面没有任何的字符
四、自定义对象与bool的对应关系
1.奇怪现象
a = ''
print(a == None)
-->
False
a
与None
的取值是不相同的,但if a
与 if None
走的分支却是相同的。这是为什么?
因为,if 在逻辑判断时,python中的每个对象都与bool有对应关系。例如:
'' # False
[] # False
None # False #他俩的bool值是相同的
那么,自定义对象与bool有对应关系是什么呢?
2.自定义对象与__len__
方法
以我们之前的student类为例:
class Test():
pass
test = Test() #实例化一个对象
if test:
print('S')
-->
S
上面是我们经常判空的一个逻辑误区:自定义对象,一定都是True。即:
如果test存在,那么if test
成立,就能打印出S。
相反,如果test的取值是None时,那么if test
就不成立,就不能打印出S。
(1)那么有没有可能,test依然存在,不是None,但依然打印不出S呢?
有。
class Test():
def __len__(self): #在test类下面,定义一个内置的方法len,它是个实例方法
return 0 #让test的长度永远是0
test = Test()
if test:
print('S')
-->
没有S被打印出来
如果加个else分支:
class Test():
def __len__(self): #在test类下面,定义一个内置的方法len,它是个实例方法
return 0 #让test的长度永远是0
test = Test()
if test:
print('S')
else:
print('F')
-->
F
这就是判空操作时,习以为常的最大的误区。
自定义对象,做判空操作时,不一定就是True,就会进入if分支
解释如下:
class Test():
def __len__(self):
return 0
test = Test()
print(bool(None))
print(bool([]))
print(bool(test))
-->
False
False
False # test的bool型是False,所以上面进入的是else分支
如果把len方法去掉:
class Test():
pass
test = Test()
print(bool(None))
print(bool([]))
print(bool(test)
-->
False
False
True
由此可见,自定义的对象,其bool类型与len方法有关系。
(2)当然,除了上面的__len__
方法,还与一个__bool__
方法有关系。下面,探讨下这两个方法,是如何决定和影响test对象的最终的bool取值的?
class Test():
pass #什么都没定义
print(bool(Test()))
-->
True
以上,让我们天然的以为:只要test对象存在,就一定是True。
class Test():
def __len__(self): #定义了一个len方法
return 0 #空值
print(bool(Test()))
-->
False
class Test():
def __len__(self):
return 8 #非空值
print(bool(Test()))
-->
True
以上很好理解,因为返回的数字0被转型为False,数字8被转型为True。
但是,当返回的不是数字时:
class Test():
def __len__(self):
return '8' #字符串8
print(bool(Test()))
-->
error ❌ #内置函数len代表的是长度,所以只能是数字
class Test():
def __len__(self):
return 1.0 #数字的浮点型1.0
print(bool(Test()))
-->
error ❌ #内置函数len代表的是长度,所以只能是数字,且只能是整型int
class Test():
def __len__(self):
return True #浮点型
print(bool(Test()))
-->
True
小结:
1.内置函数len代表的是长度,所以只能是数字,且只能是整型int
2.Test类的bool取值,以__len__的返回结果来决定。
因为,运行bool()
实质上还是在调用对象下面__len__的方法。
- 有__len__的方法
class Test():
def __len__(self):
return True
print(len(Test())) #全局的内置方法len(),其实质也是在调用对象下面的__len__的方法
print(bool(Test()))
-->
1
True
- 没有__len__的方法
class Test():
pass #当对象下面没有__len__的方法时
print(len(Test()))
-->
error ❌ #内置方法len()就会出错
七月老师:
像len()
这种全局的内置函数,并不神秘。本质还是要去调用对象下面的某个方法。只不过,调用更加方便。
3.自定义的对象与__bool__
方法
当两个方法都存在时,优先调用__bool__
方法,最正宗。
没有时,再调用__len__
方法。
class Test():
def __bool__(self):
return False
def __len__(self): #上下两个方法背道而驰
return True
print(bool(Test()))
-->
False # 优先用bool方法
class Test():
def __bool__(self):
return 0 #能返回整型0吗
def __len__(self):
return True
print(bool(Test()))
-->
error ❌ #__bool__方法只能返回bool类型
虽然,很多时候,数字与bool能相互换算,但是类型不同,所以本质就不是一个东西。
class Test():
def __bool__(self):
print('bool called')
return False
def __len__(self):
print('len called')
return True
print(bool(Test()))
-->
bool called #只运行了__bool__()方法
False
class Test():
# def __bool__(self): #把__bool__()方法去掉
# print('bool called')
# return False
def __len__(self):
print('len called')
return True
print(bool(Test()))
-->
len called #__len__将会被调用
True
Comments | NOTHING