Python

[TOC] 什么是列表生成式 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。 举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11)): >>> list(range(1, 11)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 但如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么做?方法一是循环: >>> L = [] >>> for x in range(1, 11): ... L.append(x * x) ... >>> L [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 但是循环太繁琐,而列表生成式则可以用一行语句代替循环生成上面的list: >>> [x * x for x in range(1, 11)] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来,十分有用,多写几次,很快就可以熟悉这种语法。 ...

March 22, 2016

Python特性之迭代

[TOC] 迭代 如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。 list和tuple 在Python中,迭代是通过for ... in来完成的,而很多语言比如C语言,迭代list是通过下标完成的,比如Java代码: for (i=0; i<list.length; i++) { n = list[i]; } 可以看出,Python的for循环抽象程度要高于C的for循环,因为Python的for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上。 dict list这种数据类型虽然有下标,但很多其他数据类型是没有下标的,但是,只要是可迭代对象,无论有无下标,都可以迭代,比如dict就可以迭代: >>> d = {'a': 1, 'b': 2, 'c': 3} >>> for key in d: ... print(key) ... a c b 因为dict的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样。 默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()。 由于字符串也是可迭代对象,因此,也可以作用于for循环: >>> for ch in 'ABC': ... print(ch) ... A B C 所以,当我们使用for循环时,只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象究竟是list还是其他数据类型。 那么,如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断: >>> from collections import Iterable >>> isinstance('abc', Iterable) # str是否可迭代 True >>> isinstance([1,2,3], Iterable) # list是否可迭代 True >>> isinstance(123, Iterable) # 整数是否可迭代 False 迭代索引 最后一个小问题,如果要对list实现类似Java那样的下标循环怎么办?Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身: ...

March 22, 2016

Python特性之切片

[TOC] 切片 取一个list或tuple的部分元素是非常常见的操作。比如,一个list如下: >>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] 取前3个元素,应该怎么做? 笨办法: >>> [L[0], L[1], L[2]] ['Michael', 'Sarah', 'Tracy'] 之所以是笨办法是因为扩展一下,取前N个元素就没辙了。 取前N个元素,也就是索引为0-(N-1)的元素,可以用循环: >>> r = [] >>> n = 3 >>> for i in range(n): ... r.append(L[i]) ... >>> r ['Michael', 'Sarah', 'Tracy'] 对这种经常取指定索引范围的操作,用循环十分繁琐,因此,Python提供了切片(Slice)操作符,能大大简化这种操作。 对应上面的问题,取前3个元素,用一行代码就可以完成切片: >>> L[0:3] ['Michael', 'Sarah', 'Tracy'] L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。 如果第一个索引是0,还可以省略: >>> L[:3] ['Michael', 'Sarah', 'Tracy'] 也可以从索引1开始,取出2个元素出来: >>> L[1:3] ['Sarah', 'Tracy'] 类似的,既然Python支持L[-1]取倒数第一个元素,那么它同样支持倒数切片,试试: >>> L[-2:] ['Bob', 'Jack'] >>> L[-2:-1] ['Bob'] 记住倒数第一个元素的索引是-1。 切片操作十分有用。我们先创建一个0-99的数列: >>> L = list(range(100)) >>> L [0, 1, 2, 3, ..., 99] 可以通过切片轻松取出某一段数列。比如前10个数: ...

March 20, 2016

Python函数的参数

[TOC] 函数的参数 定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了。对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解。 Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。 位置参数 我们先写一个计算x^2^的函数: def power(x): return x * x 对于power(x)函数,参数x就是一个位置参数。 当我们调用power函数时,必须传入有且仅有的一个参数x: >>> power(5) 25 >>> power(15) 225 现在,如果我们要计算x^3^怎么办?可以再定义一个power3函数,但是如果要计算x^4^、x^5^……怎么办?我们不可能定义无限多个函数。 你也许想到了,可以把power(x)修改为power(x, n),用来计算x^n^,说干就干: def power(x, n): s = 1 while n > 0: n = n - 1 s = s * x return s 对于这个修改后的power(x, n)函数,可以计算任意n次方: >>> power(5, 2) 25 >>> power(5, 3) 125 修改后的power(x, n)函数有两个参数:x和n,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数x和n。 默认参数 新的power(x, n)函数定义没有问题,但是,旧的调用代码失败了,原因是我们增加了一个参数,导致旧的代码因为缺少一个参数而无法正常调用: >>> power(5) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: power() missing 1 required positional argument: 'n' Python的错误信息很明确:调用函数power()缺少了一个位置参数n。 ...

March 16, 2016

Python函数的使用

[TOC] 调用函数 Python内置了很多有用的函数,我们可以直接调用。 要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数abs,只有一个参数。可以直接从Python的官方网站查看文档: http://docs.python.org/3/library/functions.html#abs 也可以在交互式命令行通过help(abs)查看abs函数的帮助信息。 调用abs函数: >>> abs(100) 100 >>> abs(-20) 20 >>> abs(12.34) 12.34 调用函数的时候,如果传入的参数数量不对,会报TypeError的错误,并且Python会明确地告诉你:abs()有且仅有1个参数,但给出了两个: >>> abs(1, 2) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: abs() takes exactly one argument (2 given) 如果传入的参数数量是对的,但参数类型不能被函数所接受,也会报TypeError的错误,并且给出错误信息:str是错误的参数类型: >>> abs('a') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: bad operand type for abs(): 'str' 而max函数max()可以接收任意多个参数,并返回最大的那个: >>> max(1, 2) 2 >>> max(2, 3, 1, -5) 3 ###数据类型转换 ...

March 14, 2016

CentOS安装Docker

[TOC] Docker 安装 docker 简单又实用,一起来学习吧.现在官方也给出了比较全面(各种系统各种版本)的安装方法.链接在此.下面简单记录一下centos下的安装. 卸载旧版本 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-selinux \ docker-engine-selinux \ docker-engine yum源安装 通过添加docker仓库,只要有网在哪里都可以下载.有版本更新也可以直接一条命令解决. 安装依赖包 sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2 添加docker仓库 sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo 一键安装 sudo yum install docker-ce sudo usermod -aG docker your-user #普通用户要使用docker需要添加到docker组 启动docker sudo systemctl start docker docker version #查看版本 rpm包安装 下载好的rpm安装快速,可离线安装. sudo yum install /path/to/package.rpm sudo usermod -aG docker your-user #普通用户要使用docker需要添加到docker组 sudo systemctl start docker docker version #查看版本 脚本安装 curl -fsSL get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker your-user #普通用户要使用docker需要添加到docker组 sudo systemctl start docker docker version #查看版本 镜像加速器 在国内下载docker镜像很可能会很慢,甚至有的都不能下载.使用加速器将会提升在国内获取Docker官方镜像的速度.其实就是阿里等先把官方的镜像下载到自己的机房,定时更新然后做成一个仓库站点,供国内使用,所以要快很多.添加方法很简单,通过修改daemon配置文件/etc/docker/daemon.json来使用加速器. ...

March 13, 2016

Python字典dict和集set

[TOC] dict Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 举个例子,假设要根据同学的名字查找对应的成绩,如果用list实现,需要两个list: names = ['Michael', 'Bob', 'Tracy'] scores = [95, 75, 85] 给定一个名字,要查找对应的成绩,就先要在names中找到对应的位置,再从scores取出对应的成绩,list越长,耗时越长。 如果用dict实现,只需要一个“名字”-“成绩”的对照表,直接根据名字查找成绩,无论这个表有多大,查找速度都不会变慢。用Python写一个dict如下: >>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} >>> d['Michael'] 95 为什么dict查找速度这么快?因为dict的实现原理和查字典是一样的。假设字典包含了1万个汉字,我们要查某一个字,一个办法是把字典从第一页往后翻,直到找到我们想要的字为止,这种方法就是在list中查找元素的方法,list越大,查找越慢。 第二种方法是先在字典的索引表里(比如部首表)查这个字对应的页码,然后直接翻到该页,找到这个字。无论找哪个字,这种查找速度都非常快,不会随着字典大小的增加而变慢。 dict就是第二种实现方式,给定一个名字,比如'Michael',dict在内部就可以直接计算出Michael对应的存放成绩的“页码”,也就是95这个数字存放的内存地址,直接取出来,所以速度非常快。 你可以猜到,这种key-value存储方式,在放进去的时候,必须根据key算出value的存放位置,这样,取的时候才能根据key直接拿到value。 把数据放入dict的方法,除了初始化时指定外,还可以通过key放入: >>> d['Adam'] = 67 >>> d['Adam'] 67 由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉: >>> d['Jack'] = 90 >>> d['Jack'] 90 >>> d['Jack'] = 88 >>> d['Jack'] 88 如果key不存在,dict就会报错: >>> d['Thomas'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'Thomas' 要避免key不存在的错误,有两种办法,一是通过in判断key是否存在: ...

March 2, 2016

Python判断和循环

[TOC] 条件判断 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断。 比如,输入用户年龄,根据年龄打印不同的内容,在Python程序中,用if语句实现: age = 20 if age >= 18: print('your age is', age) print('adult') 根据Python的缩进规则,如果if语句判断是True,就把缩进的两行print语句执行了,否则,什么也不做。 也可以给if添加一个else语句,意思是,如果if判断是False,不要执行if的内容,去把else执行了: age = 3 if age >= 18: print('your age is', age) print('adult') else: print('your age is', age) print('teenager') 注意不要少写了冒号:。 当然上面的判断是很粗略的,完全可以用elif做更细致的判断: age = 3 if age >= 18: print('adult') elif age >= 6: print('teenager') else: print('kid') elif是else if的缩写,完全可以有多个elif,所以if语句的完整形式就是: if <条件判断1>: <执行1> elif <条件判断2>: <执行2> elif <条件判断3>: <执行3> else: <执行4> if语句执行有个特点,它是从上往下判断,如果在某个判断上是True,把该判断对应的语句执行后,就忽略掉剩下的elif和else,所以,请测试并解释为什么下面的程序打印的是teenager: age = 20 if age >= 6: print('teenager') elif age >= 18: print('adult') else: print('kid') if判断条件还可以简写,比如写: ...

February 28, 2016

Python列表list和元组tuple

[TOC] list Python内置的一种数据类型是列表:list。list是一种有序的集合,可以随时添加和删除其中的元素。 比如,列出班里所有同学的名字,就可以用一个list表示: >>> classmates = ['Michael', 'Bob', 'Tracy'] >>> classmates ['Michael', 'Bob', 'Tracy'] 变量classmates就是一个list。用len()函数可以获得list元素的个数: >>> len(classmates) 3 用索引来访问list中每一个位置的元素,记得索引是从0开始的: >>> classmates[0] 'Michael' >>> classmates[1] 'Bob' >>> classmates[2] 'Tracy' >>> classmates[3] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list index out of range 当索引超出了范围时,Python会报一个IndexError错误,所以,要确保索引不要越界,记得最后一个元素的索引是len(classmates) - 1。 如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素: >>> classmates[-1] 'Tracy' 以此类推,可以获取倒数第2个、倒数第3个: >>> classmates[-2] 'Bob' >>> classmates[-3] 'Michael' >>> classmates[-4] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list index out of range 当然,倒数第4个就越界了。 ...

February 27, 2016

Python字符串和编码

[TOC] 字符编码 我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题。 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是65535,4个字节可以表示的最大整数是4294967295。 由于计算机是美国人发明的,因此,最早只有127个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。 但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。 你可以想得到的是,全世界有上百种语言,日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里,各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。 因此,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。 Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。 现在,捋一捋ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节。 字母A用ASCII编码是十进制的65,二进制的01000001; 字符0用ASCII编码是十进制的48,二进制的00110000,注意字符'0'和整数0是不同的; 汉字中已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101。 你可以猜测,如果把ASCII编码的A用Unicode编码,只需要在前面补0就可以,因此,A的Unicode编码是00000000 01000001。 新的问题又出现了:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。 所以,本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间: 字符 ASCII Unicode UTF-8 A 01000001 00000000 01000001 01000001 中 x 01001110 00101101 11100100 10111000 10101101 从上面的表格还可以发现,UTF-8编码有一个额外的好处,就是ASCII编码实际上可以被看成是UTF-8编码的一部分,所以,大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。 搞清楚了ASCII、Unicode和UTF-8的关系,我们就可以总结一下现在计算机系统通用的字符编码工作方式: 在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。 用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件: 浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器: 所以你看到很多网页的源码上会有类似<meta charset="UTF-8" />的信息,表示该网页正是用的UTF-8编码。 Python的字符串 搞清楚了令人头疼的字符编码问题后,我们再来研究Python的字符串。 在最新的Python 3版本中,字符串是以Unicode编码的,也就是说,Python的字符串支持多语言,例如: >>> print('包含中文的str') 包含中文的str 对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符: >>> ord('A') 65 >>> chr(66) 'B' #注意下面两个在jupyter notebook中会报错,但是在python命令行中却不会错, 看看你的locale charmap是否为utf-8 >>> ord('中') 20013 >>> chr(25991) '文' 如果知道字符的整数编码,还可以用十六进制这么写str: ...

February 26, 2016