首先我们说说对于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
View Code
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()
View Code
这个例子中,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
View Code
实际上,在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()
View Code
JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于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
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#
c#
在JavaScript语言中无块级作用域


1 function Main(){
2 if(1==1){
3 var name = 'seven';
4 }
5 console.log(name);
6 }
7 // 输出: seven
View Code
补充:标题之所以添加双引号是因为JavaScript6中新引入了 let 关键字,用于指定变量属于块级作用域。
二、JavaScript采用函数作用域
在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
View Code
三、JavaScript的作用域链
由于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();
View Code
如上述代码则出现三个作用域组成的作用域链,如果出现作用域链后,那么寻找变量时候就会出现顺序,对于上述实例:
当执行console.log(xo)时,其寻找顺序为根据作用域链从内到外的优先级寻找,如果内层没有就逐步向上找,直到没找到抛出异常。

四、JavaScript的作用域链执行前已创建
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
View Code
上述代码,在函数被调用之前作用域链已经存在:
- 全局作用域 -> Func函数作用域 -> inner函数作用域
当执行【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
View Code
上述代码和示例一的目的相同,也是强调在函数被调用之前作用域链已经存在:
- 全局作用域 -> Func函数作用域 -> inner函数作用域
不同的时,在执行【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
View Code
上述代码,在函数被执行之前已经创建了两条作用域链:
- 全局作用域 -> Bar函数作用域
- 全局作用域 -> Func函数作用域
当执行【ret();】时,ret代指的Bar函数,而Bar函数的作用域链已经存在:全局作用域 -> Bar函数作用域,所以,执行时会根据已经存在的作用域链去寻找。
五、声明提前
在JavaScript中如果不创建变量,直接去使用,则报错:
1 console.log(xxoo); 2 // 报错:Uncaught ReferenceError: xxoo is not
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])
















