<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; Coroutine</title>
	<atom:link href="https://blog.gmem.cc/tag/coroutine/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Fri, 03 Apr 2026 04:13:36 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>Python网络编程</title>
		<link>https://blog.gmem.cc/python-network-programming</link>
		<comments>https://blog.gmem.cc/python-network-programming#comments</comments>
		<pubDate>Mon, 09 May 2011 07:40:49 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Network]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Coroutine]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=6627</guid>
		<description><![CDATA[<p>TCP编程代码示例 单线程Echo服务 [crayon-69d26b48cf232081238217/] 以下是客户端代码： [crayon-69d26b48cf239188392412/] 基于asyncore模块的异步Echo服务 asyncore模块将网络活动抽象为事件，由事件循环分派出去进行异步处理。事件循环通过select()或者poll()系统调用构建。 [crayon-69d26b48cf23b780076963/] 基于协程技术实现的异步Echo服务 [crayon-69d26b48cf23e359715062/]</p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/python-network-programming">Python网络编程</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">TCP编程代码示例</span></div>
<div class="blog_h3"><span class="graybg">单线程Echo服务</span></div>
<pre class="crayon-plain-tag">from socket import *  # @UnusedWildImport
DEFAULT_PORT = 1918
if __name__ == '__main__':
    # 创建基于IPv4的TCP套接字对象
    s = socket( AF_INET, SOCK_STREAM )
    # 绑定到通配符地址的1918端口
    s.bind( ( '0.0.0.0', DEFAULT_PORT ) )
    logging.debug( 'Echo server is listening on port %d', DEFAULT_PORT )
    # 开始监听，最大排队数量(backlog)为10
    s.listen( 10 )
    
    while True:
        # 接受一个客户端连接请求，返回套接字对象和地址的元组
        client, addr = s.accept()
        logging.debug( '%s connected', addr )
        msg = client.recv( 1024 )
        logging.debug( 'Received message : %s', msg )
        client.send( msg )
        client.close()</pre>
<p>以下是客户端代码：</p>
<pre class="crayon-plain-tag">from socket import *  # @UnusedWildImport

DEFAULT_PORT = 1918
if __name__ == '__main__':
    # 创建基于IPv4的TCP套接字对象
    s = socket( AF_INET, SOCK_STREAM )
    # 连接到服务器端
    s.connect( ( '127.0.0.1', DEFAULT_PORT ) )
    s.send( 'Hello Server!' )
    logging.debug( 'Echo from server: %s', s.recv( 1024 ) )</pre>
<div class="blog_h3"><span class="graybg">基于asyncore模块的异步Echo服务</span></div>
<p>asyncore模块将网络活动抽象为事件，由事件循环分派出去进行异步处理。事件循环通过select()或者poll()系统调用构建。</p>
<pre class="crayon-plain-tag"># 主分发器，关联服务器端监听套接字
class EchoSocketDispatcher( asyncore.dispatcher ):
    def __init__( self, port ):
        asyncore.dispatcher.__init__( self )
        # 创建当前分发器关联的套接字对象
        self.create_socket( socket.AF_INET, socket.SOCK_STREAM )
        self.bind( ( '0.0.0.0', port ) )
        self.listen( 1024 )
    def handle_accept( self ):
        client, addr = self.accept()
        logging.debug( 'Accepted connection from %s', addr )
        return EchoDispatcher( client )
# 子分发器，处理单个客户端连接套接字    
class EchoDispatcher( asyncore.dispatcher ):
    
    def __init__( self, client ):
        asyncore.dispatcher.__init__( self, client )
        self.chunk = None
    # 何时允许读
    def readable( self ):
        logging.debug( 'HH' )
        return True
    # 何时允许写
    def writable( self ):
        return self.chunk != None
    # 处理读取
    def handle_read( self ):
        self.chunk = self.recv( 8192 )
        logging.debug( 'Received message: %s', self.chunk )
    # 处理写入
    def handle_write( self ):
        self.send( self.chunk )
        self.chunk = None
    def handle_close( self ):
        logging.debug( 'Connection closed by peer.' )
        asyncore.dispatcher.handle_close( self )
        
DEFAULT_PORT = 1918     
if __name__ == '__main__':
    dispatcher = EchoSocketDispatcher( DEFAULT_PORT )
    logging.debug( 'Start polling on %d', DEFAULT_PORT )
    # 持续执行轮询
    asyncore.loop( use_poll=True, timeout=10 )</pre>
<div class="blog_h3"><span class="graybg"><a id="echo-service-by-coroutine"></a>基于协程技术实现的异步Echo服务</span></div>
<pre class="crayon-plain-tag"># Tasklet、SystemCall、Scheduler模拟了一个微型的操作系统

# 模拟进程
class Tasklet( object ):
    def __init__( self, target ):
        self.target = target  # 当前目标协程
        self.sendval = None  # 协程恢复时发送的值
        self.stack = []  # 历史协程的栈，对方法调用机制的一种模拟
    def run( self ):
        try:
            # 执行协程到下一次退出，并获取协程的返回值
            result = self.target.send( self.sendval )
            if isinstance( result, SystemCall ):
                # 如果是一个“系统调用”包装对象，栈状态不变，类似于中断的效果
                return result
                # 当前目标将期望此系统调用的结果被发送给它，以继续执行
            elif isinstance( result, types.GeneratorType ):
                # 如果结果是一个生成器对象实例，相当于调用一个新方法，需要将当前目标压栈
                self.stack.append( self.target )
                self.sendval = None
                self.target = result
            else:
                # 如果结果不是一个生成器对象实例，相当于新方法调用返回，需要废弃当前目标，并弹出栈顶作为新目标
                if not self.stack : return
                self.sendval = result
                self.target = self.stack.pop()
                
        except StopIteration: 
            # 当前协程已经终止，需要移除，并弹出栈顶作为新的目标
            if not self.stack: raise
            self.sendval = None
            self.target = self.stack.pop()

# 模拟系统调用
class SystemCall( object ):
    def handle( self, sched, task ):
        pass
# 读写“系统调用”实现
class ReadWait( SystemCall ):
    def __init__( self, f ):
        self.file = f
    def handle( self, sched, task ):
        fd = self.file.fileno()
        sched.readwait( task, fd )
class WriteWait( SystemCall ):
    def __init__( self, f ):
        self.file = f
    def handle( self, sched, task ):
        fd = self.file.fileno()
        sched.writewait( task, fd )

# 调度程序，相当于操作系统的调度例程
class Scheduler( object ):
    def __init__( self ):
        # 这个队列相当于操作系统的进程集
        self.task_queue = collections.deque()
        self.read_waiting = {}
        self.write_waiting = {}
        self.taskcount = 0
    def new( self, target ):
        newtask = Tasklet( target )
        self.schedule( newtask )
        self.taskcount += 1
    def schedule( self, task ):
        self.task_queue.append( task )
    def readwait( self, task, fd ):
        self.read_waiting[fd] = task
    def writewait( self, task, fd ):
        self.write_waiting[fd] = task
    def mainloop( self, count=-1, timeout=None ):
        while self.taskcount:
            # 如果有I/O事件的队列，那么先轮询I/O事件
            if self.read_waiting or self.write_waiting:
                # 如果进程队列为空则等待时间为timeout，否则等待时间为0
                # 队列为空的场景：没有创建进程；所有进程都在I/O等待集上
                # timeout默认为一直等待，直到有可能的描述符
                wait = 0 if self.task_queue else timeout
                # 查看一组文件描述符的输入、输出、异常状态。返回输入、输出、异常准备就绪的列表的元组
                # 前三个参数是整数描述符的列表；或者带有fileno()方法的对象（该方法返回文件描述符）
                # wait不指定会一直等待直到至少一个文件描述符准备好为止，为0则指进行一次轮询即返回
                r, w , e = select.select( self.read_waiting, self.write_waiting, [], wait )
                # 将就绪的文件描述符从等待集中移除，加入到正常调度集中
                for fd in r:
                    self.schedule( self.read_waiting.pop( fd ) )
                for fd in w:
                    self.schedule( self.write_waiting.pop( fd ) )
            # 逐个执行队列上的任务
            while self.task_queue:
                # 取出一个任务，从进程列表中移除
                task = self.task_queue.popleft()
                try:
                    # 执行这个任务
                    result = task.run()
                    if isinstance( result, SystemCall ):
                        # 模拟系统调用陷入内核
                        result.handle( self, task )
                    else: 
                        # 其它的，要么相当于方法调用，要么相当于方法返回，继续调度
                        self.schedule( task )
                except StopIteration :
                    # 不需要再考虑此任务，其生命周期已经结束
                    self.taskcount -= 1
            else:
                if count &gt; 0: count -= 1
                if count == 0:
                    return

# Echo服务器协程
from socket import socket, AF_INET, SOCK_STREAM
def EchoServer( host, port , sched ):
    # 服务器监听套接字
    s = socket( AF_INET, SOCK_STREAM )
    s.bind( ( host , port ) )
    logging.debug( 'EchoServer listening on %s:%d', host, port )
    s.listen( 128 )
    while True:
        # 等待服务器监听套接字可读
        yield ReadWait( s )
        conn, addr = s.accept()
        logging.debug( 'Client connected: %s', addr )
        sched.new( EchoSocket( conn ) )
def EchoSocket( conn ):
    while True:
        # 等待套接字可读
        yield ReadWait( conn )
        chunk = conn.recv( 1024 )
        if chunk:
            logging.debug( 'Received message: %s', chunk )
            yield WriteWait( conn )
            conn.send( chunk )
            

if __name__ == '__main__':
    sched = Scheduler()
    sched.new( EchoServer( '0.0.0.0', 1918, sched ) )
    sched.mainloop( -1, None )</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/python-network-programming">Python网络编程</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/python-network-programming/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Python学习笔记</title>
		<link>https://blog.gmem.cc/python-study-note</link>
		<comments>https://blog.gmem.cc/python-study-note#comments</comments>
		<pubDate>Fri, 29 Apr 2011 07:32:42 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Coroutine]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=4186</guid>
		<description><![CDATA[<p>安装与配置 安装Python Windows：可以使用WinPython，这是一个免安装、开箱即用的Python发布版，包含很多预置工具 Linux：通常已经随操作系统安装 环境变量设置  环境变量 说明  PYTHON_HOME Python安装目录 PATH 添加[crayon-69d26b48d0592080097055-i/]  PYTHONPATH Python的模块搜索路径，在前面出现的优先级高添加[crayon-69d26b48d0597711715236-i/] ，任何需要Python解释器找到的模块，都需要加到此环境变量上 仅在Windows下你可能需要手工设置此环境变量 Linux的模块安装位置与Windows不同： 模块执行脚本编译的二进制文件通常存放到[crayon-69d26b48d0599337416961-i/] 或者[crayon-69d26b48d059b538394311-i/] ，而不是[crayon-69d26b48d059d034677922-i/] 子目录 模块安装位置则可能是[crayon-69d26b48d059f337213374-i/] ，还可以安装到当前用户的目录[crayon-69d26b48d05a1595651261-i/] 下 可以使用下面的命令得到PYTHONPATH： [crayon-69d26b48d05a3355544603/] Linux下安装额外版本 Ubuntu14.04.3自带的Python版本时2.7.6和3.4.3。你可以下载并构建自己的版本，但是不要全局的安装，替换系统的python2、python3符号链接可能导致系统无法工作。 下面的脚本示例如何安装3.5版本的Python： <a class="read-more" href="https://blog.gmem.cc/python-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/python-study-note">Python学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">安装与配置</span></div>
<div class="blog_h2"><span class="graybg">安装Python</span></div>
<ol>
<li>Windows：可以使用<a href="http://winpython.github.io/">WinPython</a>，这是一个免安装、开箱即用的Python发布版，包含很多预置工具</li>
<li>Linux：通常已经随操作系统安装</li>
</ol>
<div class="blog_h3"><span class="graybg">环境变量设置</span></div>
<table border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td> 环境变量</td>
<td>说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>PYTHON_HOME</td>
<td>Python安装目录</td>
</tr>
<tr>
<td>PATH</td>
<td>添加<pre class="crayon-plain-tag">%PYTHON_HOME%;%PYTHON_HOME%\Scripts</pre> </td>
</tr>
<tr>
<td>PYTHONPATH</td>
<td>
<p>Python的模块搜索路径，在前面出现的优先级高<br />添加<pre class="crayon-plain-tag">%PYTHON_HOME%\Lib;%PYTHON_HOME%\Lib\site-packages</pre> ，任何需要Python解释器找到的模块，都需要加到此环境变量上</p>
<p>仅在<span style="background-color: #c0c0c0;">Windows下你可能</span>需要手工设置此环境变量</p>
<p>Linux的模块安装位置与Windows不同：</p>
<ol>
<li>模块执行脚本编译的二进制文件通常存放到<pre class="crayon-plain-tag">/usr/local/bin</pre> 或者<pre class="crayon-plain-tag">/usr/bin</pre> ，而不是<pre class="crayon-plain-tag">Scripts</pre> 子目录</li>
<li>模块安装位置则可能是<pre class="crayon-plain-tag">/usr/local/lib/python2.7/dist-packages:/usr/lib/python2.7/dist-packages</pre> ，还可以安装到当前用户的目录<pre class="crayon-plain-tag">~/.local</pre> 下</li>
</ol>
<p>可以使用下面的命令得到PYTHONPATH：</p>
<pre class="crayon-plain-tag">python -c "import sys; print ('\n'.join(x for x in sys.path if x))"</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">Linux下安装额外版本</span></div>
<p>Ubuntu14.04.3自带的Python版本时2.7.6和3.4.3。你可以下载并构建自己的版本，但是<span style="background-color: #c0c0c0;">不要全局的安装</span>，替换系统的python2、python3符号链接可能导致系统无法工作。
<p>下面的脚本示例如何安装3.5版本的Python：</p>
<pre class="crayon-plain-tag">cd ~/Downloads/3.5.1
./configure --prefix=$HOME/Python/3.5.1
make &amp;&amp; make install

# 检查PYTHONPATH
cd $HOME/Python/3.5.1/bin
./python3 -c "import sys; print ('\n'.join(x for x in sys.path if x))"
# 输出如下：
# /home/alex/Python/3.5.1/lib/python35.zip
# /home/alex/Python/3.5.1/lib/python3.5
# /home/alex/Python/3.5.1/lib/python3.5/plat-linux
# /home/alex/Python/3.5.1/lib/python3.5/lib-dynload
# /home/alex/Python/3.5.1/lib/python3.5/site-packages</pre>
<div class="blog_h1"><span class="graybg">使用Python解释器</span></div>
<div class="blog_h2"><span class="graybg">调用Python解释器</span></div>
<p>Ubuntu下，Python解释器的默认安装位置为/usr/bin/python；Windows下是C:\python27。需要将其添加到环境变量PATH中，然后打开Shell窗口：</p>
<pre class="crayon-plain-tag">#运行交互式的解释器
python
#输入文件结束符（Unix的Ctrl+D、Windows的Ctrl+Z）可以退出解释器
#调用quit()函数亦可退出解释器

#启动解释器并执行命令，应当用单引号包围命令，防止有空格之类的特殊字符
python -c command [arg] ...

#调用作为脚本使用的模块
python -m module [arg] ...</pre>
<div class="blog_h3"><span class="graybg">参数传递</span></div>
<p>调用解释器时，<span style="background-color: #c0c0c0;">脚本名、参数</span>传入字符串列表<span style="background-color: #c0c0c0;">sys.argv</span>中，该列表至少有一个元素。</p>
<ol>
<li>没有给定脚本名、参数时，sys.argv[0] = ''</li>
<li>脚本名指定为标准输入时，sys.argv[0] = '-'</li>
<li>使用-c参数调用解释器时，sys.argv[0] = '-c'</li>
<li>使用-m参数调用解释器时，sys.argv[0]  = 模块全名</li>
</ol>
<div class="blog_h3"><span class="graybg">交互模式</span></div>
<p>从终端读取命令并执行，称为交互模式，以主提示符（&gt;&gt;&gt;）为依据执行，输入多行结构，则需要附加从属提示符（...），例如：</p>
<pre class="crayon-plain-tag">&gt;&gt;&gt; if name == 'Alex':
...     print "Hello " + name
...
Hello Alex</pre>
<div class="blog_h2"><span class="graybg">解释器及其环境</span></div>
<div class="blog_h3"><span class="graybg">错误处理</span></div>
<p>有错误发生时，解释器在stderr上打印错误信息、调用栈跟踪。在交互模式下，会返回主提示符；如果从文件输入执行，则以非零状态退出。<br /> 使用try的except子句可以捕获异常</p>
<div class="blog_h3"><span class="graybg">执行Python脚本</span></div>
<p>Linux下，Python脚本可以直接执行（需要chmod +x）：</p>
<pre class="crayon-plain-tag">#! /usr/bin/env python
...</pre>
<p>在Windows下，安装程序会把*.py与python.exe关联，可以双击执行，*pyw类似，但是不显示控制台窗口</p>
<div class="blog_h3"><span class="graybg">交互执行文件</span></div>
<p>如果需要让解释器在每次启动时均执行一个脚本，可以设置环境变量：PYTHONSTARTUP，这类似于Linux Shell的.profile文件</p>
<div class="blog_h3"><span class="graybg">本地化模块</span></div>
<p>钩子方法sitecustomize、usercustomize用于提供本地化。</p>
<div class="blog_h1"><span class="graybg">Python3新特性</span></div>
<div class="blog_h2"><span class="graybg">版本3.0</span></div>
<div class="blog_h3"><span class="graybg">print语句变为函数</span></div>
<pre class="crayon-plain-tag">print "The answer is", 2*2
print("The answer is", 2*2)
# 可以定制打印项之间的分隔符
print("There are &lt;", 2**32, "&gt; possibilities!", sep="")</pre>
<div class="blog_h3"><span class="graybg">视图和迭代器</span></div>
<p>字典的<pre class="crayon-plain-tag">dict.keys()</pre>, <pre class="crayon-plain-tag">dict.items()</pre>, <pre class="crayon-plain-tag">dict.values()</pre>方法返回的不再是列表，而是“视图”。因此：</p>
<pre class="crayon-plain-tag">k = d.keys();
k.sort()       # ERR
k = sorted(d)  # OK</pre>
<p><pre class="crayon-plain-tag">map()</pre>, <pre class="crayon-plain-tag">filter()</pre>, <pre class="crayon-plain-tag">zip()</pre>等返回迭代器。要获得列表，可以用<pre class="crayon-plain-tag">list()</pre>包装，或者使用列表推导：</p>
<pre class="crayon-plain-tag">result = [ x for x in map()]</pre>
<div class="blog_h3"><span class="graybg">文本和数据</span></div>
<p>在Python 3中，使用文本和（二进制）数据两个概念，来代替Uincode字符串和8-bit字符串。所有文本都是基于Unicode的，但是编码后的Unicode表示为二进制数据。存储文本的类型是<pre class="crayon-plain-tag">str</pre>。存储二进制数据的类型为<pre class="crayon-plain-tag">bytes</pre>。</p>
<p>不再需要使用u前缀来表示文本：<pre class="crayon-plain-tag">u"..."</pre>，但是要表示二进制数据直接量则必须使用前缀<pre class="crayon-plain-tag">b"..."</pre>。</p>
<p>任何混合文本、数据的操作会导致TypeError。你必须明确的进行转换：</p>
<pre class="crayon-plain-tag"># 编码为二进制数据
str.encode() 
# 编码为Unicode文本
bytes.decode()</pre>
<p>在原始字符串中，反斜杠被原样看待，例如： <pre class="crayon-plain-tag">r'\u20ac'</pre> 是6字符的串。</p>
<div class="blog_h3"><span class="graybg">函数注解</span></div>
<p>PEP 3107 – Function Annotations引入了为函数添加任何元数据（注解）的能力，注意这些注解没有任何具体语义，不改变函数的运行时行为，仅仅用于文档、类型提示（这是最重要的用法）以及为第三方框架提供信息。</p>
<p>语法形式：</p>
<pre class="crayon-plain-tag">def my_function(arg1: annotation1, arg2: annotation2) -&gt; annotation3:

# 类型提示的例子：
def greet(name: str, age: int) -&gt; str:
    return f"Hello, {name}! You are {age} years old."</pre>
<div class="blog_h3"><span class="graybg">仅关键字参数</span></div>
<p>调用函数时，这种参数必须使用关键字参数语法来传入。要定义仅关键字参数，使用一个<pre class="crayon-plain-tag">*</pre>号，其后面的参数均为仅关键字参数：</p>
<pre class="crayon-plain-tag">def my_function(arg1, *, kwarg1, kwarg2):
    print(f"arg1: {arg1}, kwarg1: {kwarg1}, kwarg2: {kwarg2}")

my_function(10, kwarg1="a", kwarg2="b")</pre>
<div class="blog_h3"><span class="graybg">nonlocal关键字</span></div>
<p>用于直接对外层作用域（非顶级作用域）中的变量进行赋值：</p>
<pre class="crayon-plain-tag">def outer_function():
    outer_var = 10

    def inner_function():
        nonlocal outer_var  # Declare outer_var as nonlocal to access it from the outer_function scope
        outer_var += 5</pre>
<div class="blog_h3"><span class="graybg">扩展迭代器解包语法</span></div>
<p>用于接收迭代器中所有其它对象：</p>
<pre class="crayon-plain-tag">(a, *rest, b) = range(5)     # rest为 [1, 2, 3 ]</pre>
<div class="blog_h3"><span class="graybg">字典推导</span></div>
<p>类似与列表推导：</p>
<pre class="crayon-plain-tag">{k: v for k, v in stuff}

squares = {i: i**2 for i in range(1, 6)}
even_squares = {i: i**2 for i in range(1, 11) if i % 2 == 0}</pre>
<div class="blog_h3"><span class="graybg">集合直接量</span></div>
<p>例如：<pre class="crayon-plain-tag">{1, 2}</pre>，注意{}表示空字典而非集合。空集合是<pre class="crayon-plain-tag">set()</pre>。</p>
<p>集合推导的语法和列表推导一样：<pre class="crayon-plain-tag">{x for x in stuff}</pre></p>
<div class="blog_h3"><span class="graybg">二进制/八进制直接量</span></div>
<pre class="crayon-plain-tag">0o720
0b1010</pre>
<div class="blog_h3"><span class="graybg">元类新语法</span></div>
<pre class="crayon-plain-tag"># 不在支持：
class C:
    __metaclass__ = M

# 新语法：
class C(metaclass=M):</pre>
<div class="blog_h2"><span class="graybg">版本3.1</span></div>
<div class="blog_h3"><span class="graybg"> 有序字典</span></div>
<p>原先字典迭代的顺序是任意的，可以使用<pre class="crayon-plain-tag">collections.OrderedDict</pre>来保证迭代顺序（按插入顺序迭代）：</p>
<pre class="crayon-plain-tag">from collections import OrderedDict

# Creating an OrderedDict
fruits = OrderedDict([
    ('apple', 4),
    ('banana', 6),
    ('orange', 2),
    ('grapes', 10),
])

# Adding a new item to the OrderedDict
fruits['strawberry'] = 8

# Output: apple -&gt; 4, banana -&gt; 6, orange -&gt; 2, grapes -&gt; 10, strawberry -&gt; 8
for fruit, count in fruits.items():
    print(f"{fruit} -&gt; {count}", end=", ")</pre>
<div class="blog_h3"><span class="graybg">增强的str.format</span></div>
<pre class="crayon-plain-tag">print("{}, {}".format("a", "b"))  # It now implicitly auto-numbers the fields. Output: "a, b"
print("{:,}".format(9876543210))  # The comma format specifier. Output: "9,876,543,210"</pre>
<div class="blog_h3"><span class="graybg">精确浮点加法</span></div>
<pre class="crayon-plain-tag">import math

nums = [1e20, 1, -1e20]
print(sum(nums))       # Result: 0.0, which can have an accumulated error
print(math.fsum(nums)) # Result: 1.0, a mathematically accurate summation</pre>
<div class="blog_h2"><span class="graybg">版本3.2</span></div>
<div class="blog_h3"><span class="graybg"> argparse模块<br /></span></div>
<p>强大、灵活的命令行参数解析模块，支持位置参数、子命令等：</p>
<pre class="crayon-plain-tag">import argparse

def main():
    parser = argparse.ArgumentParser(description="A simple script demonstrating argparse.")
    parser.add_argument("-n", "--name", required=True, help="Your name")
    parser.add_argument("-a", "--age", type=int, help="Your age")
    
    args = parser.parse_args()
    
    print(f"Hello, {args.name}!")
    if args.age:
        print(f"You are {args.age} years old.")

if __name__ == "__main__":
    main()</pre>
<div class="blog_h3"><span class="graybg">concurrent.futures</span></div>
<p>提供了基于线程/进程等方式来异步执行callable的高层接口，简化了工作线程、进程的管理：</p>
<pre class="crayon-plain-tag">import concurrent.futures
import time

def perform_work(n):
    time.sleep(n)
    return f"Work completed after {n} seconds"

# Run two tasks concurrently using ThreadPoolExecutor:
with concurrent.futures.ThreadPoolExecutor() as executor:
    work_items = [2, 3]
    results = [executor.submit(perform_work, n) for n in work_items]

    for result in concurrent.futures.as_completed(results):
        print(result.result())

# Run two tasks concurrently using ProcessPoolExecutor:
with concurrent.futures.ProcessPoolExecutor() as executor:
    work_items = [2, 3]
    results = [executor.submit(perform_work, n) for n in work_items]

    for result in concurrent.futures.as_completed(results):
        print(result.result())</pre>
<div class="blog_h2"><span class="graybg">版本3.3</span></div>
<div class="blog_h3"><span class="graybg">yield from</span></div>
<p>该表达式允许一个生成器将它的部分操作，委托给另外一个生成器。当一个生成器在迭代由另外一个生成器产生的条目时，可以简化代码。</p>
<pre class="crayon-plain-tag">def concat_gen(list_of_generators):
    for gen in list_of_generators:
        yield from gen   # 每次调用触发一次yield，第一个生成器完毕后，才循环到第二个

gen1 = (x for x in range(1, 4))  # Generates 1, 2, 3
gen2 = (x for x in range(4, 7))  # Generates 4, 5, 6

# Concatenate the generators using `yield from`
result_gen = concat_gen([gen1, gen2])

for x in result_gen:
    print(x)

# Output: 1, 2, 3, 4, 5, 6</pre>
<div class="blog_h3"><span class="graybg">venv模块和pyvenv脚本</span></div>
<p>用于代替第三方虚拟（隔离）环境模块virtualenv。pyvenv脚本用于管理虚拟环境。</p>
<pre class="crayon-plain-tag"># Create a virtual environment
python -m venv my_virtual_env

# Activate the virtual environment (on Linux or macOS)
source my_virtual_env/bin/activate

# Activate the virtual environment (on Windows)
my_virtual_env\Scripts\activate.bat

# Install packages within the virtual environment
pip install requests

# Deactivate the virtual environment when done
deactivate</pre>
<div class="blog_h3"><span class="graybg">lzma模块</span></div>
<p>支持基于LZMA算法的压缩，也就是那些扩展名为.xz  .7z  .lzma的压缩包。</p>
<pre class="crayon-plain-tag">import lzma

data = b"Example data that will be compressed using LZMA."

# Compress data using LZMA
compressed_data = lzma.compress(data)

# Decompress the data back to its original form
original_data = lzma.decompress(compressed_data)

assert data == original_data, "Decompressed data should match the original data."</pre>
<div class="blog_h3"><span class="graybg">faulthandler模块</span></div>
<p>用于在关键事件（例如段错误）时dump出Python的调用栈。</p>
<pre class="crayon-plain-tag">import faulthandler
import os
import time
import sys

# Enable faulthandler to dump tracebacks to a file
with open("traceback.log", "w") as logfile:
    faulthandler.dump_traceback(file=logfile)

# You can also enable fault handling for uncaught exceptions and signals (e.g., SIGSEGV)
faulthandler.enable(file=sys.stderr, all_threads=True)

# Your program code...
time.sleep(1)</pre>
<div class="blog_h3"><span class="graybg">命名空间包</span></div>
<p>引入一种机制，允许单个包跨越多个目录，可以实现更好的模块化，并且让过大的包易于维护。例如对于下面的目录结构：</p>
<pre class="crayon-plain-tag">dir1/
    my_package/
        __init__.py
        module1.py

dir2/
    my_package/
        # no __init__.py file needed
        module2.py</pre>
<p>可以将my_package看作单一的命名空间包，不需要任何额外配置，导入该包中定义的两个模块：</p>
<pre class="crayon-plain-tag">from my_package.module1 import class1
from my_package.module2 import class2</pre>
<div class="blog_h2"><span class="graybg">版本3.4</span></div>
<div class="blog_h3"><span class="graybg">asyncio模块</span></div>
<p> 这个模块用于实现异步IO、并发编程。从3.5开始，可以利用关键字<pre class="crayon-plain-tag">async</pre> /  <pre class="crayon-plain-tag">await</pre>。</p>
<pre class="crayon-plain-tag">import asyncio

async def hello():
    print("Hello")
    await asyncio.sleep(1)
    print("World")

async def main():
    # Schedule two coroutines to run concurrently
    task1 = asyncio.create_task(hello())
    task2 = asyncio.create_task(hello())

    await task1
    await task2

# Execute main asynchronously using asyncio.run (Python 3.7 and later)
asyncio.run(main())</pre>
<p> 注意，对async函数的调用，会得到一个协程（或者生成器），而不是直接同步的执行函数体：</p>
<pre class="crayon-plain-tag"># 异步函数（协程）
async def async_function():
    return 1
print(type(async_function()) is types.CoroutineType)
 
 
# 异步生成器
async def async_generator():
    yield 1
print(type(async_generator()) is types.AsyncGeneratorType)</pre>
<p>你可以向操作普通协程那样，对其调用send()：</p>
<pre class="crayon-plain-tag">try:
    async_function().send(None)
except StopIteration as e:
    # 生成器/协程在正常返回退出时会抛出一个StopIteration异常，而原来的返回值会存放在StopIteration对象的value属性中
    print(e.value)</pre>
<p>在async函数中，可以使用await挂起自身，并等待另外一个协程的结果：</p>
<pre class="crayon-plain-tag">async def async_function():
    return 1
 
async def await_coroutine():
    # await语法只能出现在通过async修饰的函数中，否则会报SyntaxError错误
    result = await async_function()
    print(result)
    
run(await_coroutine())



# 注意 await后面必须跟着一个 Awaitable，或者实现了 __await__ 方法：
class Awaitable(metaclass=ABCMeta):
    __slots__ = ()
 
    @abstractmethod
    def __await__(self):
        yield
 
    @classmethod
    def __subclasshook__(cls, C):
        if cls is Awaitable:
            return _check_methods(C, "__await__")
        return NotImplemente</pre>
<div class="blog_h3"><span class="graybg">enum模块</span></div>
<p>可以方便的定义简单的枚举类。</p>
<pre class="crayon-plain-tag">from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

# Usage examples
print(Color.RED)        # Output: Color.RED
print(Color.RED.name)   # Output: RED
print(Color.RED.value)  # Output: 1

# 可以让枚举类同时继承A，这样它就有了A类型的能力，例如用在需要A的地方</pre>
<div class="blog_h3"><span class="graybg">pathlib模块</span></div>
<p>用于简化文件系统路径操作。</p>
<pre class="crayon-plain-tag">from pathlib import Path

path = Path("example.txt")

# Check if the file exists
if not path.exists():
    # Create a new file and write some content
    path.write_text("Hello, World!")

# Read file content
content = path.read_text()
print(content)  # Output: Hello, World!

# List all files in the current directory
for file_path in Path(".").iterdir():
    print(file_path) </pre>
<div class="blog_h3"><span class="graybg">pickle模块</span></div>
<p>为pickle增加了新版本（4）的默认协议，支持更加高效的、针对大规模数据结构的串行化。</p>
<pre class="crayon-plain-tag">import pickle

data = {
    "name": "Alice",
    "age": 30,
    "city": "New York",
}

# Serialize data using protocol version 4 (new in Python 3.4)
serialized = pickle.dumps(data, protocol=4)

# Deserialize the data back to a Python object
deserialized_data = pickle.loads(serialized)

assert data == deserialized_data, "Deserialized data should match the original data."</pre>
<div class="blog_h3"><span class="graybg">selectors模块</span></div>
<p>提供了事件驱动的IO框架、在套接字以及其它非阻塞IO上的多路复用的IO操作支持。</p>
<pre class="crayon-plain-tag"># This example shows how to use `selectors` for a simple echo server.
# For brevity, error handling is omitted. This example is for Linux/Unix-based systems using `selectors.DefaultSelector`.
import socket
import selectors

sel = selectors.DefaultSelector()

def accept(sock):
    conn, addr = sock.accept()
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)

def read(conn):
    received_data = conn.recv(1000)
    if received_data:
        # Echo the received data back to the client
        conn.sendall(received_data)
    else:
        # Connection closed, unregister the socket from selector
        sel.unregister(conn)
        conn.close()

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('localhost', 1234))
server_socket.listen(5)
server_socket.setblocking(False)
sel.register(server_socket, selectors.EVENT_READ, accept)

while True:
    events = sel.select()
    for key, _ in events:
        key.data(key.fileobj) </pre>
<div class="blog_h3"><span class="graybg">单分发通用函数</span></div>
<p>通过装饰器<pre class="crayon-plain-tag">functools.singledispatch</pre>实现，用于定义多个版本的函数实现，每个函数针对不同的参数类型。</p>
<pre class="crayon-plain-tag">from functools import singledispatch

@singledispatch
def process_data(data):
    print(f"Unknown data type: {data}")

@process_data.register(int)
def _(data: int):
    print(f"Processing integer data: {data}")

@process_data.register(str)
def _(data: str):
    print(f"Processing string data: {data}")

process_data(42)         # Output: Processing integer data: 42
process_data("Hello")    # Output: Processing string data: Hello
process_data([1, 2, 3])  # Output: Unknown data type: [1, 2, 3]</pre>
<div class="blog_h2"><span class="graybg">3.5</span></div>
<div class="blog_h3"><span class="graybg"> 矩阵乘法操作符</span></div>
<pre class="crayon-plain-tag">import numpy as np

mat1 = np.array([[1, 2], [3, 4]])
mat2 = np.array([[5, 6], [7, 8]])

mat_product = mat1 @ mat2  # Matrix multiplication using new @ operator

# Output: array([[19, 22], [43, 50]])
print(mat_product)</pre>
<div class="blog_h3"><span class="graybg">解包能力增强</span></div>
<p>可以在单个函数调用、单个推导（comprehension）操作中，从多个可迭代对象中解包多个元素：</p>
<pre class="crayon-plain-tag">a = [1, 2]
b = [3, 4, 5]

# Merging lists using unpacking generalizations
merged_list = [*a, *b]  # Output: [1, 2, 3, 4, 5]

dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}

