1.is, id,==
-
is用来判断两个对象是不是同一个(同一个是指同一内存地址)
>>> a = "sadasdasdasdas"
>>> b = a
>>> a is b
True # 很好理解,a, b指向同一个地址
>>> a = 3
>>> b = 3
>>> a is b
True # 原理同Java虚拟机常量池,Python也有同样机制,整数3这个对象会被缓存在“常量池中”,a,b指向常量池中同一对象
-
id用来获取对象的内存地址,一般来说id相同(使用同一个内存地址)是同一个对象,is判断为真
>>> class A():
... pass
...
>>> a1 = A()
>>> id(a1)
139995267455168
>>> del a1
>>> a2 = A()
>>> id(a2)
139995267455168
# ab地址上相同,但时间上不同时存在,a is not ba1和a2内存地址一样,但明显不是同一个对象,造成此现象原因在于Python内存分配机制,a2与a1同一类型,并且使用同等大小的空间,del a1后,腾出来的空间恰好给a2使用。不好想,就想象成有个栈,a1入栈(分配内存)后出栈(del释放内存),a2入栈时就恰好与a1在同一个位置
-
一般来说a is b,或者ab时间上同时存在且id(a)==id(b)为真,a == b为真。即:同一个对象,或者同一时间同一地址的对象,==值相等。绝大多数情况下,正确。反例:
>>> a = float("nan")
>>> b = a
>>> a is b
True
>>> id(a) == id(b)
True
>>> a == b
False
#为什么是这样,官方文档有解释,RFCXXX记不得了
2.with语句中的异常机制和上下文嵌套
-
with与异常
当一个对象被用作上下文管理器时:
__enter__
方法将在进入代码块前被调用。__exit__
方法则在离开代码块之后被调用(即使在代码块中遇到了异常)。下面是上下文管理器的一个例子,模仿open使用我们自己的上下文管理器。例子中,我们完全忽视了语句块内部可能出现的问题。
class MyOpen: def __init__(self, filename, mode): self.filename = filename self.mode = mode def __enter__(self): self.openedFile = open(self.filename, self.mode) return self.openedFile def __exit__(self, *unused): self.openedFile.close() with MyOpen(filename, mode) as writer: writer.write("Hello World from our new Context Manager!")
如果with语句块内部发生了异常,
__exit__
方法将先被调用,然后异常将会被重新抛出(re-raised)。当处理文件写入操作时,大部分时间你肯定不希望隐藏这些异常,所以这是可以的。而对于不希望重新抛出的异常,我们可以让__exit__
方法简单的返回True来忽略语句块中发生的所有异常(大部分情况下这都不是明智之举)。with语句异常re-raised机制类似try...finally...的re-raised机制,finally中的return,continue或break,则会把抛出的异常淹没,throw会把抛出的异常覆盖
完备的__exit__函数签名应该是这样的:
def __exit__(self, exc_type, exc_val, exc_tb)#(异常类型,异常值以及异常追踪信息)
-
with上下文嵌套
假设我们有两个文件,一个读一个写,需要进行拷贝
不要这样写
with open('toReadFile', 'r') as reader: with open('toWriteFile', 'w') as writer: writer.writer(reader.read())
可以通过contextlib.nested进行简化:
with contextlib.nested(open('fileToRead.txt', 'r'), open('fileToWrite.txt', 'w')) as (reader, writer): writer.write(reader.read())
在Python2.7中这种写法被一种新语法取代:
with open('fileToRead.txt', 'r') as reader, \ open('fileToWrite.txt', 'w') as writer: writer.write(reader.read())
3.yield
yield是保留了堆栈现场的return,将来某一时间接受消息,恢复堆栈,重新运行。
-
理解yield
可以把 yield 想象成下面的伪代码
x = yield i ==等价于==> put(i); x = wait_and_get()
-
可以理解为先是一个 put(i) ,这个 i 就是 yield 表达式后面的参数,如果 yield 没有参数,则表示 None 。它表示将 i 放到一个全局缓冲区中,相当于返回了一个值。
-
wait_and_get()
可以理解为一个阻塞调用,它等待着外界来(send)唤醒它,并且可以返回一个值,返回给yield前面的接受者,如例中x
-
-
例子解读
def g():
... print 'step 1'
... x = yield 'hello'
... print 'step 2', 'x=', x
... y = 5 + (yield x)
... print 'step 3', 'y=', y
>>> f = g()
>>> f.next()
step 1
'hello'
# 执行 next() 时,代码执行到 x = yield 'hello' 就停住了,并且返回了 yield 后面的 'hello'>>> f.send(5)
step 2 x= 5 #send(5)的参数5被yield接受,并传递给yield前面的接受者x
5 # 继续运行至下一个yield,将x返回
-
小结
- next() 相当于 send(None)
- yield 后面没有参数表示返回为 None
- 注意:第一次调用时要么使用 next() ,要么使用 send(None) ,不能使用 send() 来发送一个非 None 的值,原因就是第一次没有一个 yield 表达式来接受这个值(第一次没有一个 yield卡住需要被唤醒)