Python学习20问--3
1 短路逻辑
规则:
- 表达式从左至右运算,若 or 的左侧逻辑值为 True ,则短路 or 后所有的表达式(不管是 and 还是 or),直接输出 or 左侧表达式 。若 or 的左侧逻辑值为 False ,则输出or右侧的表达式,不论其后表达式是真是假,整个表达式结果即为其后表达式的结果
- 表达式从左至右运算,若 and 的左侧逻辑值为 False ,则短路其后所有 and 表达式,直到有 or 出现,输出 and 左侧表达式到 or 的左侧,参与接下来的逻辑运算。若 and 的左侧逻辑值为 True,则输出其后的表达式,不论其后表达式是真是假,整个表达式结果即为其后表达式的结果
- 若 or 的左侧为 False ,或者 and 的左侧为 True 则不能使用短路逻辑。
注意:
- 在Python中and的优先级是大于or的,而且and和or都是会返回值的并且不转换为True和False。当not和and及or在一起运算时,优先级为是not>and>or
- 在Python中,None、任何数值类型中的0、空字符串“”、空元组()、空列表[]、空字典{}都被当作False,还有自定义类型,如果实现了 __ nonzero __ () 或 __ len __ () 方法且方法返回 0 或False,则其实例也被当作False,其他对象均为True。
- 记住,所有被短路的表达式均不会被输出。
- not>and>or
- 三元运算操作符 bool ? a : b ,若 bool 为真则 a ,否则为 b 。
python写法: bool and a or b
2 int()函数
- int() 函数用于将一个字符串或数字转换为整型。
- class int(x, base=10) x – 字符串或数字。base – 进制数,默认十进制。
- 若 x 为纯数字,则不能有 base 参数,否则报错;其作用为对入参 x 取整
- int会采取比较暴力的截断方式,即向下取整
- 如果需要四舍五入的时候可以加0.5
3 语句书写
- python中可以在一行书写多个语句,中间用分号进行分隔
- 一行过长的语句可以使用反斜杠或者括号分解成几行
4 assert
- Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。
- 断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况,
5 python中的内存机制
- 短的字符串,数字python在内存中是一个对象
- 字典,数组这样的对象在内存中python会创建两个不同的对象
- 长的字符串python在内存中同样会创建两个不同的对象
- .变量分为不可变类型和可变类型,字符串、元组、整型、浮点型等数字都是不可比那类型,字典、列表、集合是可变类型
- 对于不可变类型,无论是执行a = a+b还是a += b,a的内存地址都会变,可以理解为生成了新的变量;
那对于可变类型,执行my_list = my_list + new_list时,my_list的内存地址发生变化,即my_list变成了一个新的变量;但是在执行my_list += new_list的时候,my_list内存地址不会发生变化,即没有生成新的变量,这种方法效果等类似于列表的extend操作 - str是有__iadd__方法的,只不过不像列表一样地址ID不变,str的地址是会变的
6 魔法方法
拷贝于此,侵删
魔法方法是python内置方法,不需要主动调用存在的目的是为了给python的解释器进行调用。
7 在python中,所有的多对象的、逗号分隔的、没有明确用符号定义的这些集合默认的类型都是元组。
8 列表推导式,字典推导式
-
列表推导式书写形式:
[表达式 for 变量 in 列表] 或者 [表达式 for 变量 in 列表 if 条件] -
它的结构是在一个中括号里包含一个表达式,然后是一个for语句,然后是 0 个或多个 for 或者 if 语句。那个表达式可以是任意的,意思是你可以在列表中放入任意类型的对象。返回结果将是一个新的列表,在这个以 if 和 for 语句为上下文的表达式运行完成之后产生。
-
列表推导式的执行顺序:各语句之间是嵌套关系,左边第二个语句是最外层,依次往右进一层,左边第一条语句是最后一层。
-
当然,也不是所有场景都推荐使用列表推导式.比如说:如果列表推导的代码超过了两行,就要考虑改成用for循环了.超过了两行的列表推导式就真的没有可读性了.通常的原则是,只用列表推导来创建新的列表,并且尽量保持简短.
-
字典推导式
自Python2.7以来,列表推导概念就移植到了字典上,从而有了字典推导(后面还会看到集合推导).dict_a = {key: value for key in 'python' for value in range(2)} dict_b = {key: key * key for key in range(6)} list_phone = [('HUAWEI', '华为'), ('MI', '小米'), ('OPPO', 'OPPO'), ('VIVO', 'VIVO')] dict_c = {key: value for key, value in list_phone}
-
集合是无序且不重复的,所以会自动去掉重复的元素,并且每次运行显示的顺序不一样.
-
元组推导式
- 使用元组推导式可以快速生成一个元组,其表现形式和列表推导式类似,只是将列表推导式中的中括号“[]”修改为小括号“()”。
- 使用元组推导式生成的结果不是一个元组或者列表**,而是一个生成器对象**,这一点和列表生成器是不同的。
- 无论通过哪种方式遍历后,若想再使用该生成器对象,都必须重新创建一个生成器,因为遍历后的原生成器对象已经不存在了。
- 每访问生成器对象中的一个元素,就会少一个元素。
-
总结:
-
集合推导式就是将列表推导式的[]换成{},字典推导式就是推导出两个值并构建成键值对的样子.
-
另外,不管是字典推导式还是集合推导式,后面都可以像列表推导式一样接if条件语句,嵌套循环等,具体可以根据您自己的需求来使用.
9 函数文档
- 在函数内部的第1行开始,使用三引号作为帮助文档的标记字符
- 从输出可见,使用函数的__doc__属性来查看函数文档,结果更为简洁。
10 函数使用
- 先函数定义,后函数调用
- 函数中调用函数不受此限制
11 return
- return 语句就是讲结果返回到调用的地方,并把程序的控制权一起返回
- 程序运行到所遇到的第一个return即返回(退出def块),不会再运行第二个return。
- 要返回两个数值,写成一行即可
- 函数没有 return,默认 return一个 None 对象。
12 内存泄漏
内存泄露指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。
- 内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。导致程序运行速度减慢甚至系统崩溃等严重后果。
- 不使用一个对象时使用:delobject 来删除一个对象的引用计数就可以有效防止内存泄漏问题。
- 通过Python 扩展模块 gc 来查看不能回收的对象的详细信息。可以通过 sys.getrefcount(obj) 来获取对象的引用计数,并根据返回值是否为 0 来判断是否内存泄漏。
如何避免内存泄漏
- 尽量避免使用全局变量,例如使用立即执行函数的形式。
- 使用“严格模式”开发,避免因为我们的疏忽导致意外产生全局变量。
- 对于一些占用内存较大的对象,在变量不在使用后,手动将其赋值为 null,例如前面例子中的超大的数组。
- 尽量避免把闭包内部与外部产生关联引用,例如上面例子中的 theThing 变量
13 Python UnboundLocalError和NameError错误根源解析
如果引用了某个变量,但是变量名没有找到,该类型的错误就是NameError。如果该名字是一个还没有被绑定的局部变量名,那么该类型的错误是NameError中的UnboundLocalError错误。
在Python中要想引用一个name,该name必须要可见而且是绑定的。
几个概念:
- code block:作为一个单元(Unit)被执行的一段python程序文本。例如一个模块、函数体和类的定义等。
- scope:在一个code block中定义name的可见性;
- block’s environment:对于一个code block,其所有scope中可见的name的集合构成block的环境。
- bind name:下面的操作均可视为绑定操作
- 函数的形参
- import声明
- 类和函数的定义
- 赋值操作
- for循环首标
- 异常捕获中相关的赋值变量
- local variable:如果name在一个block中被绑定,该变量便是该block的一个local variable。
- global variable:如果name在一个module中被绑定,该变量便称为一个global variable。
- free variable: 如果一个name在一个block中被引用,但没有在该代码块中被定义,那么便称为该变量为一个free variable。
总的来说就是在一个code block中,所有绑定操作中被绑定的name均可以视为一个local variable;但是直到绑定操作被执行之后才可以真正的引用该name。
当一个name被引用时,他会在其最近的scope中寻找被引用name的定义。
14 闭包
闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。
- 如果在一个函数的内部定义了另一个函数,外部的我们叫他外函数,内部的我们叫他内函数。
- 闭包: 在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
- 一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
- 闭包中内函数修改外函数局部变量:
在闭包内函数中,我们可以随意使用外函数绑定来的临时变量,但是如果我们想修改外函数临时变量数值的时候发现出问题了! - 在基本的python语法当中,一个函数可以随意读取全局数据,但是要修改全局数据的时候有两种方法:1 global 声明全局变量 2 全局变量是可变类型数据的时候可以修改
- 在闭包内函数也是类似的情况。在内函数中想修改闭包变量(外函数绑定给内函数的局部变量)的时候:
- 在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。
- 在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。
- 使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量
对闭包中引用的自由变量有如下的认识:
闭包中的引用的自由变量只和具体的闭包有关联,闭包的每个实例引用的自由变量互不干扰。
一个闭包实例对其自由变量的修改会被传递到下一次该闭包实例的调用
15 is 和 == 的区别
- is 判断两个变量是否是引用同一个内存地址。is 判断两个对象是否为同一对象, 是通过 id 来判断的; 当两个基本类型数据(或元组)内容相同时, id 会相同, 但并不代表 a 会随 b 的改变而改变。is 不能被重载。
- == 判断两个变量是否相等。判断两个对象的内容是否相同, 是通过调用 eq() 来判断的。 = 可以被重载。
在 Python 中一切都是对象,毫无例外整数也是对象,对象之间比较是否相等可以用==,也可以用is。==和is操作的区别是:
- Is 比较的是两个对象的id值是否相等,也就是比较俩对象是否为同一个实例对象,是否指向同一个内存地址。
- == 比较的是两个对象的内容是否相等,默认会调用对象的__eq__()方法。
16 python中内存地址
- 整数、字符串是真正意义上的值,而内存只有一份指的是较小的值
对于(-5~256)之间的整数,会在内存中进行缓存,两个相同的整数只开辟一份内存,当地址引用为0时,内存自动删除 - 对于元组、字典、列表、集合以及range、map等容器类对象,这些的值看起来一样,但内存却不是保存一份
- 同一个列表 或者元组 中,大的整数 在内存中会保存同一个地址
- 对于实数(有理数、无理数)来说(不包括-5~256),地址是不同的
- 整数的话 -5 以下内存另开辟,大于 -5 的正整数内存地址一样,实验到了百亿两个地址一样
17 打开文件地址书写
Windows在路径名中既可以接受斜线(/),也可以接受反斜线(\),不过如果要是使用反斜线作为路径名的分隔符的话,要注意使用双反斜线进行转义(\\),否则Python将会把反斜线进行转义。
18 f.close()
- Python拥有垃圾回收机制,会在文件对象的引用计数降至零的时候自动关闭文件,但是并不是说可以不关闭文件,如果你对文件进行了写操作,应该在写入完成后进行关闭文件。
- 在向以文本格式(而不是二进制格式)打开的文件中写入数据时,Python 出于效率的考虑,会先将数据临时存储到缓冲区中,只有使用 close() 函数关闭文件时,才会将缓冲区中的数据真正写入文件中。
- 当然在某些实际场景中,我们可能需要在将数据成功写入到文件中,但并不想关闭文件。这也是可以实现的,调用 flush() 函数即可
19 python os模块
20 Python pickle模块详解
该pickle模块实现了用于序列化和反序列化Python对象结构的二进制协议。 “Pickling”是将Python对象层次结构转换为字节流的过程, “unpickling”是反向操作,从而将字节流(来自二进制文件或类似字节的对象)转换回对象层次结构。pickle模块对于错误或恶意构造的数据是不安全的。
pickle协议和JSON(JavaScript Object Notation)的区别 :
- JSON是一种文本序列化格式(它输出unicode文本,虽然大部分时间它被编码utf-8),而pickle是二进制序列化格式;
- JSON是人类可读的,而pickle则不是;
- JSON是可互操作的,并且在Python生态系统之外广泛使用,而pickle是特定于Python的;
pickle 数据格式是特定于Python的。它的优点是没有外部标准强加的限制,例如JSON或XDR(不能代表指针共享); 但是这意味着非Python程序可能无法重建pickled Python对象。
模块接口:
要序列化对象层次结构,只需调用该dumps()函数即可。同样,要对数据流进行反序列化,请调用该loads()函数。但是,如果您想要更多地控制序列化和反序列化,则可以分别创建一个Pickler或一个Unpickler对象
-
pickle.dump(obj,file,protocol = None,*,fix_imports = True )
- 将obj对象的编码pickle编码表示写入到文件对象中,相当于Pickler(file,protocol).dump(obj)
- 可供选择的协议参数是一个整数,指定pickler使用的协议版本,支持的协议是0到HIGHEST_PROTOCOL。如果未指定,则默认为DEFAULT_PROTOCOL。如果指定为负数,则选择HIGHEST_PROTOCOL。
- 文件参数必须具有接受单个字节的参数写方法。因此,它可以是为二进制写入打开的磁盘文件, io.BytesIO实例或满足此接口的任何其他自定义对象。
- 如果fix_imports为true且protocol小于3,则pickle将尝试将新的Python 3名称映射到Python 2中使用的旧模块名称,以便使用Python 2可读取pickle数据流
-
pickle.load(file,*,fix_imports = True,encoding =“ASCII”,errors =“strict” )
- 从打开的文件对象 文件中读取pickle对象表示,并返回其中指定的重构对象层次结构。这相当于Unpickler(file).load()。
- pickle的协议版本是自动检测的,因此不需要协议参数。超过pickle对象的表示的字节将被忽略。
- 参数文件必须有两个方法,一个采用整数参数的read()方法和一个不需要参数的readline()方法。两种方法都应返回字节。因此,文件可以是为二进制读取而打开的磁盘文件,io.BytesIO对象或满足此接口的任何其他自定义对象。
- 可选的关键字参数是fix_imports,encoding和errors,用于控制Python 2生成的pickle流的兼容性支持。如果fix_imports为true,则pickle将尝试将旧的Python 2名称映射到Python 3中使用的新名称。编码和 错误告诉pickle如何解码Python 2编码的8位字符串实例; 这些默认分别为’ASCII’和’strict’。该编码可以是“字节”作为字节对象读取这些8位串的实例。使用encoding='latin1’所需的取储存NumPy的阵列和实例datetime,date并且time被Python 2解码。