merged_dict = {**dict1, **dict2}  # Output: {'a': 1, 'b': 2, 'c': 3, 'd': 4}</pre>
<div class="blog_h3"><span class="graybg">类型提示</span></div>
<p>包<pre class="crayon-plain-tag">typing</pre>提供了增强的类型提示能力，例如，可以指定容器类型的元素的类型。</p>
<pre class="crayon-plain-tag">from typing import List, Dict

#                列表值类型            字典键值了类型       非容器类型
def greet(names: List[str], age_dict: Dict[str, int]) -&gt; str:
    greetings = []
    for name in names:
        age = age_dict.get(name)
        greeting = f"Hello, {name}! You are {age} years old."
        greetings.append(greeting)
    return "\n".join(greetings)

names_list = ["Alice", "Bob", "Charlie"]
ages_dict = {"Alice": 25, "Bob": 30, "Charlie": 35}

result = greet(names_list, ages_dict)
print(result)</pre>
<p>除了常用的集合类型容器，typing模块还提供了：</p>
<ol>
<li><pre class="crayon-plain-tag">Optional</pre>：用于提示参数或者字段是可选的</li>
<li><pre class="crayon-plain-tag">Union</pre>：用于提示参数可能是多个类型之一</li>
<li><pre class="crayon-plain-tag">Any</pre>：不限制类型</li>
<li><pre class="crayon-plain-tag">Callable</pre>：提示参数是可调用的</li>
</ol>
<p>对于复杂的、反复使用的类型提示，可以定义别名：</p>
<pre class="crayon-plain-tag">Person = Tuple[str, int]
People = List[Person]</pre>
<div class="blog_h3"><span class="graybg">async with</span></div>
<p>这个语句初始化一个异步的上下文管理器。异步上下文管理器对象必须实现特定的方法，以创建/清理临时上下文，即使有异常抛出，清理工作也会执行。需要实现的方法如下：</p>
<ol>
<li><pre class="crayon-plain-tag">async def __aenter__(self)</pre>：进入async with块时该方法被调用</li>
<li><pre class="crayon-plain-tag">async def __aexit__(self, exc_type, exc_value, traceback)</pre>：离开块时该方法被调用</li>
</ol>
<p>示例：</p>
<pre class="crayon-plain-tag">import aiofiles

async def read_file(file_name: str) -&gt; str:
    async with aiofiles.open(file_name, mode="r") as f:
        text = await f.read()
    return text</pre>
<p>和同步的上下文管理器的主要区别是：资源创建、主体逻辑、资源清理都是异步进行的，而后者这三个操作是<span style="background-color: #c0c0c0;">同步进行（即在同一个事件循环中、阻塞其它操作）</span>。</p>
<div class="blog_h2"><span class="graybg">版本3.6</span></div>
<div class="blog_h3"><span class="graybg">f格式化字符串</span></div>
<pre class="crayon-plain-tag">name = "John"
age = 25
print(f"Hello, my name is {name} and I'm {age} years old.")</pre>
<div class="blog_h3"><span class="graybg">数字可以用下划线</span></div>
<pre class="crayon-plain-tag">one_million = 1_000_000
print(f"One million is written as {one_million}.")</pre>
<div class="blog_h3"><span class="graybg">变量注解语法 </span></div>
<pre class="crayon-plain-tag">count: int = 0
def greet(name: str) -&gt; str:
    return f'Hello, {name}'

print(greet("John"))</pre>
<div class="blog_h3"><span class="graybg">异步生成器</span></div>
<p>可以对列表、集合、字典以及列表推导、生成器等使用<pre class="crayon-plain-tag">async for</pre>操作：</p>
<pre class="crayon-plain-tag">import asyncio

async def ticker(delay, to):
    """Yield numbers from 0 to `to` every `delay` seconds."""
    for i in range(to):
        yield i
        await asyncio.sleep(delay)

async def run():
    # 这里的迭代是异步进行的
    async for i in ticker(1, 5):
        print(i)

asyncio.run(run())</pre>
<div class="blog_h2"><span class="graybg">版本3.7 </span></div>
<div class="blog_h3"><span class="graybg">类型注解的延迟估算</span></div>
<p>这个特性使得使用前向引用（forward references ，即引用尚未定义的类型）和生命类型提示变得简单，不再需要在类型注解中使用字符串直接量。</p>
<p>当存在类型之间的循环引用时，类型注解可能会导致问题，为了解决此问题，需要使用字符串直接量：</p>
<pre class="crayon-plain-tag">class A:
    def x(self) -&gt; "B":  # Using string literal for forward reference
        pass

class B:
    def y(self) -&gt; A:
        pass</pre>
<p>有了该特性后，则可以：</p>
<pre class="crayon-plain-tag">from __future__ import annotations  # Import annotations from __future__

class A:
    def x(self) -&gt; B:  # No need for string literal
        pass

class B:
    def y(self) -&gt; A:
        pass</pre>
<div class="blog_h3"><span class="graybg">dataclasses </span></div>
<p>这个模块提供了一个装饰器，用来装饰一个仅仅用来存放数据的类型：</p>
<pre class="crayon-plain-tag">from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

p1 = Point(1.0, 2.5)
p2 = Point(3.5, 0.5)

print(p1)  # Output: Point(x=1.0, y=2.5)
print(p2)  # Output: Point(x=3.5, y=0.5)</pre>
<div class="blog_h3"><span class="graybg">breakpoint()函数</span></div>
<p>调用该函数，可以直接从代码进入Python Debugger（PDB），可以方便调试：</p>
<pre class="crayon-plain-tag">def divide(a, b):
    breakpoint()  # Debugger will be triggered here
    return a / b

result = divide(4, 2)</pre>
<div class="blog_h2"><span class="graybg">版本3.8</span></div>
<div class="blog_h3"><span class="graybg">:=操作符</span></div>
<p>支持将赋值作为表达式的一部分：</p>
<pre class="crayon-plain-tag">n = 10
while (squared := n * n) &lt; 100:
    print(squared)
    n += 1</pre>
<div class="blog_h3"><span class="graybg">仅位置参数</span></div>
<p>支持指定某些函数参数，必须以位置参数的形式传入：</p>
<pre class="crayon-plain-tag">def my_function(pos1, pos2, /, pos_or_kwarg1, *, kwarg1, kwarg2):
    pass

# Allowed:
my_function(1, 2, 3, kwarg1=4, kwarg2=5)

# Not allowed:
my_function(1, 2, pos_or_kwarg1=3, kwarg1=4, kwarg2=5)</pre>
<p>符号 <pre class="crayon-plain-tag">/</pre> 前面的都是仅位置参数。</p>
<div class="blog_h3"><span class="graybg">逆转字段顺序</span></div>
<p>内置函数可以用来逆转字典键值对顺序：</p>
<pre class="crayon-plain-tag">my_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
reversed_items = list(reversed(my_dict.items()))

print(reversed_items)  # Output: [('d', 4), ('c', 3), ('b', 2), ('a', 1)]</pre>
<div class="blog_h2"><span class="graybg">版本3.9</span></div>
<div class="blog_h3"><span class="graybg">字典合并操作符</span></div>
<pre class="crayon-plain-tag">dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}

merged = dict1 | dict2
print(merged)  # Output: {"a": 1, "b": 3, "c": 4}

dict1 |= dict2
print(dict1)  # Output: {"a": 1, "b": 3, "c": 4}</pre>
<div class="blog_h3"><span class="graybg">新的字符串方法</span></div>
<p>移除字符串前后缀的新方法：</p>
<pre class="crayon-plain-tag">filename = "document.pdf"

name = filename.removesuffix(".pdf")
print(name)  # Output: "document"

name = filename.removeprefix("docu")
print(name)  # Output: "ment.pdf"</pre>
<div class="blog_h3"><span class="graybg">标准库的时区支持</span></div>
<pre class="crayon-plain-tag">from datetime import datetime
from zoneinfo import ZoneInfo

dt = datetime(2021, 1, 1, tzinfo=ZoneInfo("America/New_York"))
print(dt)  # Output: 2021-01-01 00:00:00-05:00</pre>
<div class="blog_h3"><span class="graybg">类型提示的泛型支持</span></div>
<p>在老版本，你必须借助typing模块：</p>
<pre class="crayon-plain-tag">from typing import List

def process_numbers(numbers: List[int]) -&gt; None:
    for number in numbers:
        print(number * 2)</pre>
<p>新版本中直接使用内置类型：</p>
<pre class="crayon-plain-tag">def process_dict(data: dict[str, int]):
    for key, value in data.items():
        print(key, value * 2)

def process_tuple(data: tuple[int, int, int]):
    for value in data:
        print(value * 2)

def process_set(data: set[int]):
    for value in data:
        print(value * 2)</pre>
<p>需要注意，某些库可能仍然依赖旧的语法。</p>
<div class="blog_h2"><span class="graybg">版本3.10</span></div>
<div class="blog_h3"><span class="graybg">更简单的联合类型提示</span></div>
<p>不再需要Union</p>
<pre class="crayon-plain-tag"># Before Python 3.10 Release
from typing import Union
def f(list: List[Union[int, str]], param: Optional[int]):
    pass

# In Python 3.10 Release
def f(list: List[int | str], param: int | None):
    pass

# Calling the function
f([1, “abc”], None)</pre>
<div class="blog_h3"><span class="graybg">match-case分支判断</span></div>
<pre class="crayon-plain-tag">def process_number(number):
    match number:
        case 1:
            print("One")
        case 2:
            print("Two")
        case _:
            print("Other")

process_number(1)  # Output: One
process_number(5)  # Output: Other </pre>
<div class="blog_h3"><span class="graybg">结构化模式匹配</span></div>
<p>使用match - case语句，可以对复杂结构进行模式匹配：</p>
<pre class="crayon-plain-tag">def process_data(data):
    match data:
        case None:
            print("No data")
        case {"status": "success", "result": int(result)}:
            print(f"Success: {result}")
        case {"status": "error", "message": str(message)}:
            print(f"Error: {message}")
        case _:
            print("Unknown data format")</pre>
<div class="blog_h3"><span class="graybg">多行上下文管理器</span></div>
<pre class="crayon-plain-tag">with open("file1.txt") as file1, open("file2.txt") as file2:  # Before Python 3.10
    pass

# In Python 3.10
with (
    open("file1.txt") as file1,
    open("file2.txt") as file2,
):
    pass</pre>
<div class="blog_h2"><span class="graybg">版本3.11</span></div>
<div class="blog_h3"><span class="graybg">异常组和except*</span></div>
<p>异常组让相关的异常被一起处理。</p>
<p>在Python中，所有异常均是BaseException的子类型。异常具有一个message参数来提供消息：</p>
<pre class="crayon-plain-tag">raise SyntaxError("Just raising a syntax error")</pre>
<p> 使用try - except块可以捕获并处理异常：</p>
<pre class="crayon-plain-tag">try:
   prin(34/0)
