11 Pythonic与Python杂记之二


目录

  • 用字典映射代替其他语言中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的本质概念

NoneFalse
数据类型NoneType型bool型
本质概念表示不存在表示假

有时候,if Noneif False得到的结果可能是相同的,但是两者的本质概念是不同的。

同理:

[]      #表示这个列表中没有任何元素
''      #表示这个字符串中没有任何元素

或:这个序列里面没有任何的字符

四、自定义对象与bool的对应关系

1.奇怪现象

a = ''
print(a == None)

-->
False

aNone的取值是不相同的,但if aif 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

声明:Jerry's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 11 Pythonic与Python杂记之二


Follow excellence, and success will chase you.