swagger:https://mp.weixin.qq.com/s/0TzOKmxI3vxN50CYMtxCdQ
在Python 3中,异常对象在 except
代码块作用域之外是无法访问的
模块之间出现循环依赖(circular dependencies)
假设你有两个文件,分别是 a.py
和 b.py
,二者相互引用,如下所示:
a.py
文件中的代码:
b.py
文件中的代码:
首先,我们尝试导入 a.py
模块:
代码运行正常。也许这出乎了你的意料。毕竟,我们这里存在循环引用这个问题,想必应该是会出现问题的,难道不是吗?
答案是,仅仅存在循环引用的情况本身并不会导致问题。如果一个模块已经被引用了,Python可以做到不再次进行引用。但是如果每个模块试图访问其他模块定义的函数或变量的时机不对,那么你就很可能陷入困境。
那么回到我们的示例,当我们导入 a.py
模块时,它在引用 b.py
模块时是不会出现问题的,因为 b.py
模块在被引用时,并不需要访问在 a.py
模块中定义的任何变量或函数。 b.py
模块中对a模块唯一的引用,就是调用了a模块的 foo()
函数。但是那个函数调用发生在 g()
函数当中,而 a.py
或 b.py
模块中都没有调用 g()
函数。所以,不会出现问题。
但是,如果我们试着导入 b.py
模块呢(即之前没有引用 a.py
模块的前提下):
糟糕。情况不太妙!这里的问题是,在导入 b.py
的过程中,它试图引用 a.py
模块,而 a.py
模块接着又要调用 foo()
函数,这个 foo()
函数接着又试图去访问 b.x
变量。但是这个时候, b.x
变量还没有被定义,所以才出现了AttributeError异常。
解决这个问题有一种非常简单的方法,就是简单地修改下 b.py
模块,在 g()
函数内部才引用 a.py
:
现在我们再导入 b.py
模块的话,就不会出现任何问题了
================================
在遍历列表时更改列表:能够做到这点的一个编程范式就是列表解析式
==================
错误理解Python中的变量名解析
上述错误的出现,是因为当你在某个作用域内为变量赋值时,该变量被Python解释器自动视作该作用域的本地变量,并会取代任何上一层作用域中相同名称的变量。
正是因为这样,才会出现一开始好好的代码,在某个函数内部添加了一个赋值语句之后却出现了 UnboundLocalError
,难怪会让许多人吃惊。
foo1
函数并没有为 lst
变量进行赋值,但是 foo2
却有赋值。我们知道, lst+=[5]
只是 lst=lst+[5]
的简写,从中我们就可以看出, foo2
函数在尝试为 lst
赋值(因此,被Python解释器认为是函数本地作用域的变量)。但是,我们希望为 lst
赋的值却又是基于 lst
变量本身(这时,也被认为是函数本地作用域内的变量),也就是说该变量还没有被定义。
=====================
可选参数默认值的设置在Python中只会被执行一次,也就是定义该函数的时候。因此,只有当 foo()
函数被定义时, bar
参数才会被初始化为默认值(也就是,一个空列表),但是之后每次 foo()
函数被调用时,都会继续使用 bar
参数原先初始化生成的那个列表
一个常见的解决办法就是:
==============================
1 导入模块多次和导入一次的效果是一样的
2 若希望模块能像程序一样被执行,可以使用-m切换开关来执行,如python -m progname args (运行带命令行参数args的progname程序)
3 模块真正的用处在于它可以保持自己的作用域
4 告知模块本身是作为程序运行还是导入到其他程序,需要使用__name__变量,被当做程序执行时,__name__的值是__main__,被导入时,值为模块的名字
5 模块存储在文件中,包就是模块所在的目录,为了让python将目录作为包对待,目录必须包含一个__init__.py的文件(模块)
6若将__init__.py作为普通模块导入的话,__init__.py模块的内容就是包的内容。如constants包的__init__.py包括PI=3.14,那么可以像下面这样做:import constants print constants.PI
7 非常重要的一个知识点,一定要记住
8 __all__
__all__定义了模块的公有接口
若模块有__all__变量,则__all__代表from 模块 import * 代表什么含义
不在__all__变量中的函数无法通过import *导入,如copy模块中的dispatch_table 不在__all__中,要使用dispatch_table就要通过 from copy import dispatch_table 实现,或先导入copy,再copy.dispatch_table
若模块没有__all__,则import * 默认导入模块中所有不以下划线开头的全局变量
9 想看函数的使用说明,可以打印__doc__,如 print(range.__doc__)
10 找到源码的位置:print copy.__file__
11 sys.argv列表中,脚本的名字为sys.argv[0]
12 利用heapq可以求出前N个最大最小值
import heapq lyst = [-3, 22, 45, 34, 99, 102, -44] low3 = heapq.nlargest(3, lyst) top3 = heapq.nsmallest(3, lyst)
13
为了让特殊字符表现得像普通字符一样,需要对它进行转义
profile模块可用于代码片段效率分析;trace模块可以提供总的分享
cmd 可以编写命令行解释器
14
re.search 寻找匹配的字符,找到了返回true,没找到返回false
re.match 从字符串开头匹配,
=========================
1 open第3个参数控制着文件的缓冲,取值0则表示无缓冲,直接写磁盘;取值1则表示直接写内存,只有flush或close时才写磁盘;大于1时代表换届区大小(单位是字节),-1(或其他负数)代表使用默认缓冲区大小
2 sys.stdin sys.stdout sys.stderr :它们都是文件,大部分文件对象的方法它们也可以使用(如read)
3 随意读取使用seek:seek(offset,whence),其中offset表示偏移的字节数,whence默认是0,表示从文件开头开始偏移,取值1时表示从当前位置开始偏移,取值2时表示从文件结尾移动
4 tell返回当前文件的位置
5 读取大文件的方法:
with open(path,'r') as f: for line in f: print(line) print("+++++++++++") with open(path,'r') as f: while True: line=f.readline() if line: print(line) else: break print("+++++++++++")
6 有双下划线的方法是私有方法
7 前面有下划线的名字都不会被带星号的Import语句导入(from module import *)
8 判断一个类是否是另一个类的子类:issubclass
9 在多重继承中,若父类中有相同名字的方法,则先继承的类中的方法会重写后续继承的类中的方法
10 判断方法是否存在:hasattr(Zcy,'haha')
调用方法:getattr(Zcy(),'haha')()
设置方法:
setattr(Zcy,'oo','ouou') print(Zcy.oo) def pp(): print('pp') setattr(Zcy,'rr',pp) Zcy.rr()
11
12 自己的异常类要继承自 Exception
13 若想将捕获的异常 传递出去,只需调用不带参数的raise
try: print(1/0) except ZeroDivisionError: print("sdf") raise
14 捕获多个异常
try: print(1/'sdf') except (ZeroDivisionError,TypeError): print("sdf")
15 访问异常对象本是,下面示例中e就是异常对象本身
try: ss=1 print(ss.split(',')) except (ZeroDivisionError,TypeError,AttributeError) as e: print(e)
捕获所有异常用 except Exception as e:
16 调用时间不可知,不建议使用
17 若子类重写了构造方法,那么一定要调用父类的构造方法,否则子类可能不能正确的初始化
18 x[-n] 和 x[ len(x)-n] 是一样的
19 迭代器
class DD(object): def __init__(self): self.age=0 def __iter__(self): #返回一个迭代器 return self def __next__(self): # 实现了__next__ 方法的才是迭代器 if self.age<5: self.age +=1 return self.age raise StopIteration sdf=DD() try: for age in sdf: print(age) print ('hello') except Exception as ex: print(ex) #没有走到这里
20 生成器:任何包含yield语句的函数称为生成器
21 当使用一个负数作为步长时,必须让开始点大于结束点,如:
y=[11,22,33,5,6,7,8] print y[6:0:-2] 输出:[8, 6, 33]
22 any: 有一个是真就返回true 【0或“”或false或None 都算是假】
all:全部是真才返回true
23
@property装饰器必须先于@setter,@deleter使用,且三者说装饰的函数必须同名
如果一个方法被@classmethod所装饰,就代表该方法与类绑定,而不是与实例对象绑定
被@staticmethod所装饰的方法为静态方法,静态方法一般使用场景就是和类相关的操作,但是又不会依赖和改变类、实例的状态,比如一些工具方法
24
在 queue 模块中提供了 3种同步的、线程安全的队列,分别由 3个类 Queue【先进先出】、LifoQueue【先进后出】 和 PriorityQueue【优先级队列】,它们的唯一区别是元素取出的顺序不同。并且,LifoQueue 和 PriorityQueue 都是 Queue 的子类。
25
event对象的一个重要特点是它被设置为True时会唤醒所有等待它的线程,如果唤醒单个线程的最好用Condition