except (ZeroDivisionError, NameError) as exc:
   print(exc)
except  XxError as e:
   pass</pre>
<p>这种处理方式有以下限制：</p>
<ol>
<li>一次只能处理一个异常</li>
<li>仅仅会执行第一个匹配的except块</li>
</ol>
<p><pre class="crayon-plain-tag">ExceptionGroup</pre>是Exception的子类，你可以像处理普通异常一样处理它：</p>
<pre class="crayon-plain-tag">print(issubclass(ExceptionGroup, Exception))

raise ExceptionGroup("exception groups", [ValueError(1), TypeError(2)]) 

try:
    raise ExceptionGroup("An exception group", [ValueError(), TypeError(1)])
except ExceptionGroup:
    print("I caught an exception group")</pre>
<p>异常组的第二参数，是组中包含的异常的列表。你可以捕获其中任何成员：</p>
<pre class="crayon-plain-tag">try:
     raise ExceptionGroup("An exception group", [ValueError(), \
     TypeError(1)])
except TypeError:
     print("I am handling the TypeError in the exception group")</pre>
<p>使用特殊的<pre class="crayon-plain-tag">except *</pre>可以捕获多个异常成员：</p>
<pre class="crayon-plain-tag">try:
    raise ExceptionGroup("An exception group", [ValueError(), TypeError(1)])
except * TypeError:
    print("I am handling a Type error")
except * ValueError:
    print("I am handling a ValueError")

# I am handling a Type error
# I am handling a ValueError </pre>
<div class="blog_h3"><span class="graybg">类型提示的参数化</span></div>
<pre class="crayon-plain-tag">from typing import TypeVar

T0 = TypeVar("T0")
T1 = TypeVar("T1")

def flip(pair: tuple[T0, T1]) -&gt; tuple[T1, T0]:
    first, second = pair
    return (second, first)</pre>
<div class="blog_h3"><span class="graybg">Self类型提示</span></div>
<p>这个特殊的类型表示当前类的类型。</p>
<pre class="crayon-plain-tag">from typing import Self

class Article:
   def a_method_that_returns_an_instance(self) -&gt; Self:
       ...</pre>
<p>&nbsp;</p>
<div class="blog_h3"><span class="graybg">TypeVarTuple类型提示</span></div>
<p>这个类型允许容器类型具有任意数量的元素类型：</p>
<pre class="crayon-plain-tag">from typing import TypeVarTuple, TypeVar, Tuple

TS = TypeVarTuple("TS")
T = TypeVar("T")
def example(value_1:Tuple[T, *TS]):  # 必须使用*解包语法
   ...


example(value_1=(1, 'a number', 3.0))</pre>
<div class="blog_h3"><span class="graybg">TypedDict类型化字典</span></div>
<p>用于限定字典具有哪些键值、值的类型：</p>
<pre class="crayon-plain-tag">from typing import TypedDict

class ArticleType(TypedDict):
   article_id: int
   title: str
   rating: float


article_1: ArticleType = {
   "article_id": 23,
   "title": "Introducing the new features in Python 3.11",
   "rating":4.5
}</pre>
<p>在3.11，你可以控制某个键值是否为必须：</p>
<pre class="crayon-plain-tag">from typing import TypedDict, Required, NotRequired

class ArticleType(TypedDict):
   article_id: Required[int]
   title: NotRequired[str]
   rating: float</pre>
<p>&nbsp;</p>
<div class="blog_h1"><span class="graybg">Python语言基础</span></div>
<div class="blog_h2"><span class="graybg">基于缩进的代码风格</span></div>
<p>Python的每个语句以换行符结束，如果太长，可以使用续行符（反斜杠）跨行</p>
<pre class="crayon-plain-tag">a = math.cos(3 * (x - n)) + \
    math.sin(3 * (x - n))</pre>
<p>使用<span style="background-color: #c0c0c0;">三引号定义的字符串</span>、<span style="background-color: #c0c0c0;">列表</span>、<span style="background-color: #c0c0c0;">元组</span>或<span style="background-color: #c0c0c0;">字典</span>分布在多行上时，不需要使用续行符。</p>
<p>缩进用于表示<span style="background-color: #c0c0c0;">不同的代码块，如函数体、条件语句、循环和类</span>。代码块中首条语句的<span style="background-color: #c0c0c0;">缩进</span>可以任意的，但是后续语句必须与之<span style="background-color: #c0c0c0;">保持一致</span>。</p>
<p>如果函数体、分支、循环等较短，可以放在一行，不需要缩进：</p>
<pre class="crayon-plain-tag">if a: pass</pre>
<p>应当使用空格，而不是制表符进行缩进。</p>
<div class="blog_h2"><span class="graybg">标识符和保留字</span></div>
<div class="blog_h3"><span class="graybg">标识符</span></div>
<p>变量标识符<span style="background-color: #c0c0c0;">仅支持：数字、下划线、A-Za-z</span>，并且数字不能作为标识符的开头。标识符区分大小写。</p>
<p>以下划线开始或结束的标识符具有特殊含义：</p>
<ol>
<li>以单下划线开头的标识符不能通过from module import *导入</li>
<li>__func__用于定义特殊方法</li>
<li>__priv用于定义类私有成员</li>
</ol>
<div class="blog_h2"><span class="graybg">基础数据类型</span></div>
<div class="blog_h3"><span class="graybg">数字（int、Long、float、complex、bool）</span></div>
<pre class="crayon-plain-tag">#Python中，整数的位数是任意的
bigInt = 15156165496484919616
#不同进制的整数
0644        #八进制
0x100fea8   #十六进制
0b1110001   #二进制
#浮点数表示
3.14
1.2334e+02
#使用等号赋值
width = 20
#整数与浮点数运算时，自动转换为浮点数
3 * 3.75 / 1.5
#实数的类型转换函数：int、float、long等
int(3.75)

#支持复数，j（或者J）表示虚部
z = 1.5 + 0.5j
#获取实部、虚部
z.real
z.imag</pre>
<div class="blog_h3"><span class="graybg">布尔值</span></div>
<p>标识符<span style="background-color: #c0c0c0;">True和False</span>被解释为布尔值，其整数值分别是<span style="background-color: #c0c0c0;">1和0</span>。</p>
<div class="blog_h3"><span class="graybg">序列类型</span></div>
<p>字符串、列表、元组，统称为序列类型。序列类型具有共同的特征：</p>
<ol>
<li>支持索引访问，例如s[0]</li>
<li>支持切片运算符，例如s[0:5]，对于可变序列，还可以删除切片，例如del s[i:j]</li>
<li>使用len(s)可以返回序列长度</li>
<li>使用min(s)、max(s)可以返回序列中元素的最小最大值</li>
<li>使用all(s)、any(s)可以检查是否每个元素、存在任何元素为True</li>
</ol>
<div class="blog_h3"><span class="graybg">字符串（str、unicode）</span></div>
<p>支持双引号或者单引号的字符串。Python字符串是不可变的。</p>
<pre class="crayon-plain-tag">#使用单引号、双引号定义多行文本，必须在行尾添加换行+续行符，行首的空白符也会被识别
str = "This is a long string\n   \
    containing two lines os text \
"

#可以用三引号对来标识字符串，不需要\n转义
"""
Hello there
Greetings
"""

#原始字符串，不进行转义
str = r"\n is just \n"
#字符串可以使用+来连接，使用*来重复：
str = str + str * str

#字符串切片，起始索引、结束索引如果不指定，分别为0、字符串长度
#容错：如果结束索引过大，自动认为等于字符串长度，如果开始索引小于结束索引，返回空串
str = HelpA
word[4] == 'A'
word[0:2] == 'He'
#如果索引为负数，则表示从右侧算索引
word[-1] = 'A'
word[-2] = 'p'
word[-2:] = 'pA'
word[-0] = 'H' #-0就作为0看待
word[:] = 'HelpA'  #这种特殊的写法表示返回完整的字符串

#字符串相关函数
len(s) #返回字符串的长度
str(3.4) #转换为字符串
repr(3.4)#转化为字符串，显示为对象内部精确值：3.3999999999999999
format(3.4,'0.5f') #格式化输出3.40000</pre>
<p>Python 2.0以后引入Unicode，来表示Unicode字符串，必要时可以与原始字符串进行转换：</p>
<pre class="crayon-plain-tag">#使用u前缀表示Unicode字符串
str = u"蟒蛇"
#使用Unicode转义\u****来插入特殊Unicode字符
str = u"Hello\u0020World"
#Unicode字符串的原始模式（不进行转义）
str = ur"Hello\u0020World"</pre>
<p>利用str()函数进行转换时，会使用默认编码（通常是ASCII）：</p>
<pre class="crayon-plain-tag">&gt;&gt;&gt; str(u"蟒蛇")
Traceback (most recent call last):
  File "", line 1, in 
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)</pre>
<p>可以使用encode方法来获取特定编码的16进制转写：</p>
<pre class="crayon-plain-tag">u"蟒蛇".encode('utf-8')
#内置函数unicode可以使用所有已注册的Unicode编码来解码
unicode('\xc3\xa4\xc3\xb6\xc3\xbc', 'utf-8')</pre>
<div class="blog_h3"><span class="graybg">列表（list）</span></div>
<pre class="crayon-plain-tag">#列表的元素可以是不同的类型
a = ['spam', 'eggs', 100, 1234]
#类似字符串，列表可以被切片或者连接，切片返回浅拷贝的副本
a[0] == 'spam'
a[-2] == 100
a[:2] + ['bacon', 2*2] == ['spam', 'eggs', 'bacon', 4]
a = [1, 2, 3] + [4, 5]  #连接列表

#内置函数len可以用于获取列表的长度
len(2)

#相关方法
a = list()  #等价于a = []
a.append(1) #在尾部插入元素
a.insert(2,200) #插入元素到指定索引位置</pre>
<div class="blog_h3"><span class="graybg">元组（tuple）</span></div>
<p>在圆括号里面包含一组值，即为元组。元组创建后，不能修改其内容（替换、添加或者删除元素）。使用元组代替小列表，更加节约内存</p>
<pre class="crayon-plain-tag">corp = ("3203001102256", "徐州工业集团", 3200) #定义元组
corp = "3203001102256", "徐州工业集团", 3200   #这样也可以识别元组
a = ()   #空元组
b = (1,) #一元组，注意结尾的逗号
c = 1,   #一元组

#可以使用数字索引获取元组中的值，但是更常见的做法是将元组解包为一组变量
regNo, corpName, regCapi = corp</pre>
<div class="blog_h3"><span class="graybg">集合（set、frozenset）</span></div>
<p>集合是无序的、不包含重复元素的对象组：</p>
<pre class="crayon-plain-tag">s = set ([3,5,9,10])	#创建一个数值集合
t = set ("Hello")	#创建包含4个字符的集合（两个l只能保存一个在集合中）

#集合运算符
a = t | s   #t和S的并集
a = t &amp; s   #t和s的交集
a = t - s   #求差集（项在t中，但不在s中）
a = t ^ s   #对称差集（项在t或S中，但不会同时出现在二者中）

#集合方法
t.add('x')  #添加一个项
s.update([10,37,42])#添加多个项
t.remove('H') #删除一个项</pre>
<div class="blog_h3"><span class="graybg">字典（dict）</span></div>
<p>字典就是关联数组（散列表）。字符串、元组等可以作为散列键，但是可变对象例如列表、字典则不可以作为键。</p>
<pre class="crayon-plain-tag">prices = {
    "GOOG" : 490.10, 
    "APPL" : 123.50, 
    "IBM"  : 91.50, 
    "MSFT" : 52.13
}

#创建空字典
prices = {}
prices = dict()

#使用in运算符可以测试某个项是否字典成员
if "GOOG" in prices:
    p = prices["GOOG"]

#相关函数
keys = list(prices) #获取关键字的列表
del prices["GOOG"]  #删除字典元素

#相关方法
p = prices.get("GOOG", 0.0) #如果不存在，返回0.0</pre>
<div class="blog_h3"><span class="graybg">None</span></div>
<p>None是一个特殊的类型，用于表示null对象。<br /> 如果一个函数没有显式的返回值，则自动返回None。布尔求值时为False</p>
<div class="blog_h2"><span class="graybg">流程控制</span></div>
<div class="blog_h3"><span class="graybg">if分支结构</span></div>
<pre class="crayon-plain-tag">x = int(raw_input("Please enter an integer: "))
if x &lt; 0:
    x = 0
    print 'Negative changed to zero'
elif x == 0:
    print 'Zero'
elif x == 1:
    print 'Single'
else:
    print 'More'

#条件表达式简写
minvalue = a if a &lt;= b else b</pre>
<div class="blog_h3"><span class="graybg">for循环结构</span></div>
<pre class="crayon-plain-tag">a = ['cat', 'window', 'defenestrate']
for x in a:
    print x, len(x) #逗号分隔的print可以连续打印不换行
#打印：
#cat 3
#window 6
#defenestrate 12

#迭代列表的副本，以便安全的修改列表
for x in a[:]:
    if len(x) &gt; 6: a.insert(0, x)
    elif False : continue
    elif False : break
    elif False : pass     #什么都不做，作为语法占位符

#使用range函数可以生成一个等差级数俩表：
range(10)             #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(5, 10)          #[5, 6, 7, 8, 9]
range(-10, -100, -30) #[-10, -40, -70] 第三个参数为步长

#迭代一个列表
a = ['Mary', 'had', 'a', 'little', 'lamb']
for i in range(len(a)):
    print i, a[i]
#或者
for item in a:
    print item
#解包一个序列，变量个数必须与序列元素个数一致
for x,y,z in s:
    pass
#内置函数enumerate，迭代序列，返回索引、元素组成的元组
for i,x in enumerate(s):
    pass
#同时迭代两个以上的序列，使用内置的zip函数
for x,y,z in zip(r,s,t):
    pass
#迭代一个字典
c = {"GOOG": 490.10, "IBM":91.50 }
for key in c:
    print key, c[key]

#打印一个文件中所有行
f = open("foo.txt")
for line in f:
    print line</pre>
<div class="blog_h2"><span class="graybg">函数</span></div>
<pre class="crayon-plain-tag">#关键字def引入函数定义，其后必须跟有函数名、形参列表
def reminder(a, b):
#函数体必须缩进，可以添加docstring
    """docstring"""   #使用remider.__doc__可以访问文档字符串
    q = a // b        #截断除法运算符
    r = a - q * b
    return r          #如果省略return语句，自动返回None
#如果函数要返回多个值，可以使用元组
def divide(a, b):
    q = a // b
    r = a - q*b
    return (q, r)

#形参可以具有默认值
def connect(host ,port, timeout = 300):
    h0 = host   #在函数体内创建的变量，作用域是局部的，函数退出后自动销毁
    global h0 = host #使用此关键字来修改全局变量的值
    pass

#位置参数：要使用函数，只需要传入实参列表即可，参数数量、顺序必须匹配，否则引发TypeError
reminder(88, 3)
#可以省略提供默认值的参数
connect('gmem.cc', 80)
#关键字参数：可以按照任意顺序提供参数，但是需要提供形参名称
connect(port = 80, host = 'gmem.cc')

#使用可变对象作为形参默认值，会导致意外的行为：每次调用，可能使用同一个对象
def add(x, items=[]):
    items.append(x)
    return items
add(1)          #返回[1]
add(2)          #返回[1, 2]，不符合预期

#在最后一个参数前面加*，可以接受任意数量的位置参数，自动存入元组
def fprintf(file, fmt, *args):
    file.write(fmt % args)
fprintf(out,"%d %s %f", 42, "hello world", 3.45) #args == (42, "hello world", 3.45)

#在最后一个参数前加**，则所有额外的关键字参数存入字典
def make_tab(data, **params):
    fc = params.pop('fgcolor','black')
make_tab(items, fgcolor='red')

#位置参数与关键字参数可以一起使用，**必须出现在最后面
#编写代理、包装器函数时，经常需要使用*args、**kwargs
def func(*args, **kwargs):
    pass</pre>
<div class="blog_h3"><span class="graybg">类型注解</span></div>
<p>Python支持类型注解：</p>
<pre class="crayon-plain-tag"># Python 3 类型注解写法
#         参数类型
#                        返回值类型
def add(x:int, y:int) -&gt; int:
    return x + y

# Python 2写法
def add(x, y):
    return x + y</pre>
<p>类型注解的作用是，让开发人员直观的了解返回值类型，让IDE能够进行自动的类型推导。对解释器的行为不产生任何影响。 </p>
<p>对于容器类型，需要从<pre class="crayon-plain-tag">typing</pre>包引入一些类：</p>
<pre class="crayon-plain-tag">from typing import List, Dict, Tuple, Sequence

# 可以指定容器元素类型
def list() -&gt; List[float]: pass

# 可以定义类型别名
Vector = List[float]
def list() -&gt; Vector: pass


# 复杂的类型注解
ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]

