Python晦涩知识点

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 b

    a1和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()

    1. 可以理解为先是一个 put(i) ,这个 i 就是 yield 表达式后面的参数,如果 yield 没有参数,则表示 None 。它表示将 i 放到一个全局缓冲区中,相当于返回了一个值

    2. 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返回

  • 小结

    1. next() 相当于 send(None)
    2. yield 后面没有参数表示返回为 None
    3. 注意:第一次调用时要么使用 next() ,要么使用 send(None) ,不能使用 send() 来发送一个非 None 的值,原因就是第一次没有一个 yield 表达式来接受这个值(第一次没有一个 yield卡住需要被唤醒)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值