Python 3:强大而灵活的编程语言
一、Python 3 概述
(一)发展历程
Python 的作者是荷兰人 Guido von Rossum,1982 年,Guido 从阿姆斯特丹大学获得了数学和计算机硕士学位。1989 年,为了打发圣诞节假期,Guido 开始写 Python 语言的编译 / 解释器。1991 年,第一个 Python 编译器(同时也是解释器)诞生。今天 Python 已经进入到 3.0 的时代。
Python 3.0 于 2008 年 12 月 3 日发布,此版不完全兼容之前的 Python 源代码。从 Python 3.0 到现在,各个版本不断带来新特性和改进。例如,Python 3.0 中,print 语句变成了一个函数;Python 3.6 中,字典保持插入顺序,引入了 f-strings 等新的字符串格式化语法;Python 3.7 引入了数据类等。
(二)特点与优势
Python 3 具有诸多特点和优势。首先,它简洁易学,有相对较少的关键字,结构简单,语法明确,学习起来更加简单。代码定义清晰,可读性强,使得维护也变得容易。
在跨平台方面表现出色,基于其开放源代码的特性,Python 3 已经被移植到许多平台,在 UNIX、Windows 和 Macintosh 等系统上都能兼容很好。
Python 3 在数据处理方面优势明显,拥有丰富的库如 NumPy、Pandas 和 Matplotlib 等,使得我们可以轻松地进行数值计算、数据可视化和机器学习等任务。在 Web 开发中,也可以使用 Django 或 Flask 等框架编写高效、可扩展的 Web 应用程序。
此外,Python 3 还支持互动模式,可以从终端输入执行代码并获得结果,方便进行测试和调试代码片断。它的可扩展性也很强,若需要关键代码运行更快或有不愿公开的算法,可以使用 C 或 C++ 完成那部分程序,然后从 Python 程序中调用。同时,Python 3 提供所有主要商业数据库的接口,支持 GUI 编程,可嵌入到 C/C++ 程序中,让程序的用户获得 “脚本化” 的能力。
二、常用库介绍
(一)数据分析库
- NumPy:NumPy 是 Python 进行科学计算的基础库。它提供高性能的多维数组对象以及这些数组的操作。可以进行快速的数学运算,如线性代数、傅里叶变换和随机数生成等。在数据分析中,常常用于处理大规模的数值数据。例如,在进行矩阵运算时,NumPy 的效率远高于纯 Python 代码。同时,它还提供了与 C、C++ 和 Fortran 等语言的接口,方便与其他语言编写的程序进行交互。
- Pandas:Pandas 提供了易于使用的数据结构和数据分析工具,特别是 DataFrame 对象,非常适合处理表格数据。它可以方便地进行数据的导入、导出和清洗,支持多种数据格式,如 CSV、Excel、SQL 等。在数据分析过程中,可以使用 Pandas 进行数据的选择、过滤、排序和分组等操作。例如,通过 Pandas 可以轻松地计算数据的均值、中位数、标准差等统计指标。
- Matplotlib:Matplotlib 是强大的 2D 绘图库,能够生成高质量的图表和图形,支持多种图表类型。在数据分析中,数据可视化是非常重要的环节,Matplotlib 可以将数据以直观的方式展示出来,帮助用户更好地理解数据。例如,可以使用 Matplotlib 绘制折线图、柱状图、散点图等,还可以进行图表的自定义,包括颜色、样式、标签等。
(二)机器学习库
- Scikit-learn:Scikit-learn 提供了大量的机器学习算法和工具,支持从数据预处理到模型评估的完整流程。它涵盖了分类、回归、聚类、降维等多种机器学习任务,并且提供了简单易用的 API。例如,对于分类问题,可以使用 Scikit-learn 中的逻辑回归、支持向量机、决策树等算法进行训练和预测。
- TensorFlow:TensorFlow 是谷歌开发的深度学习框架,支持广泛的计算任务,包括神经网络模型的训练和推理。它具有高度的灵活性和可扩展性,可以在不同的硬件平台上运行,如 CPU、GPU 和 TPU。例如,在图像识别、自然语言处理等领域,TensorFlow 被广泛应用。
- PyTorch:PyTorch 是灵活的深度学习框架,提供强大的自动求导系统和动态计算图,易于构建和训练模型。它在学术界和工业界都受到了广泛的关注和使用。例如,在研究新的深度学习算法时,PyTorch 的动态计算图可以方便地进行调试和实验。
(三)Web 开发库
- Flask:Flask 是轻量级的 Web 应用框架,适合快速开发和 RESTful API 设计。它具有简单灵活的特点,开发者可以根据自己的需求进行定制。例如,使用 Flask 可以快速搭建一个小型的 Web 服务,并且可以方便地集成其他库和工具。
- Django:Django 是高级别的 Web 框架,提供了丰富的内置组件和工具,适合开发复杂的 Web 应用。它遵循 MVC 架构模式,提供了数据库管理、用户认证、表单处理等功能。例如,在开发大型的电子商务网站或社交网络平台时,Django 可以提供强大的支持。
- Tornado:Tornado 是一个异步的 Web 框架,具有高性能和可扩展性。它适用于处理大量的并发连接,特别是在实时性要求较高的应用场景中。例如,在开发实时聊天应用、在线游戏等方面,Tornado 可以发挥重要作用。
(四)网络爬虫库
- Requests:Requests 是简单易用的 HTTP 库,用于发送 HTTP 请求和接收响应。它可以方便地获取网页内容,支持各种 HTTP 方法,如 GET、POST、PUT 和 DELETE 等。例如,使用 Requests 可以轻松地从网页上抓取数据。
- Scrapy:Scrapy 是高效的 Web 爬虫框架,支持数据抓取、解析和存储,支持分布式爬虫。它具有强大的功能和灵活性,可以处理复杂的爬虫任务。例如,在大规模的数据采集项目中,Scrapy 可以提高开发效率。
- Beautiful Soup:Beautiful Soup 用于解析 HTML 和 XML 文档的库,将复杂的 HTML 文档转换为易于遍历的树形结构。它可以方便地提取网页中的数据,如标题、链接、文本内容等。例如,在进行网页数据挖掘时,Beautiful Soup 是一个非常有用的工具。
(五)其他常用库
- Pyglet:Pyglet 是一个用于开发多媒体应用的库,支持音频、视频和图形的处理。例如,在开发游戏、动画等方面,Pyglet 可以提供丰富的功能。
- Peewee:Peewee 是一个轻量级的 ORM(对象关系映射)库,用于操作数据库。它提供了简单易用的 API,可以方便地进行数据库的创建、查询、更新和删除等操作。例如,在小型的 Web 应用或数据分析项目中,Peewee 可以简化数据库的操作。
- Bottle:Bottle 是一个微型的 Web 框架,类似于 Flask,但更加简洁。它适合快速开发小型的 Web 应用,并且可以方便地部署。例如,在开发简单的 API 服务或个人博客时,Bottle 是一个不错的选择。
- Invoke:Invoke 是一个任务执行工具,可以方便地定义和执行各种任务。例如,在项目的自动化构建、测试和部署过程中,Invoke 可以提高工作效率。
- Splinter:Splinter 是一个用于自动化测试和 Web 抓取的库。它可以模拟浏览器的操作,如点击、输入、提交等。例如,在进行 Web 应用的自动化测试时,Splinter 可以提供方便的工具。
- Arrow:Arrow 是一个处理日期和时间的库,提供了更加人性化的 API。它可以方便地进行日期和时间的转换、格式化和计算等操作。例如,在处理日志文件、时间序列数据等方面,Arrow 可以提供帮助。
三、编程技巧
(一)使用 Python3 的建议
Python 2 已于 2020 年正式弃用,因此及时升级到 Python 3 是很有必要的。可以在代码中检查所需的最低 Python 版本,以确保代码的兼容性。例如,可以使用以下代码检查版本:
import sys
if not sys.version_info > (2, 7):
# berate your user for running a 10 year python version
elif not sys.version_info >= (3, 5):
# Kindly tell your user (s)he needs to upgrade because you're using 3.5 features
IPython 是升级版的 shell,具有自动补全功能和魔法般的内置命令行,如%cd可修改当前工作路径,%edit可打开编辑器并在关闭时执行代码,%env显示当前环境变量,%pip install [pkgs]可在不离开 Shell 的情况下安装包,%time和%timeit可为 Python 代码计时。安装 IPython 可使用命令pip3 install ipython。
(二)列表表达式
列表表达式的基本语法是[expression for item in list if conditional]。例如,可以生成包含一串数字的列表:mylist = [i for i in range(10)]。还可以在表达式中进行数学运算,如squares = [x**2 for x in range(10)]生成 0 到 9 的平方组成的列表。也可以调用外部函数,如def some_function(a): return (a + 5) / 2,然后my_formula = [some_function(i) for i in range(10)]。最后,可以用if语句进行筛选,如filtered = [i for i in range(20) if i%2==0]只保留偶数。
(三)检查对象内存占用
可以使用sys.getsizeof()来查看对象占用的内存大小。例如,import sys,然后mylist = range(0,10000),print(sys.getsizeof(mylist))会输出 48 字节,这是因为range函数只返回了一个类似列表的类,比直接使用包含一万个数字的列表节省很多空间。如果用列表解析式创建一个同样大小的实际列表myreallist = [x for x in range(0,10000)],再使用print(sys.getsizeof(myreallist))会输出 87632 字节。
(四)返回多个值
Python 的函数可以返回多个值,无需使用字典、列表或类等数据结构。例如def get_user(id): # fetch user from database #.... return name, birthday,然后可以通过name, birthday = get_user(4)获取返回值。但如果返回值超过 3 个,最好放到一个数据类中。
(五)使用数据类
从 Python 3.7 开始提供数据类功能。与常规类或其他替代方法相比,数据类有很多优势。数据类需要的代码量最少,自带__eq__功能可进行对比,自带__repr__功能方便调试,且需要类型提示,可减少 bug。例如:
from dataclasses import dataclass
@dataclass
class Card:
rank: str
suit: str
card = Card("Q", "hearts")
print(card == card) # True
print(card.rank) # 'Q'
print(card)
(六)变量交换技巧
可以使用一个小技巧在位置不变的情况下交换变量,如a = 1,b = 2,然后a, b = b, a,这样就实现了变量a和b的值交换。
(七)合并字典
从 Python 3.5 开始,合并字典变得更容易。例如,dict1 = {'a':1, 'b': 2},dict2 = {'b': 3, 'c': 4},可以使用merged = {**dict1, **dict2}来合并两个字典,结果为{'a': 1, 'b': 3, 'c': 4}。如果有重复的键,第一个字典的这个键对应的值会被覆盖掉。
(八)字符串操作
可以使用title()方法将字符串变成标题形式,非介词的首字母会大写。例如,mystring = "10 awesome python tricks",print(mystring.title())会输出'10 Awesome Python Tricks'。可以按任意字符分割字符串并存入列表,如mystring = "The quick brown fox",mylist = mystring.split(' ')会得到['The', 'quick', 'brown', 'fox']。也可以将列表中的字符串合并为一个字符串,如mylist = ['The', 'quick', 'brown', 'fox'],mystring = " ".join(mylist)会输出'The quick brown fox'。
(九)Emoji 功能
首先安装emoji模块,使用命令pip3 install emoji。安装完后可以像这样使用:import emoji,print(emoji.emojize(':thumbs_up:'))。这个功能在分析社交媒体数据时很有用。
(十)列表切片
列表切片的基本用法是a[start:stop:step],其中Start、Stop和Step都是可选参数。如果没有定义,它们会有默认值:start = 0,end = 方括号里面字符串的最后一个字符,step = 1。可以通过将step设置为-1来翻转字符串和列表。
(十一)展示图片
首先安装Pillow库,这是 Python Image 库的一个分支,使用命令pip3 install Pillow。然后下载图片并命名,例如将图片命名为kittens.jpg。可以使用以下代码展示图片:from PIL import Image,img = Image.open('kittens.jpg'),img.show()。Pillow库还可以对图片进行分析、裁剪、过滤、增强、变形等操作。
(十二)使用 map ()
Python 的内置函数map()的语法是map(function, iterable,...)。可以给它一个函数让其执行,然后传给它对应的参数。例如,def square(x): return x**2,numbers = [1, 2, 3, 4, 5],squared_numbers = list(map(square, numbers)),结果为[1, 4, 9, 16, 25]。
四、易混淆操作与高性能编码调试
(一)易混淆操作
- 随机采样:
-
- 有放回随机采样可以使用random.choice函数多次从给定序列中随机选择一个元素,可能会重复选择同一个元素。
-
- 无放回随机采样可以使用random.sample函数从给定序列中随机选择指定数量的元素,且不会重复选择。
- lambda 函数:
-
- lambda 函数是一种匿名函数,可以快速定义简单的函数。例如lambda x: x**2定义了一个接受一个参数并返回其平方的函数。
-
- lambda 函数的参数可以是多个,例如lambda x, y: x + y定义了一个接受两个参数并返回它们之和的函数。
- 浅拷贝与深拷贝:
-
- copy函数实现浅拷贝,浅拷贝中的元素是原列表中元素的别名,对别名的修改会影响原变量。例如list1 = [1, [2, 3], 4],list2 = list1.copy(),修改list2[1][0]会影响list1。
-
- deepcopy函数实现深拷贝,深层拷贝是递归的进行复制,对深层拷贝的修改不影响原变量。例如使用import copy,list1 = [1, [2, 3], 4],list3 = copy.deepcopy(list1),修改list3[1][0]不会影响list1。
- == 和 is:
-
- ==用于比较两个对象的值是否相等。例如a = [1, 2, 3],b = [1, 2, 3],a == b返回True。
-
- is用于比较两个对象是否是同一个对象。例如a = [1, 2, 3],b = a,a is b返回True,而对于上面的例子a is b返回False。
- 判断类型:
-
- 可以使用isinstance函数判断一个对象是否是某个类型的实例。例如a = 1,isinstance(a, int)返回True。
-
- 也可以使用type函数获取一个对象的类型。例如a = 1,type(a)返回<class 'int'>。
- 字符串搜索:
-
- 可以使用in关键字判断一个字符串是否包含另一个字符串。例如str1 = "hello world","world" in str1返回True。
-
- 也可以使用str.find方法查找一个字符串在另一个字符串中的位置。例如str1 = "hello world",str1.find("world")返回6。
- List 反向索引:
-
- 在 Python 中,可以使用负索引来访问列表中的元素,从列表的末尾开始计数。例如list1 = [1, 2, 3, 4, 5],list1[-1]返回5。
-
- 可以使用负索引来反转列表。例如list1 = [1, 2, 3, 4, 5],list1[::-1]返回[5, 4, 3, 2, 1]。
(二)高性能编码与调试
- 输出错误和警告信息:
-
- 向标准错误输出信息可以使用sys.stderr.write函数。例如import sys,sys.stderr.write("This is an error message")。
-
- 输出警告信息可以使用warnings.warn函数。例如import warnings,warnings.warn("This is a warning message")。
-
- 控制警告消息的输出可以使用warnings.filterwarnings函数。例如import warnings,warnings.filterwarnings("ignore")可以忽略所有警告信息。
- 代码中测试:
-
- 可以使用unittest模块进行单元测试。例如定义一个函数def add(a, b): return a + b,然后编写测试用例import unittest,class TestAdd(unittest.TestCase): def test_add(self): self.assertEqual(add(1, 2), 3)。
- 代码风格检查:
-
- 可以使用pylint进行代码风格和语法检查,能在运行之前发现一些错误信息。例如在命令行中运行pylint your_code.py。
- 代码耗时优化:
-
- 专注于优化产生性能瓶颈的地方,而不是全部代码。可以使用timeit模块来测量代码的执行时间,找到性能瓶颈。
-
- 避免使用全局变量。局部变量的查找比全局变量更快,将全局变量的代码定义在函数中运行通常会快 15%-30%。
-
- 避免使用.访问属性。使用from module import name会更快,将频繁访问的类的成员变量self.member放入到一个局部变量中。
-
- 尽量使用内置数据结构。str, list, set, dict等使用 C 实现,运行起来很快。
-
- 避免创建没有必要的中间变量,和copy.deepcopy()。字符串拼接,例如a + ':' + b + ':' + c会创造大量无用的中间变量,':'.join([a, b, c])效率会高不少。另外需要考虑字符串拼接是否必要,例如print(':'.join([a, b, c]))效率比print(a, b, c, sep=':')低。
(三)其他小技巧
- argmin 和 argmax:
-
- 在使用argmax函数时,比如在深度学习里面计算准确率经常要用到这个参数,这个参数返回的是沿轴axis最大值的索引值。对于numpy和tensorflow用法是一样的,以numpy为例,argmax(a, axis=None, out=None),其中a表示数组,axis表示指定的轴,默认是None,表示把数组平铺,out默认为None,如果指定,那么返回的结果会插入其中。
-
- 例如二维数组情况,a = np.array([[2, 5, 6], [7, 6, 1]]),print(np.argmax(a))输出结果为3,因为a里面7是最大的,如果没有指定axis,默认就是None,相当于把数组平铺为[2, 5, 6, 7, 6, 1],那么结果就是3,因为索引3对应的值最大。
-
- 对于三维数组的情况,b = np.random.randint(20, size=[3, 2, 2]),这个里面,最大的值对应的索引就是np.argmax(b)的结果。经过验证输出就是该最大值对应的索引。
-
- argmin用法相同,只是它是求最小值的索引。
- 转置二维列表:
-
- 需求是转置一个二维数组,将行列互换。例如arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]。
-
- 列表递推式提供了一个简便的矩阵转置的方法:print([[r[col] for r in arr] for col in range(len(arr[0]))]),结果为[[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]。
-
- 另一个更快和高级一些的方法,可以使用zip函数:print(map(list, zip(arr)))。
-
- 在列表递推式版本中,内层递推式表示选择什么(行),外层递推式表示选择者(列)。这个过程完成后就实现了转置。在zip版本中,我们使用arr语法将一维数组传递给zip做为参数,接着,zip返回一个元组做为结果。然后我们对每一个元组使用list方法,产生了列表的列表(即矩阵)。
-
- 因为我们没有直接将zip的结果表示为list,所以我们可以我们可以使用itertools.izip来稍微的提高效率(因为izip并没有将数据在内存中组织为列表)。import itertools,print(map(list, itertools.izip(arr)))。但是,在特定的情况下,上面的方法对效率的微弱提升不能弥补对复杂度的增加。
- 一维列表展开为二维列表:
-
- 要将一维列表变成二维列表,可以通过python中的列表推导式或者循环来实现。例如,给定一个一维列表a,我们可以把它变成一个二维列表b,其中每个子列表包含n个元素,可以这样实现:
-
- 使用列表推导式,n = 3(子列表包含的元素个数),a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],b = [a[i:i + n] for i in range(0, len(a), n)],print(b)。
-
- 使用循环,n = 3(子列表包含的元素个数),a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],b = [],for i in range(0, len(a), n): b.append(a[i:i + n]),print(b)。
-
- 以上代码中,我们把列表a按照每n个元素划分成了多个子列表,然后将这些子列表组成了一个新的二维列表b。这样就完成了一维列表到二维列表的转换。需要注意的是,在列表元素个数不能整除n的情况下,最后一个子列表的元素个数会少于n个。如果要保证每个子列表都包含n个元素,可以在原列表后面补充足够个数的None或者其他特定值,以保证每个子列表都有n个元素。