# 这样也可以
Sequence[Tuple[Tuple[str, int], Dict[str, str]]]</pre>
<div class="blog_h3"><span class="graybg">函数的属性</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;"> 属性</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>__doc__</td>
<td>文档字符串</td>
</tr>
<tr>
<td>__name__</td>
<td>函数名称 </td>
</tr>
<tr>
<td>__dict__</td>
<td>包含函数属性的字典</td>
</tr>
<tr>
<td>__code__</td>
<td>字节编译的代码</td>
</tr>
<tr>
<td>__defaults__</td>
<td>默认参数的元组</td>
</tr>
<tr>
<td>__globals__</td>
<td>定义函数的全局命名空间，即定义了函数的模块中所有全局变量/函数构成的字典</td>
</tr>
<tr>
<td>__closure__</td>
<td>嵌套作用域相关数据的元组</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2">方法的属性</td>
</tr>
<tr>
<td>__class__</td>
<td>定义方法的类</td>
</tr>
<tr>
<td>__func__</td>
<td>实现方法的函数对象</td>
</tr>
<tr>
<td>__self__</td>
<td>与方法相关的实例，如果是非绑定方法，则为None</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">常用内置函数</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 200px; text-align: center;"> 函数</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>abs (x)</td>
<td>返回绝对值</td>
</tr>
<tr>
<td>all(s)</td>
<td>如果可迭代的s中的所有值都为True,则返回True</td>
</tr>
<tr>
<td>any(s)</td>
<td>如果可迭代的s中的任意值为True，则返回True</td>
</tr>
<tr>
<td>ascii(x)</td>
<td>类似repr()，创建对象的可打印格式，但是只使用ASCII字符，非ASCII字符使用转义序列</td>
</tr>
<tr>
<td>bin(x)</td>
<td>返回一个字符串，其中包含整数二进制形式</td>
</tr>
<tr>
<td>bool([x])</td>
<td>转换为布尔型</td>
</tr>
<tr>
<td>bytearray ([x])</td>
<td>可变字节数组，x可能是范围从0到255的可迭代整数序列、8位字符串或字节字面量</td>
</tr>
<tr>
<td>bytearray(s,encoding)</td>
<td>从字符串创建字节数组，使用指定的编码</td>
</tr>
<tr>
<td>bytes ([x])</td>
<td>表示不变字节数组</td>
</tr>
<tr>
<td>chr(x)</td>
<td>将整数值转换为单字符的字符串。在Python 2中，x必须在0 &lt;= x &lt;= 255范围内</td>
</tr>
<tr>
<td>classmethod(func)</td>
<td>用于创建类方法，@classmethod装饰器隐式调用它</td>
</tr>
<tr>
<td>cmp(x, y)</td>
<td>比较两个对象，如果x&gt;y返回正数，相等返回0</td>
</tr>
<tr>
<td>compile(string)</td>
<td>编译字符串为代码</td>
</tr>
<tr>
<td>complex(real,img)</td>
<td>创建复数 </td>
</tr>
<tr>
<td>delattr(obj, attr)</td>
<td>等同于del obj.attr</td>
</tr>
<tr>
<td>dict([m])</td>
<td>创建字典</td>
</tr>
<tr>
<td>dir([object])</td>
<td>返回属性名的有序列表。用户可以通过定义__dir__()方法改变此方法的行为</td>
</tr>
<tr>
<td>divmod(a, b)</td>
<td>返回商和余数 </td>
</tr>
<tr>
<td>enumerate(iter)</td>
<td>给定可迭代对象iter,返回新迭代器，迭代元素形式为(index,el)的元组</td>
</tr>
<tr>
<td>eval(expr)</td>
<td>计算表达式的值</td>
</tr>
<tr>
<td>exec(code)</td>
<td>执行指定的代码 </td>
</tr>
<tr>
<td>filter(function, iterable)</td>
<td>在Python2中，创建来自iterable的元素的列表，对这些元素调用function结果为True则包含在结果列表中</td>
</tr>
<tr>
<td>float([x])</td>
<td> 创建浮点数</td>
</tr>
<tr>
<td>format (val, [,fmt_spec]）</td>
<td>格式化字符串</td>
</tr>
<tr>
<td>frozensett[items])</td>
<td>不变集合对象</td>
</tr>
<tr>
<td>getattr(obj, name,default)</td>
<td>返回对象的一个命名属性的信</td>
</tr>
<tr>
<td>globals()</td>
<td>返回代表当前模块全局命名空间的字典</td>
</tr>
<tr>
<td>hasattr(object, name)</td>
<td>如果object具有属性name，则返回True</td>
</tr>
<tr>
<td>hash(object)</td>
<td>返回对象的整数散列值</td>
</tr>
<tr>
<td>hex(x)</td>
<td>根据整数x创建一个十六进制字符串</td>
</tr>
<tr>
<td>id(object)</td>
<td>返回object对象的唯一标识，通常为内存地址</td>
</tr>
<tr>
<td>input([prompt])</td>
<td>在Python 2中，该函数打印一个提示符，读取输入行并通过eval对其进行处理</td>
</tr>
<tr>
<td>int(x [,base])</td>
<td>创建整数</td>
</tr>
<tr>
<td>isinstance(obj, cls)</td>
<td>如果obj是cls或者其子类的实例</td>
</tr>
<tr>
<td>issubclass(class1, class2)</td>
<td>如果class1是class2的子类，或者class1是基于抽象基类class2册的，则返回True</td>
</tr>
<tr>
<td>iter(object [,sentinel])</td>
<td>返回object的迭代器，如果不指定sentinel，则object必须具有__iter__或者__getitem__方法</td>
</tr>
<tr>
<td>len(s)</td>
<td>返回s中包含的项数，s是列表、元组、字符串、集合或字典</td>
</tr>
<tr>
<td>list([items]) </td>
<td>根据可迭代对象items创建列表</td>
</tr>
<tr>
<td>locals()</td>
<td>返回当前函数的本地命名空间构成的字典</td>
</tr>
<tr>
<td>long([x [,base]])</td>
<td>在Python 2中表示长整数的类型。为了可移植性考虑，应当避免直接使用long</td>
</tr>
<tr>
<td>map(function, items, ...)</td>
<td>在Python 2中，该函数将function应用到items的每一项并返回结果列表，Python3则返回迭代器</td>
</tr>
<tr>
<td>max(s [, args, ...])</td>
<td>
<p>如果只有一个参数s,该函数返回s中各项的最大值，s可以是任意可迭代的对象。如果有多个参数，它返回参数中的最大值。min(s [, args, ...])类似</p>
</td>
</tr>
<tr>
<td>next(s [, default])</td>
<td>返回迭代器s中的下一项。如果该迭代器没有下一项，则引发Stopiteration异常（除非指定default）</td>
</tr>
<tr>
<td>object()</td>
<td>Python中所有对象的基类。可以调用它创建一个实例</td>
</tr>
<tr>
<td>oct (x)</td>
<td>将整数转换为一个八进制字符串</td>
</tr>
<tr>
<td>open(file [,mode[,bufsize]])</td>
<td>在Python2中，打开文件返回一个新文件对象</td>
</tr>
<tr>
<td>ord(c)</td>
<td>返回字符c的整数序值。如果是普通字符，返回范围在[0,255]内的值。如果是单个Unicode字符， 通常返回范围在[0,65535]的值</td>
</tr>
<tr>
<td>pow(x, y [, z])</td>
<td>返回x ** y。如果提供了z，则该函数返回(x ** y) % z</td>
</tr>
<tr>
<td>property ( [fget [, fset [,fdel [,doc]]]])</td>
<td>创建类的property属性。get是返回属性值的函数，fset设置属性值，而fdel删除一个属性。doc表示文档字符</td>
</tr>
<tr>
<td>range([start, ] stop [, step])</td>
<td>在Python 2中，该函数创建一个完全填充的、从start到stop的整数列表</td>
</tr>
<tr>
<td>raw_input ([prompt])</td>
<td>Python 2函数，从标准输入读取一行输入并将其作为字符串返回</td>
</tr>
<tr>
<td>repr(object)</td>
<td>返回的字符串表示形式。在大多数情况下，返回的字符串是可以传递到eval()的表达式</td>
</tr>
<tr>
<td>reversed (s)</td>
<td>创建序列s的逆序迭代器。只有当s实现了序列方法__len__()、__getitem()__才可用</td>
</tr>
<tr>
<td>round(x [, n])</td>
<td>将浮点数舍五入到最近的10的负n次幂倍数后再四舍五入 </td>
</tr>
<tr>
<td>set([items])</td>
<td>创建一个使用从可迭代对象items得到的各项来填充的桌合</td>
</tr>
<tr>
<td>setattr(object, name, value)</td>
<td>设置对象的属性。name是字符串。与object.name = value相同</td>
</tr>
<tr>
<td>slice([start,] stop [, step])</td>
<td>返回表示指定范围内整数的切片对象。等同于扩展切片语法 [ i: j: k]</td>
</tr>
<tr>
<td>staticmethod (func)</td>
<td>创建在类中使用的静态方法。通过@staticmethod装饰器隐式调用该函数</td>
</tr>
<tr>
<td>str([object])</td>
<td>表示字符串的类型。在Python 2中，一个字符串包含8位字符</td>
</tr>
<tr>
<td>sum(items,[, initial])</td>
<td>计算从可迭代对象items中得到的所有项的总数。initial是初始值，默认是0</td>
</tr>
<tr>
<td>super(type [,object])</td>
<td>返回表示type基类的对象。该对象的主要用途是调用基类中的方法</td>
</tr>
<tr>
<td>tuple([items])</td>
<td>表示元组的类型。如果提供了items,则它是用于填充该元组的可迭代对象</td>
</tr>
<tr>
<td>type(object)</td>
<td>返回对象的类型</td>
</tr>
<tr>
<td>type (name, bases, dict)</td>
<td>创建一个新type对象（相当于定义一个新类)</td>
</tr>
<tr>
<td>unichr (x)</td>
<td>将整数或长整数换为一个Unicode字符</td>
</tr>
<tr>
<td>vars([object])</td>
<td>返回object的符号表（通常在它的__dict__属性中)</td>
</tr>
<tr>
<td>xrange([start,] stop [, step])</td>
<td>表示从start到stop的整数值范围的类型，该范围不包括start和stop。step是可选的步进值</td>
</tr>
<tr>
<td>zip([s1 [, s2[,..]]])</td>
<td>在Python 2中，返回一些元组的列表，其中第n个元组是(sl[n], s2[n],…）。生成的列表被截取为最短参数序列的长度。如果没有给定参数，则返回一个空列表</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">方法</span></div>
<p>所谓方法是指<span style="background-color: #c0c0c0;">在类定义中定义的函数</span>。包含实例方法、类方法、静态方法三种：</p>
<pre class="crayon-plain-tag">class Obj(object):
    def instance_method(self,arg):  #实例会作为第一个参数传入
        pass
    @classmethod
    def class_method(cls,arg):      #类对象本身会作为第一个参数传入
        pass
    @staticmethod
    def static_method(arg):
        pass


#方法的查找
o = Obj()
method = o.instance_method          #绑定方法，绑定了o对象
method(100)                         #o作为隐含的第一个参数传入
method = Obj.instance_method        #非绑定方法，没有绑定对象
method(o,100)                       #需要手工传入作为self的对象</pre>
<div class="blog_h3"><span class="graybg">生成器（Generator）</span></div>
<p>如果一个函数里面具有yield关键字，则称为生成器。调用生成器得到返回值是一个迭代器对象，这一调用本身不会执行生成器的任何代码。</p>
<p>如果生成器中存在return语句，则执行到return时<span style="color: #222222;">抛出StopIteration并终止迭代。</span></p>
<pre class="crayon-plain-tag">#斐波那契數列的生成器
def fab(max): 
    n, a, b = 0, 0, 1 
    while n &lt; max:
        #在执行时，每次遇到yield就会返回当前迭代值，并且中断执行 
        yield b  
        #下一次调用next()，在上一次中断的下一行执行，上下文与之前一致
        a, b = b, a + b 
        n = n + 1

#调用，获取生成器实例
#虽然看起来与函数调用语法一致，但是不会执行任何函数代码
#直到执行其next()方法时，才会真正调用函数代码
for n in fab(5):  #隐含调用next()
    print n 
#手工调用next()方法
iter = fab(5)
iter.next()

#判断一个函数是否为生成器
from inspect import isgeneratorfunction 
isgeneratorfunction(fab)</pre>
<div class="blog_h3"><span class="graybg">协程</span></div>
<p>上面的生成器，实际上是一种协程，协程与普通函数（Subroutine/function）的执行方式有很大的不同：</p>
<ol>
<li>子例程的起始处是惟一的入口点，一旦退出即完成了子例程的执行，子例程的一个实例只会返回一次</li>
<li>协程可以通过yield来调用其它协程。通过yield方式转移执行权的协程之间不是调用者与被调用者的关系，而是一种对等关系</li>
<li>协程的起始处是第一个入口点，协程返回点之后（yield之后一句）是接下来的入口点</li>
<li>子例程的生命期遵循后进先出（最后一个被调用的子例程最先返回），而协程的生命周期完全由他们的使用的需要决定</li>
</ol>
<p>下面是一个使用协程的例子：两个吃货到餐馆用餐，每次上完菜后就马上被他们吃掉，并继续索要新的食物，一个厨师、一个侍者为他俩服务：</p>
<pre class="crayon-plain-tag">from random import randint
from time import sleep
# 贪吃鬼：
def Gourmand( name ):
    dish = []
    while True:
        if len( dish ) == 0:
            logging.debug( '%s is so hungry, food, quick!' , name )
            # 递上盘子，等待服务员给予食物
            dish.extend( ( yield ) )
        else:
            logging.debug( '%s ate: %s', name, dish.pop() )
# 大厨
all_food = ['Wine', 'Meat', 'Beer', 'Cheese']
def Cook():
    while True:
        new_food = []
        logging.debug( 'Preparing food, please wait...' )
        time = randint( 5, 30 )
        sleep( time )
        for i in range( randint( 1, len( all_food ) - 1 ) ):
            new_food.append( all_food[randint( 0, len( all_food ) - 1 )] )
        # 返回本次制作的食物
        logging.debug( '%d new food finished in %d secs', len( new_food ), time )
        yield new_food
# 服务员        
if __name__ == '__main__':
    c = Cook()
    alex = Gourmand( 'Alex' )
    meng = Gourmand( 'Meng' )
    alex.next()
    meng.next()
    while True:
        # 新菜刚上给这两吃货，他们就会马上将其吃光，并继续索要
        alex.send( c.next() )
        meng.send( c.next() )</pre>
<div class="blog_h2"><span class="graybg">类与对象</span></div>
<div class="blog_h3"><span class="graybg">新式类和旧式类</span></div>
<p>所谓新式类是指<span style="background-color: #c0c0c0;">从object或者其它新式类衍生</span>出的类，而旧式类是Python2中没有明确指定基类的类。Python3中只有新式类。</p>
<p>Python中所有变量皆为对象。使用 __func__这样形式的函数名来实现特殊方法（例如list的__add__实现了+运算符）。</p>
<pre class="crayon-plain-tag">class Stack(object):              #继承语法：继承object，所有Python类的根类
    fast_mode = True              #类变量
    def __init__(self):           #特殊方法：用于在创建对象后进行初始化
        self.stack =[]            #实例变量
    def push(self,object):        #每个实例方法的第一个参数均执行对象本身，即self
        self.stack.append(object) #涉及对象属性的操作，均必须显式使用self变量
    def pop(self):
        return self.stack.pop() 
    def length(self):
        return len(self.stack)
 
    @staticmethod                 #装饰器：定义静态方法，静态方法仅仅是定义在类的命名空间
    def create():
        pass
    
    @classmethod                  #装饰器：定义类方法，类方法对类对象本身进行操作，第一个参数cls为当前类
    def create(cls): 
        return cls()              #可以调用cls来创建合适类型的对象，静态方法做不到这一点
    
    class Inner:                  #Python支持内部类
        pass
#使用类创建对象
s = Stack()
del s         #删除对象

#类似于C++的函数对象：定义__call__方法
class FuncObj:
    def __call__(arg0):
        pass

fo = FuncObj()
fo(1)</pre>
<div class="blog_h3"><span class="graybg">类对象的属性</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;"> 属性</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>__doc__</td>
<td>文档字符串</td>
</tr>
<tr>
<td>__name__</td>
<td>类的名称</td>
</tr>
<tr>
<td>__bases__</td>
<td>基类的元组</td>
</tr>
<tr>
<td>__dict__</td>
<td>保存类方法、变量的字典</td>
</tr>
<tr>
<td>__module__</td>
<td>定义类的模块名称</td>
</tr>
<tr>
<td>__abstractmethods__</td>
<td>抽象方法的集合</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">影响对象行为的特殊方法</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 250px; text-align: center;"> 属性</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>__new__(cls [,*args [,**kwargs]])</td>
<td>类方法，用于创建实例</td>
</tr>
<tr>
<td>__init__(self [,*args [,**kwargs]])</td>
<td>初始化对象属性，在创建对象后立即被调用</td>
</tr>
<tr>
<td>__del__(self)</td>
<td>销毁实例时调用</td>
</tr>
<tr>
<td>__format__(self, format_spec)</td>
<td>格式化后的表示</td>
</tr>
<tr>
<td>__repr__(self)</td>
<td>字符串表示，某些类允许使用<pre class="crayon-plain-tag">eval(repr(o))</pre> 创建对象</td>
</tr>
<tr>
<td>__str__(self)</td>
<td>
<p>返回对象的字符串表示，该方法在<span style="background-color: #c0c0c0;">Python2中返回的是“字节”</span>，在Python3中返回的是字符</p>
<p>在Python2中，print语句和str()函数会调用此方法</p>
</td>
</tr>
<tr>
<td>__unicode__(self)</td>
<td>
<p>返回对象的字符串表示，该方法返回的是“字符”。在Python2中，为了编码相关的兼容性，你应当把对象格式化代码放在该方法里，而把__str__创建为一个存根方法：</p>
<pre class="crayon-plain-tag">def __str__(self):
    return unicode(self).encode('utf-8')</pre>
<p>在Python2中，unicode()函数会调用此方法</p>
<p>在Python3中，此方法没有价值</p>
</td>
</tr>
<tr>
<td>__bool__(self)</td>
<td>真值测试</td>
</tr>
<tr>
<td>__hash__(self)</td>
<td>计算整数散列值</td>
</tr>
<tr>
<td>__lt__</td>
<td>
<p>小于，类似还有__le__、__gt__、__ge__、__eq__、__ne__<br />如果要用作字典键，或者根据==比较大小，则必须实现__eq__</p>
</td>
</tr>
<tr>
<td>__instancecheck__(cls,object)</td>
<td>修改isinstance(object,cls)的行为</td>
</tr>
<tr>
<td>__subclasscheck__(cls,sub)</td>
<td>修改issubclass(sub,cls)的行为</td>
</tr>
<tr>
<td>__getattribute__(self,name)</td>
<td>返回属性self.name时调用，调用此方法时，Python尚未查找对象的真实属性</td>
</tr>
<tr>
<td>__getattr__(self,name)</td>
<td>仅仅在常规方式找不到属性时调用</td>
</tr>
<tr>
<td>__setattr__(self, name, value)</td>
<td>设置self.name=value时调用</td>
</tr>
<tr>
<td>__delattr__(self, name)</td>
<td>删除self.name时调用</td>
</tr>
<tr>
<td>__len__(self)</td>
<td>返回长度</td>
</tr>
<tr>
<td>__getitem__(self,key)</td>
<td>获得self[key]</td>
</tr>
<tr>
<td>__setitem__(self,key,value)</td>
<td>设置self[key] = value</td>
</tr>
<tr>
<td>__delitem__(self,key)</td>
<td>删除self[key]</td>
</tr>
<tr>
<td>__contains__(self,obj)</td>
<td>如果包含，则返回真</td>
</tr>
<tr>
<td>__iter__()</td>
<td>
<p>如果对象支持迭代，从该方法返回迭代器对象，迭代器必须实现next()方法</p>
<pre class="crayon-plain-tag">it = s.__iter__()
while True :
    try:
        it.next()
    except StopIteration:
        break</pre>
</td>
</tr>
<tr>
<td>__next__()</td>
<td>Python3的迭代器方法，在Python2中为next()</td>
</tr>
<tr>
<td>__add__(self, other)</td>
<td>self + other</td>
</tr>
<tr>
<td>__sub__(self, other)</td>
<td>self - other</td>
</tr>
<tr>
<td>__mul__ (self, other)</td>
<td>self * other</td>
</tr>
<tr>
<td>__div__ (self, other)</td>
<td>self / other</td>
</tr>
<tr>
<td>__floordiv__(self, other)</td>
<td>self // other</td>
</tr>
<tr>
<td>__mod__ (self, other)</td>
<td>self % other</td>
</tr>
<tr>
<td>__pow__(self, other, [,modulo])</td>
<td>self ** other, pow(self,other,modulo)</td>
</tr>
<tr>
<td>__lshift__(self, other)</td>
<td>self &lt;&lt; other</td>
</tr>
<tr>
<td>__rshift__(self, other)</td>
<td>self &gt;&gt; other</td>
</tr>
<tr>
<td>__and__(self, other)</td>
<td>self  &amp; other</td>
</tr>
<tr>
<td>__or__(self, other)</td>
<td>self  | other</td>
</tr>
<tr>
<td>__xor__(self, other)</td>
<td>self  ^ other</td>
</tr>
<tr>
<td>__r**(self, other)</td>
<td>other ** self </td>
</tr>
<tr>
<td>__i**(self, other)</td>
<td>self **= other</td>
</tr>
<tr>
<td>__neg__(self)</td>
<td>-self  </td>
</tr>
<tr>
<td>__pos__(self)</td>
<td>+self</td>
</tr>
<tr>
<td>__abs__(self)</td>
<td>abs(self)</td>
</tr>
<tr>
<td>__invert__(self)</td>
<td>~self</td>
</tr>
<tr>
<td>__int__(self)</td>
<td>int(self)</td>
</tr>
<tr>
<td>__long__(self)</td>
<td>long(self)</td>
</tr>
<tr>
<td>__float__(self)</td>
<td>float(self)</td>
</tr>
<tr>
<td>__complex(self)</td>
<td>complex(self)</td>
</tr>
<tr>
<td>__call__(self[, *args[,*kwargs]])</td>
<td>函数对象</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">对象实例的属性</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;"> 属性</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>__class__</td>
<td>实例所属的类，可以使用tpye(o)得到</td>
</tr>
<tr>
<td>__dict__</td>
<td>所有实例变量构成的字典</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">异常</span></div>
<p>如果一个Python程序出现错误，则会引发异常，打印类似下面的信息：
<pre class="crayon-plain-tag">Traceback (most recent call last)： 
 File "foo.py", line 12, in 
IOError: [Errno 2] No such file or directory: 'file.txt'</pre>
<p>错误信息中包含了错误的类型、出错的代码位置。如果不做任何处理，异常会导致程序终止，除非使用try-except语句：</p>
<pre class="crayon-plain-tag">try:
    f = open('Readme.txt')
except IOError as e:       #如果发生IOError，则被捕获并且存放在e变量中
    print e


#raise用于手工触发异常
raise RuntimeError("Error occurred")
#如果raise没有指定任何参数，则再次引发最近一次生成的异常（仅正在处理前一个异常时）
try:
    pass
except RuntimeError as e:
    raise</pre>
<p>未捕获的异常将向上传递，如果到程序最顶级仍然没有处理，则导致解析器终止并打印消息。可以把未捕获的异常传递给用户自定义的<pre class="crayon-plain-tag">sys.excepthook()</pre> 函数进行处理。</p>
<p>可以使用<span style="background-color: #c0c0c0;">try-except-else</span>语句块，else在没有引发异常时执行；可以使用<span style="background-color: #c0c0c0;">try-except-finally</span>语句块，finally总是执行：</p>
<pre class="crayon-plain-tag">try:
    f = open('Readme.txt','r')
except IOError as e:
    error_log.write('Failed to open file:%s\n' % e)
else:
    data = f.read()
    f.close()
finally: #如果发生异常，控制权首先交给finally代码块，执行完毕后再进行异常处理
    f.close()</pre>
<div class="blog_h3"><span class="graybg">内置异常</span></div>
<p>Python预定义了以下异常：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">异常</td>
<td style="text-align: center;">描述</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<p>BaseException</p>
<p style="padding-left: 30px;">GeneratorExit</p>
<p style="padding-left: 30px;">Keyboardlnterrupt</p>
<p style="padding-left: 30px;">SystemExit</p>
<p style="padding-left: 30px;">Exception</p>
<p style="padding-left: 60px;">StopIteration</p>
<p style="padding-left: 60px;">StandardError</p>
<p style="padding-left: 60px;">ArithmeticError</p>
<p style="padding-left: 90px;">FloatingPointError</p>
<p style="padding-left: 90px;">ZeroDivisionError</p>
<p style="padding-left: 60px;">AssertionError</p>
<p style="padding-left: 60px;">AttributeError</p>
<p style="padding-left: 60px;">EnvironmentError</p>
<p style="padding-left: 90px;">IOError</p>
<p style="padding-left: 90px;">OSError</p>
<p style="padding-left: 60px;">EOFError</p>
<p style="padding-left: 60px;">ImportError</p>
<p style="padding-left: 60px;">LookupError</p>
<p style="padding-left: 90px;">IndexError</p>
<p style="padding-left: 90px;">KeyError</p>
<p style="padding-left: 60px;">MemoryError</p>
<p style="padding-left: 60px;">NameError</p>
<p style="padding-left: 60px;">UnboundLocalError</p>
<p style="padding-left: 60px;">ReferenceError</p>
<p style="padding-left: 60px;">RuntimeError</p>
<p style="padding-left: 60px;">NotImplementedError</p>
<p>SyntaxError</p>
<p style="padding-left: 30px;">IndentationError</p>
<p style="padding-left: 60px;">TabError</p>
<p>SystemError</p>
<p>TypeError</p>
<p>ValueError</p>
<p style="padding-left: 30px;">UnicodeError</p>
<p style="padding-left: 60px;">UnicodeDecodeError</p>
<p style="padding-left: 60px;">UnicodeEncodeError</p>
<p style="padding-left: 60px;">UnicodeTranslateError</p>
</td>
<td>
<p>所有异常的根类</p>
<p>由生成器的<pre class="crayon-plain-tag">close()</pre> 方法引发</p>
<p>由键盘中断（通常为Ctrl+C)生成</p>
<p>程序退出/终止</p>
<p>所有非退出异常的基类</p>
<p>引发后可停止迭代</p>
<p>所有内置异常的基类</p>
<p>算术异常的基类</p>
<p>浮点操作失败</p>
<p>对0进行除或取模操作</p>
<p>由assert语句引发</p>
<p>当属性名称无效时引发</p>
<p>发生在Python外部的错误</p>
<p>I/O或文件相关的错误</p>
<p>操作系统错误</p>
<p>到达文件结尾时引发</p>
<p>import语句失败</p>
<p>索引和键错误</p>
<p>超出序列索引的范围</p>
<p>字典键不存在</p>
<p>内存不足</p>
<p>无法找到局部或全局名称</p>
<p>未綁定的局部变量</p>
<p>销毁被引用对象后使用的弱引用</p>
<p>一般运行时错误</p>
<p>没有实现的特性解析错误</p>
<p>语法错误</p>
<p>缩进错误</p>
<p>使用不一致的制表符（由-tt选项生成)</p>
<p>解释器中的非致命系统错误</p>
<p>给操作传递了错误的类型</p>
<p>无效类型</p>
<p>Unicode错误</p>
<p>Unicoed解码错误</p>
<p>Unicode编码错误</p>
<p>Unicode转换错误</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">自定义异常</span></div>
<p>可以创建以Exception为父类的新类，作为自定义异常类：</p>
<pre class="crayon-plain-tag">#简单的例子
class NetworkError(Exception)： pass
#使用该异常
raise NetworkError('Cannot find host')

#自定义异常可以包含多个构造参数
class DeviceError(Exception):
    def __init__(self, errno, msg):
        self.args = (errno, msg)
