标签:
首先我们说说对于with关键字的理解
有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。
1 with open("/tmp/foo.txt") 2 as file: 3 data = file.read()
那with是如何工作的呢
这看起来充满魔法,但不仅仅是魔法,Python对with的处理还很聪明。基本思想是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法。
紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。
下面例子可以具体说明with如何工作:
1 #!/usr/bin/env 2 python 3 # 4 with_example01.py 5 6 7 class Sample: 8 def __enter__(self): 9 print "In 10 __enter__()" 11 return "Foo" 12 13 def __exit__(self, type, 14 value, trace): 15 print "In 16 __exit__()" 17 18 19 def get_sample(): 20 return Sample() 21 22 23 with 24 get_sample() as sample: 25 print "sample:", 26 sample
When executed, this will result in: 运行代码,输出如下
1 bash-3.2$ 2 ./with_example01.py 3 In 4 __enter__() 5 sample: 6 Foo 7 In 8 __exit__()
正如你看到的, 1. __enter__()方法被执行 2. __enter__()方法返回的值 - 这个例子中是"Foo",赋值给变量‘sample‘ 3. 执行代码块,打印变量"sample"的值为 "Foo" 4. __exit__()方法被调用 with真正强大之处是它可以处理异常。可能你已经注意到Sample类的__exit__方法有三个参数- val, type 和 trace。 这些参数在异常处理中相当有用。我们来改一下代码,看看具体如何工作的。
1 #!/usr/bin/env 2 python 3 # 4 with_example02.py 5 6 7 class Sample: 8 def __enter__(self): 9 return self 10 11 def __exit__(self, type, 12 value, trace): 13 print "type:", type 14 print "value:", 15 value 16 print "trace:", 17 trace 18 19 def do_something(self): 20 bar = 1/0 21 return bar + 10 22 23 with 24 Sample() as sample: 25 sample.do_something()
这个例子中,with后面的get_sample()变成了Sample()。这没有任何关系,只要紧跟with后面的语句所返回的对象有__enter__()和__exit__()方法即可。此例中,Sample()的__enter__()方法返回新创建的Sample对象,并赋值给变量sample。
代码执行后:
1 bash-3.2$ 2 ./with_example02.py 3 type: 4 <type ‘exceptions.ZeroDivisionError‘> 5 value: 6 integer division or modulo 7 by zero 8 trace: 9 <traceback object at 0x1004a8128> 10 Traceback 11 (most recent call last): 12 File "./with_example02.py", 13 line 19, in <module> 14 sample.do_something() 15 File "./with_example02.py", 16 line 15, in do_something 17 bar = 1/0 18 ZeroDivisionError: 19 integer division or modulo 20 by zero
实际上,在with后面的代码块抛出任何异常时,__exit__()方法被执行。正如例子所示,异常抛出时,与之关联的type,value和stack trace传给__exit__()方法,因此抛出的ZeroDivisionError异常被打印出来了。开发库时,清理资源,关闭文件等等操作,都可以放在__exit__方法当中。
因此,Python的with语句是提供一个有效的机制,让代码更简练,同时在异常产生时,清理工作更简单。
with知识点
这里要介绍一个知识点。我们在做上下文管理的时候,用到过with。
我们如何自定义一个with方法呢?
这是老师上课时所说的上下文管理装饰器
1 @contextlib.contextmanager 2 def worker_state(self, state_list, worker_thread): 3 """ 4 用于记录线程中正在等待的线程数 5 """ 6 state_list.append(worker_thread) 7 try: 8 yield 9 finally: 10 state_list.remove(worker_thread) 11 with self.worker_state(self.free_list, current_thread): 12 event = self.q.get()
JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走遍天下都不怕...
在Java或C#中存在块级作用域,即:大括号也是一个作用域。
1 public static void main () 2 { 3 if(1==1){ 4 String name = "seven"; 5 } 6 System.out.println(name); 7 } 8 // 报错 9 10 Java
1 public static void Main() 2 { 3 if(1==1){ 4 string name = "seven"; 5 } 6 Console.WriteLine(name); 7 } 8 // 报错 9 10 C#
在JavaScript语言中无块级作用域
1 function Main(){ 2 if(1==1){ 3 var name = ‘seven‘; 4 } 5 console.log(name); 6 } 7 // 输出: seven
补充:标题之所以添加双引号是因为JavaScript6中新引入了 let 关键字,用于指定变量属于块级作用域。
在JavaScript中每个函数作为一个作用域,在外部无法访问内部作用域中的变量。
1 function Main(){ 2 var innerValue = ‘seven‘; 3 } 4 5 Main(); 6 7 console.log(innerValue); 8 9 // 报错:Uncaught ReferenceError: innerValue is not defined
由于JavaScript中的每个函数作为一个作用域,如果出现函数嵌套函数,则就会出现作用域链。
1 xo = ‘alex‘; 2 3 function Func(){ 4 var xo = "seven"; 5 function inner(){ 6 var xo = ‘alvin‘; 7 console.log(xo); 8 } 9 inner(); 10 } 11 Func();
如上述代码则出现三个作用域组成的作用域链,如果出现作用域链后,那么寻找变量时候就会出现顺序,对于上述实例:
当执行console.log(xo)时,其寻找顺序为根据作用域链从内到外的优先级寻找,如果内层没有就逐步向上找,直到没找到抛出异常。
JavaScript的作用域在被执行之前已经创建,日后再去执行时只需要按照作用域链去寻找即可。
示例一:
1 xo = ‘alex‘; 2 3 function Func(){ 4 var xo = "seven"; 5 function inner(){ 6 7 console.log(xo); 8 } 9 return inner; 10 } 11 12 var ret = Func(); 13 ret(); 14 // 输出结果: seven
上述代码,在函数被调用之前作用域链已经存在:
当执行【ret();】时,由于其代指的是inner函数,此函数的作用域链在执行之前已经被定义为:全局作用域 -> Func函数作用域 -> inner函数作用域,所以,在执行【ret();】时,会根据已经存在的作用域链去寻找变量。
示例二:
1 xo = ‘alex‘; 2 3 function Func(){ 4 var xo = "eirc"; 5 function inner(){ 6 7 console.log(xo); 8 } 9 xo = ‘seven‘; 10 return inner; 11 } 12 13 var ret = Func(); 14 ret(); 15 // 输出结果: seven
上述代码和示例一的目的相同,也是强调在函数被调用之前作用域链已经存在:
不同的时,在执行【var ret = Func();】时,Func作用域中的xo变量的值已经由 “eric” 被重置为 “seven”,所以之后再执行【ret();】时,就只能找到“seven”。
示例三:
1 xo = ‘alex‘;<br> 2 function Bar(){ 3 console.log(xo); 4 } 5 6 function Func(){ 7 var xo = "seven"; 8 9 return Bar; 10 } 11 12 var ret = Func(); 13 ret(); 14 // 输出结果: alex
上述代码,在函数被执行之前已经创建了两条作用域链:
当执行【ret();】时,ret代指的Bar函数,而Bar函数的作用域链已经存在:全局作用域 -> Bar函数作用域,所以,执行时会根据已经存在的作用域链去寻找。
在JavaScript中如果不创建变量,直接去使用,则报错:
1 console.log(xxoo); 2 // 报错:Uncaught ReferenceError: xxoo is not defined
JavaScript中如果创建值而不赋值,则该值为 undefined,如:
1 var xxoo; 2 console.log(xxoo); 3 // 输出:undefined
在函数内如果这么写:
1 function Foo(){ 2 console.log(xo); 3 var xo = ‘seven‘; 4 } 5 6 Foo(); 7 // 输出:undefined
上述代码,不报错而是输出 undefined,其原因是:JavaScript的函数在被执行之前,会将其中的变量全部声明,而不赋值。所以,相当于上述实例中,函数在“预编译”时,已经执行了var xo;所以上述代码中输出的是undefined。
Python也是以函数作为作用域的
Li=[lambda:x for x in range(9)]
Print(li[0],li[1]) #这里的li[0],li[1],都是一个个lambda函数,这些函数都是return 一个x,而这些x它本身没有所以要去for里面去寻找,根据像js一样的作用域一样的看法可以知道for执行完之后i都变成8了,所以这个li[0]=li[1]=8
1 li =[] 2 For I in range(9): 3 Def f1(): #lambda:x等同于 4 Return i 5 li.append(f1) 6 print(i)
print(li)
print(li[0],li[1])
标签:
原文地址:http://www.cnblogs.com/237325670qqcom/p/5699122.html