开学以后,我终日与腾讯课堂、Zoom为伍,抑郁温柔地向我的生命靠近,蓝光的照射使少女花容失色。于是我又回到了我的小窝。

开学第三天了,终于迎来了我的第一节面向对象程序设计课。杂乱的记录一下开学第一课学到的东西。

Python 中的运算符重载

先来看一个案例

a = int(1.2)
b = int(2.1314)
c = a + b
print(c)

输出会和你想象的一样么?当然不

3.3314000000000004



你以为有什么奥妙么?并没有,只是我没有给出完整的代码……

1
2
3
4
5
6
class int:
def __init__(self, num):
self.num = num

def __add__(self, other):
return self.num + other.num


首先,值得一提的是,在py文件中定义了一个名为int的类,程序执行过程中会优先使用这个类,而不是直接调用Python的整型函数int,推而广之,即便py文档中没有int这个类的定义,程序执行时也会先搜寻引用的包中是否存在对int的定义,最后再使用内置函数。




这一步实现了 + 的运算符重载

1
def __add__(self, other):

应当注意的是,需要两个自定义类型的对象做相关运算时,此处应写为:

1
self.num + other.num

需要自定义类型与整型数一起做运算时(如:c = a + 1)应写为:

1
self.num + other


更多重载运算符

浮点数的计算误差

不知道你有没有好奇,为什么给出的枯燥的案例中输出结果不是3.3314,而是在小数点n多位后发生了漂移……
是的,因为蒟蒻的我提出了这个困惑,正好踩在老师要讲的浮点数计算误差上,我的后半节网课“出镜率”飙升,不知道有没有小哥哥因此会多注意我一下:)我真是个野心家!




我们知道,计算机中保存数字的寄存器是使用二进制来保存数字的。好了,为了解答这个问题,让我们先看看将十进制小数转换为二进制的过程为:

1
将小数乘以2,取出整数部分作为二进制表示的第1位;然后再将小数部分乘以2,将得到的整数部分作为二进制表示的第2位;以此类推,直到小数部分为0。

例如, 5.20 - 5 的结果是0.2时,使用二进制表示就是:0.0011 0011 0011…

是个无限循环小数。
而内存只会划分有限的空间来保存一个小数,所以当我们想要保存这个0.2的数字时,只会截取二进制数字中的一部分来保存,而当我们重新通过被截取的二进制数字来计算对应的十进制数字,就会产生误差。

这里附上大佬的原文



所以

1
print(0.1 * 0.1 == 0.01)

的输出结果是False

Python中的LEGB规则



如果你问我,那魔幻主义小清新背景是什么?

我将告诉你,那是让我思念成疾的校园。


Local, Enclosing, Global, Built-in是什么?先来一篇干货帮助我们理解一下这四个变量范围

1
2
3
4
5
6
num = 1
def fun1():
num = 123
print(num)
fun1
print(num)

num = 123 属于local scope, 输出为

1
2
123
1


运用global改写

1
2
3
4
5
6
7
num = 1
def fun1():
global num
num = 123
print(num)
fun1
print(num)

输出为

1
2
123
123



1
2
3
4
5
6
7
8
9
def outer():
num = 10
def inner():
# nonlocal num
num = 100
print(num)
inner()
print(num)
outer()

输出

1
2
100
100

取消注释后

1
2
3
4
5
6
7
8
9
def outer():
num = 10
def inner():
nonlocal num
num = 100
print(num)
inner()
print(num)
outer()

输出

1
2
100
10

此处,num = 10就存在于这个”E”,enclosing中,可以理解为local与global之间的“直接外围作用域”,它们捆绑起来就是一个闭包。