#使用该异常
raise DeviceError(1,'Not Responding')</pre>
<div class="blog_h2"><span class="graybg">断言与__debug__</span></div>
<p>assert语句用于在程序中设置断言，格式为：</p>
<pre class="crayon-plain-tag">assert test [,msg]
#test为一表达式，如果为False，则触发AssertionError，并使用msg指定的消息内容</pre>
<p> 使用-O选项使解释器运行于最优模式时，不会执行断言代码。</p>
<p>除非使用-O选项，内置只读变量<pre class="crayon-plain-tag">__debug__</pre> 的值均为true，可以用于程序调试。</p>
<div class="blog_h2"><span class="graybg">上下文管理协议</span></div>
<p>该机制主要用于在Python中安全的进行资源（数据库连接、事务、文件句柄等）管理。</p>
<p>使用with语句，可以在一个“上下文管理器”对象的控制下执行一系列的语句：</p>
<pre class="crayon-plain-tag">with context [as var]:   #执行context.__enter__(self)方法，返回值存入var
    pass
#执行context.__exit__(self, type, value, traceback)方法

#举例：锁
import threading
lock = threading.Lock()
with lock:
    pass
#执行完毕后自动清除锁定

#自动关闭打开的文件
with open( sys.argv[1] ) as infile :
    for line in infile:
        print line</pre>
<p>上下文管理器对象必须实现：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 35%; text-align: center;"><span style="color: #333333; font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: small;"> 方法</span></td>
<td style="text-align: center;"><span style="color: #333333; font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: small;">说明 </span></td>
</tr>
</thead>
<tbody>
<tr>
<td>__enter__(self)</td>
<td>进行一个新上下文时调用此方法，返回值存入 as 后面指定的变量 </td>
</tr>
<tr>
<td>__exit__(self, type, value, tb)</td>
<td>离开一个上下文是调用此方法，如果发生异常，则type、value、tb分别为异常类型、值、跟踪信息</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">模块</span></div>
<p>随着程序规模的扩大，有必要根据功能不同把代码分散在不同的文件中，作为单独的模块，并在需要使用时进行导入。</p>
<p>在Python中，<span style="background-color: #c0c0c0;">模块名就是相应脚本文件的basename</span>。当导入一个模块时，自动创建一个名字空间来存放模块定义的对象，默认此名字空间与模块名相同：</p>
<pre class="crayon-plain-tag">#file:  div.py
def divide(a, b):
    q = a/b
    r = a - q*b
    return (q, r)


#file:  main.py
import div                #导入div模块，创建了div名字空间
dir(div)                  #列出名字空间下的内容
q, r= div.divide(100,17)  #使用模块中的函数，需要加前缀
import div as d           #为导入的模块启用别名

from div import divide    #把某个具体的定义导入到当前名字空间
q, r = divide(100,18)     #不再需要使用名字空间作为前缀</pre>
<div class="blog_h3"><span class="graybg">模块的属性</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;"> 属性</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>__doc__</td>
<td>模块的文档字符串</td>
</tr>
<tr>
<td>__dict__</td>
<td>与模块相关的字典</td>
</tr>
<tr>
<td>__name__</td>
<td>模块的名称</td>
</tr>
<tr>
<td>__file__</td>
<td>用户加载模块的文件</td>
</tr>
<tr>
<td>__path__</td>
<td>完全限定的包名</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">基础运算和表达式</span></div>
<div class="blog_h2"><span class="graybg">运算符优先级（从高到低）</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">运算符</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>(...) 、 [...] 、 {...}</td>
<td>创建元组、列表或字典</td>
</tr>
<tr>
<td>s[i]、s[i:j]</td>
<td>索引、切片</td>
</tr>
<tr>
<td>s.attr</td>
<td>属性导航符</td>
</tr>
<tr>
<td>f(...)</td>
<td>函数调用</td>
</tr>
<tr>
<td>+x、-x、~x</td>
<td>一元运算符</td>
</tr>
<tr>
<td>x**y</td>
<td>乘方（右结合性）</td>
</tr>
<tr>
<td>x*y、x/y、x//y、x%y</td>
<td>乘、除、截断除、取余</td>
</tr>
<tr>
<td>x+y、x-y</td>
<td>加、减</td>
</tr>
<tr>
<td>x&lt;&lt;y、x&gt;&gt;y</td>
<td>移位</td>
</tr>
<tr>
<td>x&amp;y</td>
<td>按位与</td>
</tr>
<tr>
<td>x^y</td>
<td>按位异或</td>
</tr>
<tr>
<td>x|y</td>
<td>按位或</td>
</tr>
<tr>
<td>x &lt; y、x &lt;= y、<br />x &gt; y、 x &gt;= y、 <br />x == y、 x != y <br />x is y、 x is not y 、<br />x in s、x not in s</td>
<td>
<p>比较、序列成员检查</p>
<p>is    用于对象同一性检查<br />==  则用于值相等性检查</p>
</td>
</tr>
<tr>
<td>not x</td>
<td>逻辑非</td>
</tr>
<tr>
<td>x and y</td>
<td>逻辑与</td>
</tr>
<tr>
<td>x or y</td>
<td>逻辑或</td>
</tr>
<tr>
<td>lambda args:expr</td>
<td>lambda表达式</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">数学运算</span></div>
<p>在其它语言里面比较少见的数学运算符有：截断除法(//)、乘方(**)。</p>
<p>内置函数包括：绝对值(abs)、商与余数(divmod)、四舍五入(round)等。</p>
<div class="blog_h2"><span class="graybg">序列操作</span></div>
<p>序列包括：字符串、列表和元组。支持以下运算符或者函数：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 250px; text-align: center;">运算符或函数 </td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>s + r</td>
<td>连接两个相同类型的序列</td>
</tr>
<tr>
<td>s * n</td>
<td>生成s的n个副本，浅复制</td>
</tr>
<tr>
<td>v1,v2... = s</td>
<td>把序列解包为若干对象，变量个数必须和元素个数一致</td>
</tr>
<tr>
<td>s[i]</td>
<td>索引，返回第i+1个元素</td>
</tr>
<tr>
<td>s[i:j]</td>
<td>切片</td>
</tr>
<tr>
<td>s[i:j:stride]</td>
<td>扩展切片，stride为步进值，可以跳过若干对象，结果索引为i, i+stride, i + 2*stride直到j</td>
</tr>
<tr>
<td>x in s   以及 x not in s</td>
<td>从属关系判断</td>
</tr>
<tr>
<td>for x in s</td>
<td>迭代</td>
</tr>
<tr>
<td>all(s)</td>
<td>是否所有元素均为True，不适用于字符串</td>
</tr>
<tr>
<td>any(s)</td>
<td>是否存在元素为True，不适用于字符串</td>
</tr>
<tr>
<td>len(s)</td>
<td>长度</td>
</tr>
<tr>
<td>min(s)  max(s)</td>
<td>最值</td>
</tr>
<tr>
<td>sum(s[,initial]) </td>
<td>求和</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><strong>以下为可变序列（即列表）支持的操作</strong></td>
</tr>
<tr>
<td>s[i] = x</td>
<td>按索引赋值，如果i为负数，则从结尾算起</td>
</tr>
<tr>
<td>s[i:j] = x</td>
<td>按切片赋值，x的个数必须与切片中元素个数一致</td>
</tr>
<tr>
<td>s[i:j:stride] = x</td>
<td>扩展切片赋值，x的个数必须与切片中元素个数一致</td>
</tr>
<tr>
<td>del s[i]</td>
<td>删除一个元素</td>
</tr>
<tr>
<td>del s[i:j]</td>
<td>删除一个切片</td>
</tr>
<tr>
<td>del [i:j:stride]</td>
<td>删除一个扩展切片</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">字典操作</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 25%; text-align: center;">操作 </td>
<td style="text-align: center;">描述 </td>
</tr>
</thead>
<tbody>
<tr>
<td>x = d[k]</td>
<td>通过键进行查找</td>
</tr>
<tr>
<td>d[k] = x</td>
<td>通过键进行赋值</td>
</tr>
<tr>
<td>del d[k]</td>
<td>通过键进行删除</td>
</tr>
<tr>
<td>k in d</td>
<td>测试某个键是否存在于字典中</td>
</tr>
<tr>
<td>len(d)</td>
<td>字典的条目个数</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">集合操作</span></div>
<p>set和frozenset用于支持常见的集合操作：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 25%; text-align: center;"> 操作</td>
<td style="text-align: center;">描述 </td>
</tr>
</thead>
<tbody>
<tr>
<td>s | t</td>
<td>并集</td>
</tr>
<tr>
<td>s &amp; t</td>
<td>交集</td>
</tr>
<tr>
<td>s - t</td>
<td>差集</td>
</tr>
<tr>
<td>s ^ t</td>
<td>对称差集</td>
</tr>
<tr>
<td>len(s)</td>
<td>集合中条目个数</td>
</tr>
<tr>
<td>max(s)</td>
<td>最大值</td>
</tr>
<tr>
<td>min(s)</td>
<td>最小值</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">类型转换</span></div>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 25%; text-align: center;"> 转换函数</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>int(x [, base])</td>
<td>将x换为一个整数。如果x是一个字符串，base用于指定基数 </td>
</tr>
<tr>
<td>float(x)</td>
<td>将x换为一个浮点数</td>
</tr>
<tr>
<td>Complex (real [, imag])</td>
<td>创建一个复数</td>
</tr>
<tr>
<td>str(x)</td>
<td>将对象x转换为字符串表示</td>
</tr>
<tr>
<td>repr(x)</td>
<td>将对象x转换为一个表达式字符串，可以通过eval还原</td>
</tr>
<tr>
<td>format (x [, fmt_spec])</td>
<td>将对象x转换为格式化字符串，该函数调用x的__format__()方法</td>
</tr>
<tr>
<td>eval(str)</td>
<td>对字符串求值并返回对象</td>
</tr>
<tr>
<td>tuple(s)  </td>
<td>将s转换为元组</td>
</tr>
<tr>
<td>list(s)</td>
<td>将s转换为列表</td>
</tr>
<tr>
<td>set(s)</td>
<td>将s转换为集合</td>
</tr>
<tr>
<td>dict(d)</td>
<td>将d转换为字典，d是(key,value)形式的序列对象</td>
</tr>
<tr>
<td>frozenset(s)</td>
<td>将s转换为不可变集合</td>
</tr>
<tr>
<td>chr(x)</td>
<td>将整数转换为字符</td>
</tr>
<tr>
<td>unichr(x)</td>
<td>将整数转换为Unicode字符</td>
</tr>
<tr>
<td>ord (x)</td>
<td>将字符转换为其整数值</td>
</tr>
<tr>
<td>hex(x)</td>
<td>将整数转换为十六进制字符串 </td>
</tr>
<tr>
<td>bin (x)</td>
<td>将整数转换为二进制字符串</td>
</tr>
<tr>
<td>oct (x)</td>
<td>将整数转换为八进制字符串</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg"> 函数与函数编程</span></div>
<div class="blog_h2"><span class="graybg">变量作用域</span></div>
<ol>
<li>每次执行函数时，<span style="background-color: #c0c0c0;">自动创建局部命名空间</span>，其内包括<span style="background-color: #c0c0c0;">函数参数、函数体内定义的变量</span></li>
<li>解释器解析变量名称时，<span style="background-color: #c0c0c0;">首先从局部命名空间</span>开始；如果找不到，则搜索函数的<span style="background-color: #c0c0c0;">全局命名空间</span>（定义该函数的模块）；仍然找不到，则搜索<span style="background-color: #c0c0c0;">内置命名空间</span>；再找不到则NameError</li>
<li>除非在函数里使用<span style="background-color: #c0c0c0;">global</span>语句，否则不会<span style="background-color: #c0c0c0;">改变全局</span>命名空间变量的值，但是可以访问全局变量的值<br />
<pre class="crayon-plain-tag">i = 1
def func():
    print i   #打印1
    i = 2
    print i   #打印2
func()
print i       #仍然打印1

def func1():
    global i  #现在i位于全局名字空间
    i - 2     #修改成功</pre>
</li>
<li>Python支持嵌套函数定义，使用词法作用域来绑定嵌套函数中的变量——首先检查其<span style="background-color: #c0c0c0;">局部作用域</span>，然后检查<span style="background-color: #c0c0c0;">外部嵌套函数的作用域</span>，以此类推。在Python2中，只能对局部作用域、全局作用域进行变量赋值，<span style="background-color: #c0c0c0;">对外部嵌套函数中定义的变量进行赋值是不支持的，Python3可以使用nonlocal语句解决此问题</span></li>
<li>使用尚未赋值的局部变量，导致UnboundLocalError</li>
</ol>
<div class="blog_h2"><span class="graybg">函数对象与闭包</span></div>
<p>作为First-class对象的函数，可以当作数据传递给其他函数。把函数作为数据来处理时，它自动携带定义函数的上下文信息（变量）。</p>
<p><span style="background-color: #c0c0c0;">将函数主体语句、语句的执行环境上下文信息一起打包时，得到的对象称为闭包</span>。由于任意函数都携带定义其它的模块的全局命名空间信息（__globals__），因此本质上任何Python函数都是闭包。</p>
<div class="blog_h2"><span class="graybg">装饰器</span></div>
<p>装饰器是一个函数，用于包装一个函数或者类，目的是修改、增强被包装对象的功能。特殊符号@用于表示装饰器语法：</p>
<pre class="crayon-plain-tag">@trace
def square (x) : 
    return x*x
#上述代码等价于
def square (x) : 
    return x*x
square = trace(square)

#trace函数的定义——类似于切面，它必然返回一个函数
def trace(func)：
    def callf(*args,**kwargs):
        debug_log.write( "Calling %s： %s, %s\n" % (func.__name__, args, kwargs) )  #记录日志
        r = func(*args, **kwargs)     #调用原本的函数
        debug_log.write( "%s returned %s\n" % (func.__name__, r) )
    return callf</pre>
<p>可以声明多个装饰器，每个装饰器必须独占一行，<span style="background-color: #c0c0c0;">最后定义的装饰器，最先包装到原始函数</span>上：</p>
<pre class="crayon-plain-tag">@foo
@bar
def func(x):
    pass
#等价于
func = foo(bar(func))</pre>
<p>装饰器可以带有参数：</p>
<pre class="crayon-plain-tag">@eventhandler('BUTTON')
def handle_button(msg):
    pass
#带有参数的装饰器，装饰目标函数的步骤如下：
temp = eventhandler('BUTTON')       #使用参数调用装饰器，其应当仍然返回一个装饰器
handle_button = temp(handle_button) #使用生成的装饰器来装饰目标函数</pre>
<p>装饰器也可以应用于类，这样的<span style="background-color: #c0c0c0;">装饰器应当返回类对象作为结果</span>。 下面是一个例子：</p>
<pre class="crayon-plain-tag">def talking(cls: type):
    setattr(cls, 'talk', lambda self: print('Hello, I am %s' % str(self)))
    return cls


@talking
class person(object):
    def __init__(self, name: str):
        self.name = name

    def __str__(self):
        return self.name.upper()


if __name__ == '__main__':
    p = person('Alex Wong')
    p.talk()  #Hello, I am ALEX WONG </pre>
<div class="blog_h2"><span class="graybg">生成器与yield</span></div>
<p>使用yield关键字可以定义一个生成器对象，该对象本质上是一个函数，其生成一个值序列，在迭代中使用：</p>
<pre class="crayon-plain-tag">def countdown(n):
    while n &gt; 0:
        print('Counting down from %d' % n)
        yield n
        n -= 1
    return   #生成器不能返回除了None以外的任何值

#调用该函数，不会打印任何信息：
c = countdown(10)
#相反，返回值c是一个生成器函数，当c.next()被调用时，countdown函数被执行
#调用next()函数时，代码会正常执行，直到遇到yield语句为止，并返回yield指定的值
#通常不需要手工调用next()函数，而是在for、sum等使用序列的操作中自动调用：
for n in countdown(10):
    pass
a = sum(countdown(10))</pre>
<p>当生成器函数遇到return语句或者StopIteration异常，则其退出。</p>
<p>可选的，调用<pre class="crayon-plain-tag">close()</pre> 方法可以关闭生成器，当yield语句遭遇GeneratorExit异常时会自动调用。</p>
<div class="blog_h2"><span class="graybg">协程与yield</span></div>
<p>yield语句可以作为右值，通过该方式使用yield的函数称为协程：</p>
<pre class="crayon-plain-tag">def receiver():
    print('Prepare to receive')
    while True:
        n = (yield)
        #协程的目的是对发送给它的值做出处理
        print('Got %s' % n)

#使用协程
r = receiver()
r.next()       #执行到第一条yield语句，该调用必不可少
r.send(1)      #发送值给协程，导致其运行直到下一条yield语句
r.send('Greetings')</pre>
<p>由于协程使用时next()调用必不可少，因此可以使用类似下面的装饰器自动完成：</p>
<pre class="crayon-plain-tag">def coroutine(func):
    def start(*args,**kwargs)：
        g = func(*args,**kwargs) 
        g.next() 
        return g 
    return start</pre>
<p>协程一般是无限期运行的，可以调用close()方法显式关闭。关闭后，再调用send()会导致StopIteration异常。注意close方法会在协程内部引发GeneratorExit异常。</p>
<p>使用throw()方法可以在协程内部触发异常，该异常在yield语句处出现。使用throw()给协程发送异步信号是不安全的。</p>
<p>如果协程的yield提供值，那么将自动返回给send()方法的返回值：</p>
<pre class="crayon-plain-tag">def line_splitter(d = None):
    result = None
    while True:
        #读出line，返回result
        line = (yield result)
        result = line.split(d)

ls = line_splitter()
ls.next()                 #执行到第一次遇到yield
ls.send('X,Y,Z')          #触发继续执行，直到下一次遇到yield。因此返回['X','Y','Z']</pre>
<p>当通过使用yield返回值、throw()时需要注意，send()给协程的值将作为throw()的返回值返回。</p>
<div class="blog_h2"><span class="graybg">生成器与协程的使用场景</span></div>
<ol>
<li>数据流处理程序，类似于Unix Shell管道</li>
<li>实现某种形式的并发，例如使用某个任务管理器，把数据分发给数百个执行各种具体任务的协程</li>
</ol>
<div class="blog_h2"><span class="graybg">列表包含</span></div>
<p>Python使用“列表推导”（List comprehension）运算符，来把函数应用到列表的每个项，并根据结果创建新的列表，例如下面的操作：</p>
<pre class="crayon-plain-tag">nums = [1, 2, 3, 4, 5]
squares = []
for n in nums:
    squares.append(n * n);</pre>
<p>可以使用列表推导改写为：</p>
<pre class="crayon-plain-tag">nums = [1, 2, 3, 4, 5]
squares = [n * n for n in nums]  #列表推导</pre>
<p>列表推导的语法如下：</p>
<pre class="crayon-plain-tag">[expression for iteml in iterablel if conditionl
            for item2 in iterable2 if condition2
            ……
            for itemN in iterableN if conditionN ]

#上面的语法等价于
s = []
for item1 in iterable1:
    if condition1:
        for item2 in iterable1:  
            if condition2:
            ...
                for itemN in iterableN:  
                    if conditionN: s.append(expression)  #最终是为了计算结果列表

#举例
a = [-3,5,2,-10,7,8] 
b = 'abc'
c = [2*s for s in a]             #[-6,10,4,-20,14,16]
d = [s for s in a if s &gt;= 0]     #[5,2,7,8]
e = [(x,y) for x in a            #如果推导结果元素为元组，必须放入括号内
           for y in b            #[(5,'a'),(5,'b'),(5,'c')......]
           if x &gt; 0 ]
f = [(1,2),(3,4),(5,6)]
g = [x+y for x,y in f]           #[3,7,11]</pre>
<p>注意：Python2的列表推导迭代变量在当前作用域中求值，在列表推导完毕后，其值仍然保留。在Python3中，迭代变量是私有的，推导结束即无效。</p>
<div class="blog_h2"><span class="graybg">生成器表达式</span></div>
<p>生成器表达式与列表包含非常类似，但是它只是生成获取结果的规则，而不是生成结果本身：</p>
<pre class="crayon-plain-tag">#语法差异仅仅是把方括号换成花括号
(expression for iteml in iterablel if conditionl
            for item2 in iterable2 if condition2
            ……
            for itemN in iterableN if conditionN )

#举例
a = (1, 2, 3, 4)
b = (10*i for i in a)   #生成一个生成器
b.next()                #10

#list函数可以把生成器表达式转换为序列
list(b)</pre>
<div class="blog_h2"><span class="graybg">lambda表达式</span></div>
<p>使用lambda语句可以创建匿名函数：</p>
<pre class="crayon-plain-tag">lambda args: expression
#args为逗号分隔的参数列表
#expression为使用参数的表达式
a = lambda x,y : x + y
r = a(1,2)   #r=3
#lambda主要用于指定短小的回调函数
names.sort(key = lambda n: n.lower())</pre>
<p>lambda语句中不能出现多条语句或者非表达式语句（for、while等）。作用域规则与函数相同</p>
<div class="blog_h2"><span class="graybg">递归</span></div>
<p>sys.getrecursionlimit ()返回当前解释器对递归深度的限制，默认1000。</p>
<p>递归不能用在生成器函数、协程中。</p>
<div class="blog_h2"><span class="graybg">函数属性</span></div>
<p>可以为函数指定任意属性，这些属性会包含在__dict__属性中。</p>
<div class="blog_h2"><span class="graybg">eval()、exec()、compile()</span></div>
<pre class="crayon-plain-tag">#eval执行一个表达式字符串并返回结果
eval (str [, globals [, locals]])
#exec执行任意包含Python代码的字符串
exec (str [, globals [, locals]])</pre>
<p>eval、exec均在<span style="background-color: #c0c0c0;">调用者的变量作用域</span>中执行，可选的globals、locals用于映射全局、局部名字空间。</p>
<p>对于反复执行的代码，最好使用compile将其编译为字节码，提高性能：</p>
<pre class="crayon-plain-tag">s = "for i in range(0,10): print(i)"
c = compile(s, '', 'exec')  #编译为代码对象
exec(c)</pre>
<div class="blog_h1"><span class="graybg">类与面向对象编程</span></div>
<ol>
<li>Python没有类作用域一说，如果需要<span style="background-color: #c0c0c0;">访问对象的其它属性，必须以self.开头</span></li>
<li>派生类不会自动调用基类的__init__方法</li>
<li><pre class="crayon-plain-tag">super(cls, instance).***</pre> 用于在基类上执行属性查找，Python3直接简化为<pre class="crayon-plain-tag">super().***</pre><br />
<pre class="crayon-plain-tag">class MoreEvilAccount(EvilAccount):
    def deposit(self, amount):
        self.withdraw(5)
        super(MoreEvilAccount,self).deposit(amount) #调用父类的方法实现</pre>
</li>
<li>Python支持多重继承，示例：<br />
<pre class="crayon-plain-tag">#使用逗号分隔的基类列表
class MostEvilAccount(EvilAccount, DepositCharge, WithdrawCharge)：pass</pre>
</li>
<li>通常应当避免使用多重继承。有时，可以使用多重继承来定义混入类（mixin）。<span style="background-color: #c0c0c0;">混入类定义了需要混合到其它类中的一组方法</span>，以添加功能，<span style="background-color: #c0c0c0;">它通常假定其它方法/属性存在</span>，并以这些方法/属性为基础构建新的逻辑</li>
<li>动态绑定（多态性）：不考虑实例的类型的情况下使用实例，只要以obj.attr的形式访问属性，解释器就会按<span style="background-color: #c0c0c0;">实例本身内部 - 实例的类定义 - 基类的类定义</span>的顺序来搜索attr，返回第一个匹配。此绑定过程的关键在于它与obj的类型独立，只要它就有attr属性，就可成功绑定（鸭子类型识别）。动态绑定可以用于组件解耦，例如，可以针对具有<span style="background-color: #c0c0c0;">某个方法集的对象编写代码，而不需要考虑其属于何种类型</span>。</li>
<li>静态方法是普通的函数，只是定义在类的名字空间中；类方法的第一个参数是当前类本身；实例方法的第一个参数是当前对象本身</li>
</ol>
<div class="blog_h2"><span class="graybg">属性（property）</span></div>
<p>property是一种特殊的属性，在访问时，会计算它的值：</p>
<pre class="crayon-plain-tag">class Circle(object):
    def __init__(self,radius):
        self.radius = radius
    #属性定义
    @property  #该装饰器支持简单属性风格访问后续定义的方法
    def area(self):
        return math.pi * self.radius**2

#使用
c = Circle(5)
c.area    #访问属性，导致area()方法被调用</pre>
<p>向property添加setter、deleter方法，可以实现设置、删除属性操作：</p>
<pre class="crayon-plain-tag">class Foo(object):
    def __init__(self,name):
        self.__name = name 
    @property                        #属性读取
    def name(self):
        return self.__name           #实际存储的变量名任意，但是必须和property名不同
    @name.setter                     #属性设置
    def name(self,value):            #setter、deleter的方法名称必须与属性原始方法名一致
        if not isinstance(value,str):
            raise TypeError("string value required.")
            self.__name = value 
    @name.deleter                    #属性删除
    def name(self):
        raise TypeError("Unsupported operation")

#使用
f = Foo()
name = f.name  #调用f.name()
f.name = 50    #调用f.name(f,50)，抛出TypeError
del f.name     #调用f.name(f)，抛出TypeError</pre>
<p>另外，getter、setter、deleter风格的方法也是支持的，但是建议使用装饰器，更加简洁，不会显示大量的getter、setter等方法：</p>
<pre class="crayon-plain-tag">class Foo(object):
    def getname(self):
        return self.__name
    def setname(self,name):
        self.__name = name
    def delname(self):
        raise TypeError("Unsupported operation")</pre>
<div class="blog_h2"><span class="graybg">数据封装与私有属性</span></div>
<p>Python约定以__开头的方法为私有方法，这些方法会自动变形：<pre class="crayon-plain-tag">__func</pre> 变形为<pre class="crayon-plain-tag">__classname__func</pre> 的形式。<br /> 这不是一种严格的信息隐藏机制。</p>
<div class="blog_h2"><span class="graybg">对象内存管理</span></div>
<p>Python使用引用计数作为基础垃圾回收算法。每个对象都具有一个引用计数，将对象赋给一个新变量、将其放入容器时，均会导致该计数增加，使用<pre class="crayon-plain-tag">del</pre> 语句或者<span style="background-color: #c0c0c0;">超过变量作用范围</span>、<span style="background-color: #c0c0c0;">重新变量赋值</span>时，引用计数则减小</p>
<p>引用计数算法具有<span style="background-color: #c0c0c0;">无法处理循环引用</span>的天生缺陷，为此Python解释器会定期执行一个“循环检测器”，搜索不可访问的对象循环引用，并删除之。</p>
<p>Python类实例的创建包括两个步骤：</p>
<ol>
<li>使用特殊的<pre class="crayon-plain-tag">__new__()</pre> 方法创建实例</li>
<li>使用<pre class="crayon-plain-tag">__init__()</pre> 方法初始化实例</li>
</ol>
<p>其中类方法__new__方法很少需要用户定义，除了以下两个场景：</p>
<ol>
<li>在继承一个不变类型的基类时，用于修改对象的值</li>
<li>定义元类时使用</li>
</ol>
<p>实例创建完毕后，Python将管理其引用计数，<span style="background-color: #c0c0c0;">当引用计数为0时，实例立即被销毁</span>，其<pre class="crayon-plain-tag">__del__()</pre> 方法被调用。通常没必要定义__del__，因为无法保证解释器在退出时调用了该方法，如果需要资源清理，自定义close()方法并手工调用是最好的。</p>
<p><span style="background-color: #c0c0c0;">定义了__del__() 的对象无法被Python的循环垃圾收集器回收</span>。可以使用<pre class="crayon-plain-tag">weakref.ref()</pre> 引用解决此问题——在<span style="background-color: #c0c0c0;">不增加引用计数的情况下创建对象的引用</span>。</p>
<div class="blog_h2"><span class="graybg">__slots__</span></div>
<p>通过定义特殊变量<pre class="crayon-plain-tag">__slots__</pre> ，类可以限制设置合法属性名称：</p>
<pre class="crayon-plain-tag">class Account(object):
    #定义该变量后，只能设置列出的属性，否则抛出AttributeError
    __slots__ = ('name','balance')</pre>
<p>设置__slots__的类实例不再使用字典存储实例数据，而是改为数组，因此可以减少内存占用、执行时间。</p>
<p>没有必要在__slots__里面添加方法、property的名字，因为他们是定义在类上而不是实例级别的。</p>
<div class="blog_h2"><span class="graybg">类型、类成员测试</span></div>
<pre class="crayon-plain-tag">aid = id(a)      #内置函数id()用于获取对象的标识符，为一整数，通常为内存地址
if a is b: pass  #is运算符用于判断两个变量是否指向同一个对象（标识符相等）
if a == b: pass  #如果a与b的值相等

if type(s) is list: pass     #对象的类型也是一个对象，该对象是单例的
if type(a) is type(b): pass  #如果a与b的类型相同
#如果对象属于类cname或派生自cname的任何类，isinstance返回True
isinstance (obj, cname)
#如果A是B的子类，则issubclass()返回True
issubclass(A,B)

#考虑到鸭子类型识别的问题，可以修改isinstance、issubclass方法的行为：
class IClass(object): 
    def __init__(self):
        self.implementors = set() 
    def register(self,C):
        self.implementors.add(C) 
    def __instancecheck__(self ,x):
        return self.__subclasscheck__(type (x)) 
    def __subclasscheck__(self,sub):
        return any(c in self.implementors for c in sub.mro())</pre>
<div class="blog_h2"><span class="graybg">抽象基类</span></div>
<p>使用abc模块可以定义抽象基类，该模块由元类ABCMeta与一组装饰器组成，使用方法如下：</p>
<pre class="crayon-plain-tag">from abc import ABCMeta, abstractmethod, abstractproperty
class Foo:
    __metaclass__ =ABCMeta       # Python3使用 class Foo(metaclass=ABCMeta)
    @abstractmethod              #声明抽象方法
    def spam(self, a, b): pass
    @abstractproperty            #声明抽象属性
    def name(self): pass</pre>
<p>抽象类不能实例化，到导致TypeError，抽象的派生类只要没有全部实现抽象方法、属性，则同样不能实例化。</p>
<p>抽象基类支持对已经存在的类进行注册——使其属于该类（isinstance、issubclass返回期望的结果），但是该注册<span style="background-color: #c0c0c0;">不会检查目标子类是否实现了抽象基类的任何抽象方法、属性</span>：</p>
<pre class="crayon-plain-tag">class Bar(object): pass   #既有类
Foo.register(Bar)         #注册基类</pre>
<p>使用抽象基类注册机制，可<span style="background-color: #c0c0c0;">以重新组织已有类型的层次结构</span>，例如numbers模块把数字类型重新整理，而默认他们都是继承object类的</p>
<div class="blog_h2"><span class="graybg">元类</span></div>
<p>元类知道如何创建、管理类：</p>
<pre class="crayon-plain-tag">class Foo(object): pass
type(Foo)    #对类对象取类型，即获得其元类，默认&lt;type 'type'&gt;</pre>
<p> 使用class语句定义新类时，解释器内部事件序列可以使用下面的代码示意：</p>
<pre class="crayon-plain-tag">class_name = "Foo"                #类名
class_parents = (object,)         #基类
#类主体代码
class_body = """                  
def __init__(self, x): pass
"""
class_dict = { }
#在局部字典class_dict中执行类主体代码
exec(class_body,globals(),class_dict)

#调用元类创建类对象。这一步可以自行定义
Foo = type(class_name, class_parents, class_dict)</pre>
<p>通过指定元类，可以改变类对象创建的行为：</p>
<ol>
<li>设置类变量__metaclass__可以显式指定元类</li>
<li>如果没有显示指定元类，则使用基类元组的第一个条目的元类作为新类的元类</li>
<li>如果没有指定基类，则使用默认值：<pre class="crayon-plain-tag">types.ClassType</pre> ，即Python2.2以后的type作为元类</li>
</ol>
<p>元类通常在框架组件中使用，通常可以继承<pre class="crayon-plain-tag">type</pre> 并重新实现<pre class="crayon-plain-tag">__init__()</pre> 、<pre class="crayon-plain-tag">__new__</pre> 等方法，来扩展新的元类，下面的元类要求所有类定义必须提供文档字符串：</p>
<pre class="crayon-plain-tag">class DocMeta(type):
    def __init__(self, name, bases, dict):
        for key, value in dict.items():   #遍历所有类的元素
            # 跳过特殊方法和私有方法
            if key.startswith("__"): continue
            # 跳过不可调用的任何方法
            if not hasattr(value, "__call__"): continue 
            # 检查doc字符串
            if not getattr(value, "__doc__"):
                raise TypeError("%s must have a docstring" % key) 
        type.__init__(self, name, bases, dict)  #调用默认实现来初始化类</pre>
<div class="blog_h1"><span class="graybg">模块、包与分发</span></div>
<p>大规模的Python程序通常以模块、包的形式组织。Python的包常常和目录对应，模块则和文件对应。</p>
<div class="blog_h2"><span class="graybg">模块与import语句</span></div>
<p><span style="background-color: #c0c0c0;">任何Python源文件</span>均可以作为模块来使用，对于一个名字为util.py的源文件，可以使用import util语句来将其作为模块加载。加载模块时，Python解释器将：</p>
<ol>
<li>创建新的命名空间，用作在相应源文件中定义的所有对象的容器。源文件中定义的函数、方法在使用global语句时，将访问该命名空间</li>
<li>在新创建的命名空间中执行模块中包含的代码——import导致模块中所有语句被执行</li>
<li>在调用函数中创建名称来引用模块命名空间，该名称默认与模块名称一致：<br />
<pre class="crayon-plain-tag">#util.py
class stringutils(object):
    @classmethod
    def indexOf(string, search): pass

#main.py
import util                           #导入源文件作为模块
util.stringutils.indexOf("123","2")   #使用模块中定义的类，注意前缀</pre>
</li>
</ol>
<p>用于引用模块的名称可以使用as限定符修改，新名称只限定使用了import语句的源文件或者上下文：</p>
<pre class="crayon-plain-tag">import util as Util
Util.stringutils.indexOf</pre>
<p><span style="background-color: #c0c0c0;">模块也是Python的First class对象</span>，因此可以分配给变量，存放在列表等数据结构中。</p>
<div class="blog_h2"><span class="graybg">从模块导入指定符号</span></div>
<p>使用from语句可以将模块中指定的具体符号引入当前命名空间中，访问这些导入的符号时，不需要模块名字空间前缀：</p>
<pre class="crayon-plain-tag">from util import stringutils
i = stringutils.indexOf("123","2")

#可以同时导入多个符号
from util import stringutils, numberutils
from util import (
    stringutils,
    numberutils
)

#可以在导入的同时重命名符号
from util import stringutils as su
#星号通配符可以导入所有除下划线开头的符号
#只能在模块最顶层使用，在函数内使用非法
from util import *</pre>
<p>在模块中定义列表<pre class="crayon-plain-tag">__all__</pre> ，可以精确限制import *能导入的符号。</p>
<p>你也可以使用from<span style="background-color: #c0c0c0;">从某个包中导入模块、从包中导入（定义在包的__init__.py中的）函数</span>：</p>
<pre class="crayon-plain-tag"># 从cc/gmem/py3子包中导入HelloModule模块
from .cc.gmem.py3 import HelloModule

if __name__ == '__main__':
    HelloModule.hello()</pre>
<div class="blog_h2"><span class="graybg">相对导入</span></div>
<p>从<span style="background-color: #c0c0c0;">以点号开头</span>的路径进行导入，表示相对于当前包寻找模块或符号：</p>
<pre class="crayon-plain-tag"># 从当前包的_multiprocessing模块中导入win32
from ._multiprocessing import win32

# 导入一个兄弟模块
from . import peermodule
# 导入__init__.py中定义的变量v1
from . import v1

# 导入父包中的一个模块
from .. import parentpackagemodule</pre>
<div class="blog_h2"><span class="graybg">以主程序形式运行</span></div>
<p>模块可以在import时在自己的名字空间中运行（以库模块方式），也可以以主程序的方式运行。</p>
<p>每个模块均可以访问变量<pre class="crayon-plain-tag">__name__</pre> ，该变量用于确定当前<span style="background-color: #c0c0c0;">模块在哪个模块内部运行</span>，解释器的<span style="background-color: #c0c0c0;">顶级模块</span>的名称为<pre class="crayon-plain-tag">__main__</pre> ，在命令行指定或者直接输入的程序将在__main__模块中运行：</p>
<pre class="crayon-plain-tag">#检查模块是否以程序的形式运行 
if __name__ == '__main__':
    #是
    pass
else:
    #否，我必须以模块的形式导入
    pass</pre>
<div class="blog_h2"><span class="graybg">模块搜索路径</span></div>
<p> 加载模块时，解释器在列表sys.path中搜索字典列表，sys.path的<span style="background-color: #c0c0c0;">第一项内容是空字符串，表示当前正在使用的字典，可能包括的其他条目有：字典名称、zip归档文件、.egg文件</span>。各<span style="background-color: #c0c0c0;">条目在sys.path中出现的顺序决定了模块加载时搜索的顺序</span>。</p>
<p>egg文件只是添加了版本号、依赖项的zip文件，使用zip文件的示例如下：</p>
<pre class="crayon-plain-tag">import sys
sys.path.append("modules.zip")
#假设modules里包含foo.py、bar.py两个文件
import foo,bar

#zip文件目录层次可以与OS文件系统层次混用
sys.path.append("/tmp/modules.zip/lib/python)</pre>
<p>使用归档文件时需要注意：</p>
<ol>
<li>python不会创建.pyc、.pyo文件，应当提前创建并放在归档文件中，避免加载模块时性能下降</li>
<li>C编写的共享库、扩展模块无法从归档文件中导入</li>
</ol>
<div class="blog_h2"><span class="graybg">模块加载和编译</span></div>
<p>可使用import加载的模块包括四类：</p>
<ol>
<li>Python源代码，即.py文件</li>
<li>编译为共享库或者DLL的C/C++扩展</li>
<li>一组包含模块的包</li>
<li>使用C编写并链接到Python解释器的内置模块</li>
</ol>
<p>在加载模块foo时，在顺序sys.path下每个目录中搜索以下文件：</p>
<ol>
<li>包定义：目录foo</li>
<li>已编译扩展：foo.pyd、foo.so、foomodule.so、foomodule.dll</li>
<li>foo.pyo：使用-O、-OO选项时</li>
<li>foo.pyc</li>
<li>foo.py、foo.pyw</li>
</ol>
<p>py文件在首次被import时，会被编译为字节码并写入为.pyc文件，后续导入时，缺省直接使用.pyc文件，<span style="background-color: #c0c0c0;">除非py的修改日期更新</span>（会自动重新生成pyc）。</p>
<p>如果使用-O，则会创建pyo，并且删除行号、断言、其它调试信息。如果使用-OO，则还会删除文档字符串。</p>
<div class="blog_h2"><span class="graybg">包</span></div>
<p>使用包可以把若干模块划分为一组，可以解决不同应用程序中模块名称的命名空间冲突问题。</p>
<p>通过<span style="background-color: #c0c0c0;">创建与包名字一致的目录，并且在该目录下编写</span><pre class="crayon-plain-tag">__init__.py</pre> ，即可创建包。包内可以放入其它<span style="background-color: #c0c0c0;">源文件、编译后的扩展、子包</span>。</p>
<p>包的导入与模块导入类似，都是使用import语句。<span style="background-color: #c0c0c0;">第一次导入包的任何部分，均会执行对应的__init__.py，父包的__init__.py比子包的先执行</span>。</p>
<div class="blog_h2"><span class="graybg">基于distutils进行分发</span></div>
<p>本节以工程my-autosizer v0.1.0为例进行阐述</p>
<div class="blog_h3"><span class="graybg">步骤概述</span></div>
<p>你可以使用distutils模块来分发Python程序给其他人使用，步骤如下：</p>
<ol>
<li>相关文件有序的组织到工程目录中，这些文件包括：模块、包、脚本、支持文档等。使用Pycharm时，这个目录就是Project的根目录。工程布局示例：<br />
<pre class="crayon-plain-tag">├── my-autosizer
   ├── dist
   │   └── myautoresizer-0.1.0.tar.gz
   ├── ma_autoresize.py
   ├── MANIFEST
   ├── ma_printrect.py
   ├── myautoresizer
   │   └── __init__.py
   └── setup.py</pre>
</li>
<li>在工程目录下编写安装脚本setup.py</li>
<li>可选的，编写一个安装配置文件</li>
<li>创建源码分发包（source distribution）</li>
<li>可选的，创建更多的二进制分发包（binary distributions）</li>
</ol>
<div class="blog_h3"><span class="graybg">编写setup.py</span></div>
<p>安装脚本是构建、分发、安装等一系列活动的中心所在，最简单的例子如下：</p>
<pre class="crayon-plain-tag">from distutils.core import setup

setup(
    name="myautoresizer",                                # 软件包名称
    version="0.1.0",                                     # 字符串版本号
    description='Auto resize and move GDK windows',
    author='Alex Wong',
    author_email='alex@gmem.cc',
    url='https://py.gmem.cc/myautoresizer',
    py_modules = [],                                     # 所有单一文件的Python模块列表
    packages=['myautoresizer'],                          # 所有包目录的列表
    scripts=['ma_autoresize.py', 'ma_printrect.py']      # 脚本文件的列表
)</pre>
<div class="blog_h3"><span class="graybg">创建分发包</span></div>
<p>调用setup.py，传入不同的相应参数，可以创建分发包，这些分发包自动保存到工程目录的dist子目录下：</p>
<pre class="crayon-plain-tag">cd /home/alex/Python/projects/pycharm/my-autosizer
# 创建源码分发包，生成 dist/myautoresizer-0.1.0.tar.gz
python setup.py sdist

# 创建二进制分发程序
python setup.py bdist 
# Window下分发为安装程序.exe
python setup.py bdist_wininst 
# Redhat下分发为安装程序.prm
python setup.py bdist_rpm</pre>
<div class="blog_h3"><span class="graybg">安装包</span></div>
<p>在客户机上，可以解压并安装上面的分发包：</p>
<pre class="crayon-plain-tag">tar xzf myautoresizer-0.1.0.tar.gz
cd myautoresizer-0.1.0

# 执行安装
python setup.py install
# 在我的机器上，以下文件被安装
# /usr/local/bin/ma_autoresize.py
# /usr/local/bin/ma_printrect.py
# /usr/local/lib/python2.7/dist-packages/myautoresizer/__init__.py
# 注意：工程根目录只是容器，不会体现在安装树的任何地方</pre>
<p>调用<pre class="crayon-plain-tag">install</pre> 子命令后：</p>
<ol>
<li>模块、包通常安装到Python库的<pre class="crayon-plain-tag">site-packages</pre> 下</li>
<li>脚本在Unix下通常安装到Python解释器二进制文件所在目录</li>
<li>脚本在Windows下通常安装到<pre class="crayon-plain-tag">%PYTHON_HOME%\Scripts</pre> 目录</li>
</ol>
<p>你也可以调用pip命令，直接安装压缩包：</p>
<pre class="crayon-plain-tag">sudo pip install myautoresizer-0.1.0.tar.gz</pre>
<div class="blog_h3"><span class="graybg">发布到PyPI</span></div>
<p>Python Package Index (PyPI)存储基于distutils分发的软件包的元数据信息，如果作者愿意，软件包本身也可以存放在上面。</p>
<p>通过<pre class="crayon-plain-tag">register</pre> 和<pre class="crayon-plain-tag">upload</pre> 子命令，你可以把元数据推送到PyPI上去。PyPI允许你提交某个软件包的任意数量的版本，你还可以覆盖既有的版本。</p>
<p>执行下面的命令注册软件包的元数据：</p>
<pre class="crayon-plain-tag">python setup.py register
# 你需要根据提示，提供登录身份，或者注册新用户</pre>
<p>执行下面的命令上传软件包： </p>
<pre class="crayon-plain-tag"># 上传源码发布包、Windows二进制发布包
python setup.py sdist bdist_wininst upload
    -r https://gmem.cc/pypi   # 指定PyPI仓库地址</pre>
<p>register和upload命令会检查<pre class="crayon-plain-tag">$HOME/.pypirc</pre> 文件，从中得到用户名、密码、仓库URL信息，该文件内容格式如下：</p>
<pre class="crayon-plain-tag">[distutils]
index-servers =
    pypi

[pypi]
repository:https://pypi.python.org/pypi
username:user
password:passwd</pre>
<p>Section头中的文字，是仓库的名称，可以作为<pre class="crayon-plain-tag">-r</pre> 参数使用： </p>
<pre class="crayon-plain-tag">python setup.py sdist upload -r pypi</pre>
<div class="blog_h2"><span class="graybg">基于setuptools进行分发</span></div>
<p>Setuptools对distutils进行了一系列的增强，特别是，它能很好的处理包之间的依赖关系。下面是Setuptools的主要特性：</p>
<ol>
<li>可以在构建阶段，利用easy_install自动查找、下载、安装、升级依赖。这些依赖可以通过HTTP、HTTPS、SVN、SourceForge等方式得到</li>
<li>创建EGG——单文件的、可导入的分发格式</li>
<li>对访问位于压缩文件中的数据文件提供增强支持</li>
<li>自动包含源码树中所有包，不必在setup.py中逐个指定</li>
<li>自动包含相关的文件到源码发布包中，不需要MANIFEST.in</li>
<li>对于工程中的每一个__main__函数，自动生成包装脚本或者Windows的exe文件</li>
<li>支持上传EGG或者源码发布包到PyPI</li>
<li>创建能自动发现扩展的可扩展程序</li>
</ol>
<div class="blog_h3"><span class="graybg">简单例子</span></div>
<p>Setuptools保证了和distutils的兼容性，包括安装脚本的文件名、API风格都是一样的。Setuptools对API进行了很多扩展：</p>
<pre class="crayon-plain-tag">setup(
    name="myautoresizer",
    version="0.1.2",
    packages=find_packages(exclude=["tests.*"]),  # 自动在源码树中寻找Python包
    install_requires=['docutils&gt;=0.3'],  # 指定依赖列表
    scripts=['ma_autoresize.py', 'ma_printrect.py'],
    package_data={
        # 对于任何包，其中的*.txt、*.text文件被包含到发布树
        '': ['*.txt', '*.text'],
        # 对于myautoresizer，其中的*.ini文件被包含到发布树
        'myautoresizer': ['*.ini'],
    },
    entry_points={
        'console_scripts': [
            'ma_printrect = myautoresizer.scripts:ma_printrect',
            'ma_autoresize = myautoresizer.scripts:ma_autoresize',
        ],
    }
)</pre>
<p>执行同样的命令，可以打源码发布包：</p>
<pre class="crayon-plain-tag">python setup.py sdist</pre>
<p>源码发布包会以tar.gz格式存放在dist子目录，同时，根目录会出现一个文件夹：myautoresizer.egg-info </p>
<div class="blog_h3"><span class="graybg">调用关键字</span></div>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 25%; text-align: center;">关键字</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>include_package_data</td>
<td>如果设置为True，依据MANIFEST.in，包中的所有数据文件被包含到构建树中</td>
</tr>
<tr>
<td>exclude_package_data</td>
<td>一个字典，Key为包名称，Value为需要排除掉的文件名通配符的列表</td>
</tr>
<tr>
<td>package_data</td>
<td>一个字典，Key为包名称，Value为需要包含在构建树中的文件名通配符的列表<br />如果使用include_package_data，你不需要指定该选项，除非你需要包含setup脚本运行过程中生成的文件</td>
</tr>
<tr>
<td>zip_safe</td>
<td>布尔值，提示当前工程是否可以安全的安装并从一个压缩文件中运行，如果不指定此选项，那么bdist_egg子命令必须分析整个工程，来寻找可能的问题</td>
</tr>
<tr>
<td>install_requires</td>
<td>字符串或者列表，指定该包所依赖的其它包及其版本</td>
</tr>
<tr>
<td>entry_points</td>
<td>
<p>一个字典，Key为扩展点组（ entry point group）的名称，Value为定义扩展点的字符串或者列表</p>
<p>扩展点用于支持服务/插件的自动发现</p>
</td>
</tr>
<tr>
<td>extras_require</td>
<td>一个字典，Key为工程额外特性的名称，Value为支持此额外特性需要安装的依赖</td>
</tr>
<tr>
<td>python_requires</td>
<td>对Python版本的要求</td>
</tr>
<tr>
<td>setup_requires</td>
<td>一个字符串或者列表，为了能让安装脚本运行，所需要的依赖</td>
</tr>
<tr>
<td>dependency_links</td>
<td>一组URL由于搜索setup_requires、tests_require的依赖</td>
</tr>
<tr>
<td>namespace_packages</td>
<td>
<p>用于命名工程的“命名空间包”的字符串列表。这个命名空间包（例如倒写的域名）可能被多个工程使用</p>
<p>EGG运行时系统能够自动合并具有共同命名空间包的子包，只要命名空间包的__init__.py不包含任何代码</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">使用find_packages()</span></div>
<p>对于大型工程来说，手工列出packages需要很大的工作量，此时可以使用find_packages()函数：</p>
<pre class="crayon-plain-tag"># where 指定源码目录，默认setup.py所在目录
# 在Python 3.2-只有包含__init__.py才能被识别为包
# exclude 需要排除的包名通配符
# include 需要包含的包名通配符
def find( where='.', exclude=(), include=('*',)): pass

# 排除掉所有测试包
find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"])</pre>
<div class="blog_h3"><span class="graybg">自动脚本创建</span></div>
<p>distutils打包、安装脚本的方式比较笨拙：</p>
<ol>
<li>脚本的名称不能很好的匹配Windows/Linux的扩展名习惯</li>
<li>你需要编写单独的脚本，仅仅为了容纳一个__main__函数。my-autosizer的v0.1.0中的ma_*.py脚本就是因此而存在</li>
</ol>
<p>要让setuptools自动创建脚本，只需要：</p>
<pre class="crayon-plain-tag">setup(
    # 其它参数
    entry_points={
        # 控制台脚本
        'console_scripts': [
            'foo = my_package.some_module:main_func',
            'bar = other_module:some_func',
        ],
        # GUI脚本
        'gui_scripts': [
            'baz = my_package_gui:start_func',
        ]
    }
)</pre>
<div class="blog_h3"><span class="graybg">让EGG可以直接执行</span></div>
<pre class="crayon-plain-tag">setup(
    entry_points = {
        'setuptools.installation': [
            'eggsecutable = my_package.some_module:main_func'
        ]
    }
)</pre>
<p> 添加此配置后，目标EGG增加可执行权限后，即可直接在Unix-like系统上执行。</p>
<div class="blog_h3"><span class="graybg">声明依赖</span></div>
<p>对于依赖版本的限制，你可以使用以下操作符：<pre class="crayon-plain-tag">&lt; &gt; &lt;= &gt;= == !=</pre> </p>
<div class="blog_h2"><span class="graybg">安装第三方库</span></div>
<p><a href="http://pypi.python.org" target="_blank">PyPI网站</a>包含大量的第三方扩展资源。对于没有依赖的简单库，可以使用脚本<pre class="crayon-plain-tag">python setup.py install</pre> 安装。</p>
<p>对于依赖关系复杂的库，最好使用setuptools提供的easy_install脚本，只需要输入<pre class="crayon-plain-tag">easy_install pkgname</pre> 即可安装指定的软件包，会自动从PyPI下载合适的软件、 依赖项。</p>
<div class="blog_h1"><span class="graybg">Python包管理器</span></div>
<div class="blog_h2"><span class="graybg">pip</span></div>
<p>pip是Python Packaging Authority (PyPA) 推荐的Python包管理器，支持从<a href="https://pypi.python.org/pypi">PyPI</a>、版本控制系统、本地工程等来源安装软件包。</p>
<div class="blog_h3"><span class="graybg">安装</span></div>
<p>从2.7.9/3.4以后，该工具集成到Python中，不需要额外安装。手工安装的步骤如下：</p>
<pre class="crayon-plain-tag">wget https://bootstrap.pypa.io/get-pip.py
python get-pip.py
rm get-pip.py</pre>
<div class="blog_h3"><span class="graybg">常用子命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>install</td>
<td>
<p>安装Python软件包，如果Wheel可用pip默认会使用之，要改变此行为，添加参数<pre class="crayon-plain-tag">--no-binary</pre> </p>
<p><span style="background-color: #c0c0c0;">举例：</span></p>
<pre class="crayon-plain-tag"># 从PyPI安装：
pip install pkgname              # 最新版本
pip install pkgname==1.0.4       # 特定版本
pip install 'pkgname&gt;=1.0.4'     # 最小版本

# 为当前用户安装
# Linux下安装到 ~/.local/；Windows下安装到%APPDATA%\Python 
pip install --user pkgname

# 从指定的“需求文件”安装
pip install -r requirements.txt

# 从Wheel安装
pip install pkgname-1.0-py2.py3-none-any.whl</pre>
</td>
</tr>
<tr>
<td>uninstall</td>
<td>卸载Python软件包</td>
</tr>
<tr>
<td>list</td>
<td>
<p>列出已经安装的Python软件包，<span style="background-color: #c0c0c0;"><span style="background-color: #c0c0c0;">举例：</span></span>
<p><pre class="crayon-plain-tag"># 列出所有软件包
pip list
# 列出过期的软件包
pip list --outdated</pre>
</td>
</tr>
<tr>
<td>show</td>
<td>显示一个已安装Python软件包的信息，包括名称、版本、安装位置、依赖关系</td>
</tr>
<tr>
<td>download</td>
<td>下载Python软件包</td>
</tr>
<tr>
<td>freeze</td>
<td>把已安装的软件包是出为需求文件格式（requirements format）</td>
</tr>
<tr>
<td>search</td>
<td>从PyPI搜索软件包，目标软件包的名称或者摘要信息必须包含指定的关键字</td>
</tr>
<tr>
<td>wheel</td>
<td>
<p>要使用该命令，必须先安装<pre class="crayon-plain-tag">pip install wheel</pre> </p>
<p>Wheel是一种归档格式，比起从源码归档安装，它的速度很快</p>
</td>
</tr>
<tr>
<td>hash</td>
<td>计算包归档文件的哈希</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">配置文件</span></div>
<p>你可以使用配置文件为pip命令提供默认选项。</p>
<p>配置文件位置如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 35%; text-align: center;">配置文件</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>/etc/pip.conf</td>
<td>全局配置文件</td>
</tr>
<tr>
<td>~/.config/pip/pip.conf</td>
<td>个人配置文件</td>
</tr>
<tr>
<td>~/.pip/pip.conf</td>
<td>老版本使用的个人配置文件，目前仍然支持</td>
</tr>
<tr>
<td>$VIRTUAL_ENV/pip.conf</td>
<td><a href="https://virtualenv.pypa.io/en/stable/">virtualenv</a>中的配置文件</td>
</tr>
</tbody>
</table>
<p>配置文件示例如下：</p>
<pre class="crayon-plain-tag">; 这一段针对所有子命令
[global]
; 命令执行超时
timeout = 60
; 使用镜像包索引
index-url = https://pypi.doubanio.com/simple

; 这一段仅针对freeeze子命令
[freeze]
timeout = 10 </pre>
<div class="blog_h2"><span class="graybg">pip2</span></div>
<p>作为新版本的pip，目前还不成熟，安装步骤如下：</p>
<pre class="crayon-plain-tag">git clone https://github.com/osupython/pip2.git
cd pip2
python setup.py install</pre>
<div class="blog_h2"><span class="graybg">setuptools</span></div>
<p>使用该工具可以方便的下载、构建、安装、升级、卸载Python软件包，安装步骤如下：</p>
<pre class="crayon-plain-tag">wget https://bootstrap.pypa.io/ez_setup.py
python ez_setup.py</pre>
<p>该工具最常用的命令是easy_install，用来安装Python模块。</p>
<div class="blog_h1"><span class="graybg">Virtualenv</span></div>
<p>Virtualenv的目的是创建隔离的Python环境，可以解决不同软件之间<span style="background-color: #c0c0c0;">依赖包冲突、以及潜在的文件权限</span>的问题。</p>
<p>在开发Python应用的时候，依赖包默认都会安装到Python运行时的site-packages目录下。这样，如果两个应用依赖统一个包的不同版本，就会出现冲突。要解决这类问题，可以利用Virtualenv。</p>
<p>从3.3版本开始，Virtualenv的一部分功能作为标准库集成到venv模块中。不包含在其中的功能有：</p>
<ol>
<li>创建Bootstrap脚本</li>
<li>为其它Python版本创建虚拟环境</li>
</ol>
<div class="blog_h2"><span class="graybg">安装</span></div>
<p>可以使用任何版本的pip来安装和管理Virtualenv：</p>
<pre class="crayon-plain-tag">pip3 install virtualenv</pre>
<div class="blog_h2"><span class="graybg">使用</span></div>
<div class="blog_h3"><span class="graybg">创建虚拟环境</span></div>
<p>执行下面的命令，可以创建一个目录，并将其作为一个虚拟环境：</p>
<pre class="crayon-plain-tag">virtualenv  certbot-dns-aliyun</pre>
<p>上面命令创建的虚拟环境的名字是certbot-dns-aliyun，他相当于一个Python的$PREFIX目录，和Python标准的目录结构对应：</p>
<ol>
<li>lib，此虚拟环境的库文件，包被安装到lib/pythonX.X/site-packages/下</li>
<li>bin，可执行文件，包括python这个文件</li>
</ol>
<p>包括pip、setuptools在内的包，会自动安装到新创建的虚拟环境中。</p>
<div class="blog_h3"><span class="graybg">进入虚拟环境</span></div>
<p>要进入虚拟环境，执行其中的脚本：</p>
<pre class="crayon-plain-tag">source certbot-dns-aliyun/bin/activate</pre>
<p>你会发现命令提示符增加了前缀<pre class="crayon-plain-tag">(certbot-dns-aliyun)</pre>。 </p>
<p>现在，你通过pip安装的包，都会安装到当前虚拟环境中，系统的Python环境不会受到影响。</p>
<div class="blog_h3"><span class="graybg">退出虚拟环境</span></div>
<p>执行命令<pre class="crayon-plain-tag">deactivate</pre>即可退出当前虚拟环境。</p>
<div class="blog_h3"><span class="graybg">删除虚拟环境</span></div>
<p>简单的删除对应目录即可。</p>
<div class="blog_h2"><span class="graybg">命令</span></div>
<div class="blog_h3"><span class="graybg">格式</span></div>
<pre class="crayon-plain-tag">virtualenv [OPTIONS] DEST_DIR</pre>
<div class="blog_h3"><span class="graybg">选项</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">选项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>-p PYTHON_EXE</td>
<td>
<p>指定使用的Python解释器：</p>
<pre class="crayon-plain-tag">virtualenv -p /usr/bin/python2.7 
virtualenv --python=python3.5</pre>
<p>将会基于该解释器（及其安装的包）来创建新的虚拟环境 </p>
</td>
</tr>
<tr>
<td>--no-site-packages</td>
<td>
<p>不将系统Python环境的第三方包复制过来，也就是说创建的是一个空的干净的环境</p>
<p>已经废弃，目前默认行为就是如此</p>
</td>
</tr>
<tr>
<td>--system-site-packages</td>
<td>允许虚拟环境访问系统Python环境的第三方包</td>
</tr>
<tr>
<td>--always-copy</td>
<td>总是拷贝而非符号链接文件</td>
</tr>
<tr>
<td>--relocatable</td>
<td>
<p>使一个即有的虚拟环境可重定位：</p>
<ol>
<li>修复脚本内容</li>
<li>使所有.pth文件使用相对路径</li>
</ol>
</td>
</tr>
<tr>
<td>--no-setuptools<br />--no-pip<br />--no-wheel</td>
<td>不在虚拟环境中安装这些软件</td>
</tr>
<tr>
<td>--extra-search-dir</td>
<td>从此额外的目录中搜索setuptools/pip，可以指定多次</td>
</tr>
<tr>
<td>--download<br />--no-download</td>
<td>从PyPI下载预安装的包</td>
</tr>
<tr>
<td>--prompt=PROMPT</td>
<td>指定命令提示符前缀</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">常用代码片段</span></div>
<div class="blog_h2"><span class="graybg">语言基础</span></div>
<div class="blog_h3"><span class="graybg">随机数和文本</span></div>
<pre class="crayon-plain-tag">import random

corpTypes = [22, 23, 24, 25, 26, 49, 50, 180, 181, 182, 184, 185, 186, 77, 164, 165, 220]

# 对序列进行随机的重新排序
random.shuffle(corpTypes)

# 从序列中随机选取一个
random.choice(corpTypes)

# 从序列中随机选取N个样本
random.sample([10, 20, 30, 40, 50], k=N)

# 随机浮点数
# 0-1之间随机
random.random()
# 2.5-10.0之间随机
random.uniform(2.5, 10.0)   

# 随机整数
# 0-9之间随机
random.randrange(10)  
# 3-9之间随机
random.randrange(3,10)</pre>
<div class="blog_h3"><span class="graybg">数学运算</span></div>
<pre class="crayon-plain-tag">#decimal模块实现了IBM通用十进制算法标准，能够准确表示十进制值
import decimal 
x = decimal.Decimal('3.4')
y = decimal.Decimal('4.5')
a = x * y  #15.30
b = x / y  #0.7555555555555555555555556
#改变精度并计算
decimal.getcontext().prec = 3  #三位有效数字
a = x * y  #15.3
b = x / y  #0.756
#只改变语句块的精度
with decimal.localcontext(decimal.Context(prec=10)):
    a = x * y  #15.30
    b = x / y  #0.7555555556

#Context对象用于控制十进制数的各种属性
Context(
    prec = None,       #精度的位数，即有效数字数
    rounding = None,   #四舍五入方式
    traps = None,      #信号列表，在发生算术异常时有用
    flags = None,      #环境初始状态的信号列表
    Emin = None,       #指数最小值
    EMax = None,       #指数最大值
    capitals = 1       #指数使用'E'还是'e'，默认'E'
)


#math模块定义了很多标准算术运算函数
#numbers模块对数值类的层次进行梳理</pre>
<div class="blog_h3"><span class="graybg"> 命令行参数</span></div>
<pre class="crayon-plain-tag"># Python使用列表sys.argv存放命令行参数，第一个元素是程序的名称，后续为命令行参数
import sys
if len( sys.argv ) != 3 :
    sys.stderr.write( "Invalid arguments" )  # 访问标准输出
    raise SystemExit( 1 )  # 以非零退出
inputfile = sys.argv[1]



# 对于复杂的命令行参数，可以使用optparse模块进行处理
import optparse
p = optparse.OptionParser()
p.add_option( 
             "-o" ,  # 命令行选项
             action = "store",   #store表示把值存放在Options中
             dest = "outfile",   #在处理结果Options中的键
             default = "out.log" #默认值
            )
p.add_option( "--output" , action = "store", dest = "outfile" )
p.add_option( "-o" , "--output" , action = "store", dest = "outfile" )  # 同时指定长短选项
# 布尔选项，在命令行中只指定命令选项，而不指定值
p.add_option( "-d" , action = "store_true", dest = "debug" )       #store_true表示存储为True
p.add_option( "--debug" , action = "store_true", dest = "debug" )
# 设置一个或者多个选项的默认值
p.set_defaults( debug = False )
# 解析命令行
# opts为包含所有选项值的字典
# args为为解析为选项的命令行项的列表
(opts, args) = p.parse_args()
outfile = opts.outfile
debugmode = opts.debug</pre>
<div class="blog_h3"><span class="graybg">环境变量</span></div>
<pre class="crayon-plain-tag">#可以通过字典os.environ访问环境变量
import os
path = os.environ ["PATH" ] 
user = os.environ["USER"]

#写入的环境变量会影响正在运行的程序、Python创建的子进程
os.environ ["PATH" ] = ""</pre>
<div class="blog_h3"><span class="graybg">如何使用弱引用</span></div>
<pre class="crayon-plain-tag">#弱引用不会导致引用计数的增加，因而可以避免循环引用
class A: pass
a = A()
ar = weakref.ref(a)  #创建弱引用
#只要把弱引用作为函数调用，即可获得底层的对象，如果对象已经不存在，则返回None
a = ar()</pre>
<div class="blog_h3"><span class="graybg">访问标准输入输出</span></div>
<p>Python在sys模块提供stdin、stdout、stderr三个文件对象，用于访问标准输入、输出、错误。</p>
<pre class="crayon-plain-tag">import sys
sys.stdout.write("Enter password: ")  #写到标准输出，通常在屏幕上显示
sys.stdin.readline()                  #读取标准输入，通常映射到键盘
#可以如下从stdin读取一行文本（不包括结尾换行符）
name = raw_input ("Enter password: ")</pre>
<p>键盘中断会引发KeyboardInterrupt异常。  </p>
<div class="blog_h3"><span class="graybg">深复制/浅复制</span></div>
<pre class="crayon-plain-tag">import copy
ls = [[], []]
lsc = copy.copy(ls)
lsdc = copy.deepcopy(ls)</pre>
<p>实现<pre class="crayon-plain-tag">__copy__(self)</pre>、<pre class="crayon-plain-tag">__deepcopy__(self)</pre>可以控制复制行为。</p>
<div class="blog_h3"><span class="graybg">退出钩子</span></div>
<p>在解释器退出时执行某个函数：</p>
<pre class="crayon-plain-tag">def exithook():
    print 'Exiting...'
import atexit
atexit.register(exithook)</pre>
<p>&nbsp;</p>
<div class="blog_h2"><span class="graybg">调用Shell</span></div>
<div class="blog_h3"><span class="graybg">执行命令</span></div>
<pre class="crayon-plain-tag"># 执行指定的命令，并等待其结束
# args：字符串，或者程序参数的序列。如果传递字符串则要求shell=True或者字符串仅仅是不带参数的被调用程序的名称
# stdin, stdout, stderr 对应被调用命令的标准输入、输出、错误
# shell 如果设置为True则目标程序通过Shell执行
# subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)

from subprocess import call
call(['ls','-l'])
call(["ma_autoresize.py"])</pre>
<div class="blog_h3"><span class="graybg">读取输出</span></div>
<pre class="crayon-plain-tag">internal_ip = subprocess.check_output('''
    kubectl get node %s -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}'
''' % (node.node_name()), shell=True)
#                         不设置shell为True导致No such file or directory

ipstr = str(internal_ip, encoding='utf8')</pre>
<div class="blog_h3"><span class="graybg">重定向</span></div>
<pre class="crayon-plain-tag"># 丢弃
subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

# 重定向到文件
with open('sysout.log', 'w') as out, open('syserr.log', 'w') as err:
    subprocess.Popen(cmd, shell=False, stdout=out, stderr=err)</pre>
<div class="blog_h3"><span class="graybg">强杀子进程</span></div>
<pre class="crayon-plain-tag">import subprocess
import os

#若要使用kill()方法终结进程，则shell必须为False
p = subprocess.Popen(['D:/PATH_TO_BAT.bat','ARG0'], shell=False)
p.kill()

#使用psutil亦可
import psutil
import os
def kill_proc_tree(pid, including_parent=True):    
    parent = psutil.Process(pid)
    for child in parent.children(recursive=True):
        child.kill()
    if including_parent:
        parent.kill()
#杀死当前进程的所有子进程
kill_proc_tree(os.getpid(), False)</pre>
<div class="blog_h2"><span class="graybg">文件系统</span></div>
<div class="blog_h3"><span class="graybg">目录操作</span></div>
<pre class="crayon-plain-tag">#获取上级目录
import posixpath
import ntpath
print ntpath.abspath(ntpath.join('D:\\JavaEE', '..'))  # D:\
print posixpath.abspath(posixpath.join('/home', '..')) # /</pre>
<div class="blog_h3"><span class="graybg">路径判断与操控</span></div>
<pre class="crayon-plain-tag"># 是否是文件，不存在或者不是普通文件，返回False
os.path.isfile('/swapfile')
# 是否是目录
os.path.isdir('/tmp')
# 路径是否对应文件或目录
os.path.exists('~')

# 展开~开头的路径
os.path.expanduser('~')  # /home/alex
# 展开Shell变量替换
os.path.expandvars("$PWD")

# 得到文件所在目录
os.path.dirname('/home/alex')   # /home
# 得到绝对路径
os.path.abspath('.')
# 得到当前脚本所在目录
os.path.dirname(os.path.abspath(__file__))
# 得到当前工作目录
os.getcwd()
# 改变当前工作目录
os.chdir('/tmp')

# 链接子目录、文件路径
os.path.join('/home','alex')  # /home/alex
os.path.join('home','alex')   # home/alex
os.path.join('/home','/alex') # /alex</pre>
<div class="blog_h3"><span class="graybg">文件复制和移动</span></div>
<pre class="crayon-plain-tag">import os
import shutil

os.chdir('/tmp')

# 实现touch操作
def touch(path):
    with open(path, 'a'):
        os.utime(path, None)

# 创建目录
os.makedirs('src/sub1')
os.makedirs('src/sub2')
os.makedirs('dest')

# 创建文件
touch('src/sub1/file11')
touch('src/sub1/file12')
touch('src/sub1/file13')
touch('dest/file22')

# 复制文件
shutil.copy('src/sub1/file11','dest')          # 复制单个文件到目录
# 删除单个文件
os.remove('dest/file11')
shutil.copy('src/sub1/file11','dest/file22')   # 复制到文件，覆盖既有文件

# 复制目录
shutil.copytree('src/sub1','dest/sub2')        # 第二个参数必须是不存在的路径，用来作为目标目录
# 删除整个目录，包括指定的目标本身
shutil.rmtree('dest/sub1')

# 移动目录
shutil.move('src/sub1','dest/sub1')
# 移动文件
shutil.move('dest/sub1/file11','src')</pre>
<div class="blog_h3"><span class="graybg">文件的读写</span></div>
<p>打开文件读写时，可以指定r、w、a标记位。后面可以附加b、t表示二进制还是文本模式。在二进制模式下，不会在Windows下对\n与\r\n进行转换。</p>
<pre class="crayon-plain-tag">#内置函数open(name [,inode [, bufsize]])用于打开和创建文件对象
f = open('sys.log', 'r')  #以读模式打开文件，可以省略第二个参数
f = open('sys.log', 'w')  #以写模式打开文件</pre>
<p>在Python2中，所有读操作均返回二进制字符串。在Python3中，文本模式打开读取到Unicode字符串，二进制模式打开返回字节字符串。</p>
<p>如果要处理Unicode字符文件的读写，则需要考虑字节序的问题（Unicode字符在内部使用多字节整数表示），例如，需要决定把U+HHLL以Little-ending方式写为LL HH，还是Big-ending方式写为HH LL。使用codecs提供的函数是最直接的处理Unicode文件的方法：</p>
<pre class="crayon-plain-tag"># codecs.open (filename [, mode [, encoding [, errors]]]
codecs.open("var.log",'r','utf-8', 'strict')  #读取
codecs.open("var.log",'w','utf-8')  #写入

# 可以使用codecs.EncodedFile来包装已经存在的文件对象
# codecs.EncodedFile(file, inputenc [, outputenc [,errors]])
f0 = codecs.EncodedFile(f, 'utf-8')</pre>
<p>Unicode文件可能包含特殊的BOM（字节顺序标记）用于指示字节编码的方式，它作为文件的第一个字符写入：\xff\xfe表示UTF-16-LE，\xfe\xff表示UTF-16-BE。</p>
<div class="blog_h2"><span class="graybg">网络编程</span></div>
<div class="blog_h3"><span class="graybg">ICMP请求</span></div>
<p>要发送ICMP报文，可以使用ping3包：</p>
<pre class="crayon-plain-tag">ping3.ping(ip, timeout / float(1000))</pre>
<div class="blog_h3"><span class="graybg">TCP连接</span></div>
<pre class="crayon-plain-tag">with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    try:
        sock.connect((ip, port))
    except ConnectionError:
        pass</pre>
<div class="blog_h3"><span class="graybg">UDP数据报</span></div>
<pre class="crayon-plain-tag">from IN import IP_RECVERR
sock = socket.socket(socket.AF_INET, sock_type)
sock.setsockopt(socket.IPPROTO_IP, IP_RECVERR, 1)
sock.settimeout(timeout / 1000.0)
sock.sendto(bytes(1024), (ip, port))
try:
    sock.recvfrom(1024)
except socket.timeout as e:
    pass</pre>
<div class="blog_h3"><span class="graybg">静态HTTP服务</span></div>
<pre class="crayon-plain-tag">from werkzeug.serving import run_simple
from werkzeug.wrappers import Request, Response
import os
@Request.application
def application(request):
    return Response('')
run_simple('0.0.0.0', 1010, application, static_files={"/" : os.path.dirname(__file__)})</pre>
<div class="blog_h2"><span class="graybg">压缩与解压</span></div>
<pre class="crayon-plain-tag">#bz2模块用于根据bzip2压缩算法读取和写入压缩数据

#打开一个名为filename的bz2文件，压缩级别0-9越大压缩比越高
BZ2File(filename [, mode [, buffering [, compress_level]]])
#返回字符串data中数据的一个压缩版
compress(data [, compress_level])
#返回一串包含字符串data解压数据的字符串
decompress (data)


#创建用于顺序压缩数据块序列的压缩器对象
c = BZ2Compressor([compress_level])
c.compress(data)   #添加数据到压缩器对象
c.flush            #刷新内部缓冲区并返回一串包含全部剩余数据的压缩字符串

#创建一个解压缩器对象
d.BZ2Decompressor()
d.decompress()     #解压缩

#其他压缩模块
#gzip模块提供了一个类，即Gzipfile。它可以用来读取和写入与GNU gzip程序兼容的文件，其使用与普通文件相同
GzipFile([filename [, mode [, compress_leve1 [, fileobj] ]]])
open(filename [, mode [, compress_leve1 ]])
#tarfile模块用于操作tar归档文件。利用这一模块，无论tar文件压缩与否，都可以读取和写入tar文件
#zipfilee模块用于操作zip格式编码的文件
#zlib块通过提供对zlib库的访问支持数据压缩</pre>
<div class="blog_h2"><span class="graybg">数据库</span></div>
<p>Python Database API Specification V2.0是Python社区制定的一套数据库访问标准，MySQL、Oracle等数据库可以通过该标准的API访问。</p>
<pre class="crayon-plain-tag"># 连接对象：Connection
# 使用connect(dsn="hostname:DBNAME",user="",password="")函数创建Connection对象
# Connection对象的方法：
# close()      关闭数据库连接
# commit()     提交未完成的事务
# rollback()   回滚到事务开始前状态
# cursor()     创建一个游标对象，用于进行SQL查询
# Cursor对象的方法和属性：
# callproc(procname [, parameters])   调用存储过程
# close()                             关闭游标
# execute(sql [, parameters])         执行查询语句或者命令
# executemany(sql [, parameterseqs])  重复执行查询或者命令，sql是一个查询语句，parameterseqs是一个查询参数的序列，其每一项是序列或者映射
# fetchone()                          返回execute、executemany生成的结果集的下一行
# fetchonemany([size])                返回execute、executemany生成的结果集若干行
# fetchall()                          返回剩余结果集行的序列
# rowcount                            结果集的行数

#数据库类型
#Date(year, month, day)                            日期
#Time(hour, minute, second)                        时间
#Timestamp(year, month, day, hour, minute second)  时间戳
#DateFromTicks(ticks)                              根据系统时间创建日期对象。ticks是秒数，就像函数time.time()返回的一样
#TimeFromTicks(ticks)                              根据系统时间创建时间对象
#TimestampFromTicks(ticks)                         根据系统时间创建时间戳对象
#Binary(s)                                         根据字符串创建二进制对象

#数据访问异常的超类为Error，包括以下子类
#InterfaceError    与数据库界面相关的错误。但是不是数据库本身
#DatabaseError     与数据库本身相关的错误
#DataError         与处理的数据相关的错误。例如，类型转换错误，除零等
#OperationalError  与数据库本身的运行相关的错误。例如，丢失连接
#IntegrityError    当数据库的关系完整性被破坏时出现的错误
#IntenalError      数据库内部错误。例如，如果是一个失效指针
#ProgrammingError  SQL査询中的错误
#NotSupportedError 不受底层数据库支持的数据库API方法导致的错误

if __name__ == '__main__':
    import sqlite3
    conn = sqlite3.connect("dffile")
    cur = conn.cursor()
    # 执行查询
    cur.execute("select corp_name, reg_capi from t_corp where org_id = 1000")
    # 遍历结果集
    while True:
        row = cur.fetchone()
        if not row:break
        # 处理行
        corp_name, reg_capi = row
    # 另一种处理结果集的方法
    for corp_name, reg_capi in cur: pass
    
    # 预编译语句
    pstmt = "select reg_no,addr from t_corp where org_id = ? and status = ?"
    cur.execute(pstmt, (10001, 'A'))  #使用元组填充占位符?
    #注意，并不是所有数据库模块都使用?占位符，模块的paramstyle变量描述其占位符风格，例如：
    #qmark    where id = ? and status = ?           元组填充
    #numeric  where id = :0 and status = :1         元组填充
    #named    where id = :id and status = :status   字典填充</pre>
<div class="blog_h3"><span class="graybg">数据结构类</span></div>
<pre class="crayon-plain-tag">#array模块定义了新类型array，与列表类似，但是元素必须是单一类型
#array(typecode [, initializer])
#其中typecode：
# 'c'   8位字符
# 'b'   8位整型
# 'B'   8位无符号整型
# 'u'   Unicode字符
# 'h'   16位整型
# 'H'   16位无符号整型
# 'i'   整型
# 'I'   无符号整型
# 'l'   长整型
# 'L'   无符号长整型
# 'f'   单精度浮点型
# 'd'   双精度浮点型

#支持的方法、属性列表
# typecode	  用于创建数组的类型编码字符
# itemsize	  存储在数组中的项目大小（以字节为单位）
# append (x)	  将x附加到数组末尾
# buffer_info()   返回（address, length)，提供用于存储数组的缓冲区的内存位置和长度
# byteswap()	  在大尾与小尾之间切换数组中所有项目的字节顺序。仅支持整型值
# count (x)	  返回a中出现X的次数
# extend(b)	  将b附加到数组a的末尾。b可以是一个数组，也可以是一个元素类型与a中相同的可迭代对象
# fromfile(f, n)  从文件对象中读取n个项目（二进制格式），并附加到数组末尾。f必须是一个文件对象。如果可读取的项目少于n，则抛出EOFError
# fromlist(list)  将list中的项目附加到数组末尾。list可以是任何可迭代对象
# fromstring(s)	  附加字符串s中的项目，其中s是一个由二进制值组成的字符串，与使用fromfile()进行读取相同
# index(x)	  返回x在a中首次出现的位置索引。如果未找到，则抛出ValueError
# insert(i, x)	  在位置i前插入x
# pop([i])	  从数组中删除项目i并将其返回。如果i已被删除，则删除最后一个元素
# remove (x)	  从数组中删除第一个x。如果未找到，则抛出ValueError
# reverse()       反转数组的顺序
# tofile(f)       将所有项目写入文件f。数据保存为本机二进制格式
# tolist ()       将数组转换为普通的值列表
# tostring()      转换为由二进制数据组成的字符串，与使用tofile()写入的数据相同
# tounicode()     将数组转换为Unicode字符串。如果数组类型不为'u'则抛出ValueError

a = array.array ('i', [1,2,3 ,4,5])
b = array.array(a.typecode, (2*x for x in a) )  #从a中创建新数组


#collections模块包含一些有用容器类型的高能实现、各种容器的抽象基类
deque([iterable [, maxlen]])                   #双端队列
defaultdict([default_factory], ...)            #类似于基本字典，但是再查找不存在键时，会使用default_factory提供默认值
namedtuple(typename, fieldnames, [,verbose])   #命名元组，相比起字典，效率更高
IAddr =  collections.namedtuple('InternetAddress',['hostname','port'])
a = InternetAddress('gmem.cc',80)
a.hostname</pre>
<div class="blog_h2"><span class="graybg">文本处理</span></div>
<div class="blog_h3"><span class="graybg">字符串格式化</span></div>
<p>参考：<a href="/text-processing-with-python#format-string">格式化字符串</a></p>
<p>打印可以使用print语句或者print函数</p>
<pre class="crayon-plain-tag">f = open('log', 'w')
print &gt;&gt; f,"Error"    #可以改变print的目标</pre>
<div class="blog_h3"><span class="graybg">处理Unicode字符</span></div>
<p>对于原始字节字符串s，如果其包含已编码的Unicode字符串，则可以使用s.decode(encoding, errors)方法进行转换。<br /> 对于Unicode字符串u，可以使用u.encode(encoding, errors)方法进行编码。</p>
<p>如果包含目标encoding不支持的字符，默认引发UnicodeError，可以指定errors参数以修改此行为。</p>
<p>通过sys.getdefaultencoding()可以获取默认编码。</p>
<div class="blog_h2"><span class="graybg">对象序列化</span></div>
<pre class="crayon-plain-tag">#使用pickle模块进行对象序列化
if __name__ == '__main__':
    import pickle
    users = {
        10001:'Alex',
        10002:'Meng'
    }
    fname = "F:/temp/user.dmp"
    # 第三个参数表示协议，默认0，为文本协议；1为二进制协议；2为较新的协议；3只能用于Python3
    pickle.dump(users, open(fname, 'w'), 1)  # 类似的dumps方法返回包含已序列化数据的字符串
    loaded_users = pickle.load(open(fname, 'r'))  # 从文件反序列化，自动检测协议
    print loaded_users  #类似的loads从字符串反序列化
    
    #如果存在复杂的对象关系，例如循环引用，应当使用Pickle对象进行序列化
    p = pickle.Pickler(open(fname, 'w'),1)
    p.dump(users) #把users对象写入文件，并记住其唯一标识，后续再写，自动进行引用处理
    
    up = pickle.Unpickler(open(fname, 'f'))</pre>
<div class="blog_h2"><span class="graybg">日志</span></div>
<p>logging模块为Python提供了日志记录的功能。类似于Log4j。logging包含以下组件</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">组件</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>logger</td>
<td>
<p>提供日志记录接口，可以执行一些配置，或者执行日志记录：</p>
<pre class="crayon-plain-tag">#根据名称获取日志记录器，不指定name返回root记录器，此方法调用具有幂等性
logging.getLogger(name)
#默认logging提供的方法是使用root记录器</pre>
</td>
</tr>
<tr>
<td>handler</td>
<td>用于将日志发往相应的目的地，例如文件、控制台、套接字。常用的包括：StreamHandler、FileHandler 、 RotatingFileHandler、TimedRotatingFileHandler、SocketHandler、DatagramHandler</td>
</tr>
<tr>
<td>filter</td>
<td>用来过滤日志内容，决定是否将其发送给handler</td>
</tr>
<tr>
<td>formatter</td>
<td>用于指定日志输出格式 </td>
</tr>
</tbody>
</table>
<p>logging支持5种记录级别：DEBUG、INFO、WARNING、ERROR、CRITICAL，在不指定的情况下，<span style="background-color: #c0c0c0;">默认记录级别为WARNING</span>，也就是说，低于这个级别的信息不会被记录到控制台或文件。
<p>利用basicConfig函数可以配置日志记录的参数，例如下面的例子将日志记录到文件：</p>
<pre class="crayon-plain-tag">import logging
#记录到文件，日志级别为DEBUG
#使用命令行参数 --log=DEBUG 也可以指定日志级别
logging.basicConfig( filename='pyapp.log', filemode='w', level=logging.DEBUG )
logging.debug( 'This message should be written to pyapp.log' )</pre>
<p>logging支持使用C语言printf风格的格式化：</p>
<pre class="crayon-plain-tag">import logging
logging.warning( '%s is %d years old.', 'Alex', 24 )</pre>
<p>为basicConfig指定format参数，可以详细定制日志输出格式，例如：</p>
<pre class="crayon-plain-tag">import logging
#日志输出格式为：时间 日志内容
#详细规定日期时间的显示格式，datefmt的参数参考time.strftime()
#format中占位符的格式为%(key)[-][n][s|d...]，-表示右补白，n表示显示的最小宽度，s表示格式化为字符串
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')</pre>
<p> format可以使用下表列出的占位符：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">占位符</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>%(name)s</td>
<td>Logger的名称</td>
</tr>
<tr>
<td>%(levelno)s</td>
<td>数字形式的日志级别</td>
</tr>
<tr>
<td>%(levelname)s</td>
<td>文本形式的日志级别</td>
</tr>
<tr>
<td>%(pathname)s</td>
<td>发起日志函数调用的源代码文件的详细路径</td>
</tr>
<tr>
<td>%(filename)s</td>
<td>发起日志函数调用的源代码文件的名称</td>
</tr>
<tr>
<td>%(module)s</td>
<td>模块名称，即filename的基名部分</td>
</tr>
<tr>
<td>%(funcName)s</td>
<td>发起日志函数调用的函数</td>
</tr>
<tr>
<td>%(lineno)d</td>
<td>发起日志函数调用的行号</td>
</tr>
<tr>
<td>%(created)f</td>
<td>LogRecord被创建的时间</td>
</tr>
<tr>
<td>%(relativeCreated)d</td>
<td>LogRecord创建相对于logging模块被加载的时间</td>
</tr>
<tr>
<td>%(asctime)s</td>
<td>人类可阅读的LogRecord创建时间</td>
</tr>
<tr>
<td>%(msecs)d</td>
<td>LogRecord被创建时间的毫秒部分</td>
</tr>
<tr>
<td>%(thread)d</td>
<td>线程ID</td>
</tr>
<tr>
<td>%(threadName)s</td>
<td>线程名称</td>
</tr>
<tr>
<td>%(process)d</td>
<td>进程ID</td>
</tr>
<tr>
<td>%(message)s</td>
<td>日志主体内容，由msg参数传入</td>
</tr>
</tbody>
</table>
<p>除了通过编程方式配置logging以外，还可以支持配置文件方式：</p>
<pre class="crayon-plain-tag">logging.config.fileConfig('logging.conf')</pre>
<p>logging.conf文件的格式类似于INI，下面是一个示例：</p>
<pre class="crayon-plain-tag">;声明使用的日志记录器
[loggers]
keys=root,corelogger
;声明使用的日志处理器
[handlers]
keys=consoleHandler
;声明日志输出格式
[formatters]
keys=defaultFormatter
;每个日志记录器的详细配置：使用什么级别、处理器
[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_corelogger]
level=DEBUG
handlers=consoleHandler
qualname=corelogger
propagate=0

;每个处理器的详细配置：处理器的类（相对logging模块的类名或是全限定类名）、格式以及构造器入参
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=defaultFormatter
args=(sys.stdout,)

[formatter_defaultFormatter]
format=[%(levelname)-7s] [%(process)d-%(threadName)s] %(asctime)s %(module)s.%(funcName)s:%(lineno)d %(message)s
datefmt=%Y-%m-%d %I:%M:%S</pre>
<p>在多模块中使用logging时，典型做法是在main模块中初始化日志配置，其他模块引用Logger对象即可。</p>
<div class="blog_h1"><span class="graybg">常见问题</span></div>
<div class="blog_h2"><span class="graybg">零散问题</span></div>
<div class="blog_h3"><span class="graybg">Python 2.7.6报错：UnicodeDecodeError: utf8 codec can’t decode byte 0xb0 in position 1: invalid start byte</span></div>
<div>这是一个BUG，删除下面三行即可。</div>
<pre class="crayon-plain-tag">ctype = ctype.encode(default_encoding) # omit in 3.x!
except UnicodeEncodeError:
    pass</pre>
<div class="blog_h3"><span class="graybg">运行脚本报错：SyntaxError: Non-ASCII character...</span></div>
<p>类似的报错信息还可能是“but no encoding declared...”。需要指定源代码的编码方式：</p>
<pre class="crayon-plain-tag"># 在源文件开始出添加类似下面的语句
# -*- coding: cp-1252 -*-
# -*- coding: UTF-8 -*-</pre>
<div class="blog_h3"><span class="graybg">easy_install提示：*** is already the active version in easy-install.pth</span></div>
<p>如果安装了两个Python环境A、B，其中A已经安装了软件包P，在运行B的easy_install再次安装P时会出现该提示，并且Pydev不能识别路径匹配的软件包。</p>
<p>打开文件：%PYTHON_B_HOME%/site-packages/easy-install.pth，删除与P相关的行，然后拷贝%PYTHON_A_HOME%/site-packages/P-version.egg到B的对应目录即可。</p>
<div class="blog_h3"><span class="graybg">Python 2.6下pip安装模块时提示：SNIMissingWarning: An HTTPS request has been made, but the SNI ...</span></div>
<p>基于HTTPS访问PyPI时会出现此警告：SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform。</p>
<p>安装模块时还会出现证书错误提示：Certificate did not match expected hostname，导致无法完成安装。</p>
<p>解决办法：<pre class="crayon-plain-tag">pip install pyopenssl ndg-httpsclient pyasn1</pre> </p>
<div class="blog_h3"><span class="graybg">使用pip报错：ImportError: cannot import name 'HTTPSHandler' </span></div>
<p>可能是因为你构建Python时，机器上缺少OpenSSL支持，先安装OpenSSL：</p>
<pre class="crayon-plain-tag"># CentOS
yum install openssl openssl-devel
# Ubuntu
apt-get install openssl libssl-dev</pre>
<p>然后重新构建Python即可。</p>
<div class="blog_h3"><span class="graybg">ValueError: Attempted relative import in non-package</span></div>
<p>考虑如下目录结构：</p>
<pre class="crayon-plain-tag">.
└── myautosizer
    ├── __init__.py
    ├── script.py</pre>
<p>以及Python脚本文件： </p>
<pre class="crayon-plain-tag">all_usrs = []</pre><br />
<pre class="crayon-plain-tag">from . import all_usrs
if __name__ == '__main__':
    pass</pre>
<p>执行<pre class="crayon-plain-tag">python ./myautosizer/scrpit.py</pre> 时就会出现该错误。原因是：相对导入是相对于“当前包”的 ，而直接执行脚本的时，<span style="background-color: #c0c0c0;">不存在当前包的概念</span>。</p>
<p>解决办法：</p>
<ol>
<li>改变调用命令，搜索模块，作为脚本运行：<pre class="crayon-plain-tag">python -m  myautosizer.scrpit</pre> </li>
<li>或者，不使用相对导入，修改导入语句为 <pre class="crayon-plain-tag">from myautosizer.scrpit import all_usrs</pre> </li>
<li>或者，从外部脚本调用script.py，把script.py最为模块看待</li>
</ol>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/python-study-note">Python学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/python-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
