插入排序算法解析

前面说过最原始的复杂度为 O(n2) 的冒泡和选择排序,也跳跃到了复杂度为  O(n log n) 的快速排序,现在又再看一个复杂度同样为 O(n2) 的插入排序。从排序名称结合代码我们理解了为什么叫做冒泡或是选择,快速排序自认高名,那么何以这又谓之插入排序呢?是怎么插入,从左边往右边插,还是从右边往左边插,这得搞清它的排序原理:

它在列表较低的一端维护一个有序的子列表(从最左端一个元素开始),并逐个将每个新元素(高端的)"插入"这个子列表。插入的时候遍历低端列表,找准位置插入便是,插入点后的元素需后移,当所有高端的元素插入完成了,整个列表就变得有序了。

整个排序操作示意图如下: 阅读全文 >>

理解 Python 类的变量,方法与属性

熟悉了传统的 C++/Java 类定义的风格,来感受一下 Python 是如何定义类的。本篇是阅读 《The Quick Python Book》第二版关于类定义的笔记,由原书内容进一步引申,不过是依照本人的思考顺序来组织的。在理解 Python 类定义的同时头脑中应该闪现出 JavaScript/Java 如何定义类的情景。

最简单的类定义

class MyClass:
    pass

由于 class MyClass 后面要有个冒号,而冒号后总得有点东西才能表示该类定义结束了,于是放个 pass 当占位符。Python 也像 Java 一样,有一个根类,叫做 object,例如上面的定义

我们能看到它隐式的基类是 object, 而不用显式的声明为 class MyClass(object)。看到 __bases__ 属性是一个 Tuple, 意识到  Python 是支持多重继承的。 阅读全文 >>

Python 函数声明先后顺序的问题

在 Python 中如果把函数定义写在调用的下方可能会出错,例如下面的代码

foo()

def foo():
    print("hello")

执行时会报出错误

NameError: name 'foo' is not defined

这时候要把 foo() 调用代码放到该函数的声明后面

def foo():
    print("hello")

foo()

这样执行就一切正常了。这仿佛像是 C 语言中的函数调用需要提前声明一般,例如在 C 语言中要调用后头的定义的函数要写成 阅读全文 >>

开启 Python 组件 Boto3 在 IDE 中的智能提示

在用 Python 编写 AWS 服务时,要用到 Boto 3 组件,而像 boto3.client('s3') 获得的对象只能被 IDE 识别为一个 BaseClient, 具体包含什么操作方法是在运行时由参数 s3 指示的基于 JSON 文件所描述的。因此 IDE 对 s3 = boto3.client('s3')s3 对象无法提供有效的智能提示,每次用 Boto 3 时不得不打开 Boto 3 的在线 API 文档来对照。长此以往,总觉麻烦且效率低下,有种一直摸着石头过河的感觉。那么,是否有办法让 IDE 智能提示出各种 boto3.client('<service>') 的实际操作呢?网上找了找,确实有这个需求,解决办法有

  1. botostubs: 与 boto3 API 保持更新(每三天),并支持众多 IDE, 试过在 IntelliJ IDEA 和 Visual Studio Code 中可用
  2. pyboto3: 上次更新在两年前, https://github.com/wavycloud/pyboto3, 只在 Python 2.7 下测试过
  3. autoboto: 需有智能提示,但改变了应用 Boto 3 组件的方式,不建议使用

本文重点推荐 botostubs, 下面会叙说具体理由,在进入正是之前,不妨来回顾一下直接使用 Boto 3 时没有好的智能提示的问题 阅读全文 >>

让 Python 的数据库查询返回字典记录

在使用  Python 进行数据库查询,通常情况下 cursor 的 fetchall, fetchmany 返回的是元组(Tuple) 的列表,所以对查询到的结果只能用索引下标来访问,而无法通过字段名来获取值。对 Java JDBC 的 ResultSet 操作,我们有两种获取值的方式,resultSet.getString(1) 和 resultSet.getString('name')。

其实只要能用数字索引访问到字段值也就足够了,查询后字段名可以由 cursor.description 获得。通过字段名来访问值唯一的好处估计是出错的概率小些罢了,比如 result['firstname'], result['lastname'] 总是比 result[1], result[2] 更不容易搞混,错误定位也会更轻松。

假如有下面的数据库表与两条记录 阅读全文 >>

Python 版的 try-with-resources -- with 上下文管理器

作为一个  Java 为母语的程序员来讲,学习起其他新的语言就难免任何事都与 Java 进行横向对比。Java 7 引入了能省去许多重复代码的 try-with-resources 特性,不用每回 try/finally 来释放资源(不便之处有局部变量必须声明在  try 之前,finally 里还要嵌套 try/catch 来处理异常)。比如下面的  Java 代码

try(InputStream inputStream = new FileInputStream("abc.txt")) {
    System.out.println(inputStream.read());
} catch (Exception ex) {
}

它相应的不使用 try-with-resources 语法的代码就是 阅读全文 >>

用 .pth 文件附加 Python 模块搜索路径

上一篇 Python 的模块搜索路径,介绍了 Python 的模块搜索路径,最终起作用的是 sys.path 路径列表。如果要自定义自己的搜索路径,就是要怎么定制 sys.path 的内容。可以简单的用 PYTHONPATH 环境变量前向添加,这儿将要说的是用 .pth 文件的方式。也可由此进一步理解 Python 依赖管理工具,像  virtualenv 等的工作原理。

.pth 文件名是什么,无所谓,Python 只认扩展名。.pth 文件中每行指定一个路径 -- 绝对或相对路径(相对于本  .pth 文件所在的目录),另外还可以空行或 # 开始的注释行,还能有 import 语句,大概只用来校验是否能导入成功,程序代码中还是需要显示的 import 模块。

.pth 文件放在哪里

.pth 文件创建好后应该放到哪里去呢?不是 sys.prefix 指示的位置,也不是 sys.path 中任意一个目录,而是  sys.path 中属于 site.packages 的某一个目录中。可以用

>>> import site
>>> site.getusersitepackages()
>>> site.getsitepackages()

查看到, 看我在  Ubuntu Linux 中看到的内容(为便于阅读,显示列表内容时进行了换行处理) 阅读全文 >>

Python 的模块搜索路径

一种语言要使用到外部库(模块) 时必然会涉及到从哪里以及按何顺序加载依赖,就像 LD_LIBRARY_PATH, CLASSPATH 那样,Python 也有其默认的模块搜索顺序, 依序找到想要的模块即停止。Python 中 sys.path 返回的列表包含了模块搜索的顺序,我们可以程序中修改该列表,或用 PYTHONPATH 环境变量前插路径,甚至是用  .pth 文件来附加路径。

简单的,可以执行命令 python3 -c "import sys; print(str(sys.path).replace(',', '\n'))" 来查看 python3 交互 shell 下的模块搜索路径,类似结果如下:

[''
'/usr/lib/python36.zip'
'/usr/lib/python3.6'
'/usr/lib/python3.6/lib-dynload'
'/home/yanbin/.local/lib/python3.6/site-packages'
'/usr/local/lib/python3.6/dist-packages'
'/usr/lib/python3/dist-packages'
'/usr/lib/python3.6/dist-packages']

注意,第一个元素是个空字符串,代表进入 python3 shell 时的当前目录。

如果在通过一个 py 脚本文件来打印 sys.path 的话显示稍微有所差异。比如在目录 /home/yanbin/Developers/ 下创建 test.py 文件,内容为 阅读全文 >>

Python 的 __str__ 和 __repr__ 方法比较

阅读到 Strings 中关于转换对象为字符串的内容,介绍了 repr 函数,趁着还没有真正了解 Python 面向对象的生疏与热度,感性上理解一下 repr 与 str 这两个函数的区别。

Python 的全局方法 repr 和 str 会映射到对象的 __repr__ 和 __str__ 的方法调用,还有 str(obj) 时会调用哪个方法,以及 print(obj) 和调试 Python 代码时的对象显示会调用哪个方法呢?这就是本文想要印证的内容。

恰如 Java 的 System.out.println(obj) 或 "hello" + obj 都会调用 Java 对象的 toString() 方法,那么 Python 中是怎么一回事呢?

来自某本 Python 入门书的解释 repr 和 str:

  1. repr: formal string representation of a Python object
  2. str: informal string representation of a Python object,或者说 printable string representation

首先 repr 是 representation 的意思,一个是正式,另一个是非正式,看起来 repr 比 str 显得重要些。 阅读全文 >>

我的 Python 快速入门

本文只是我个人的掌握 Python 的快速入门笔记, 所以混乱不堪, 并不适合于每一个想要学习 Python 的读者

Python 命令进到它的 shell, ctrl+d 或 exit() 退出 python. help(str) 可以查看 str 函数的帮助, q 退出帮助. 对象的方法可用 dir 来查看, dir([]), dir(""), 进而 help([].append), help(dir([]))

Python 是用严格的缩进来格式化代码块的, Google 的 Python 代码规范是用 4 个空格来缩进. Google 建议 Java 是用两个空格.

Python 是动态类型的, 所以可以 a = 1; a = "string" 随意赋值为不同类型. Python 也能用分号把多条语句写在同一行里, 但基本没人用分号的.
Python 的基本类型有 整数, 长整数, 浮点数和复数, 以及字符串

字符串可以用单引号和双引号, 它们像 Javascript, 是完全一样的
''' 或 "”” 三引号的字符串是 here doc, 多行字符串
转义符也是用 \, 如 ''What\’s your name\n?
自然字符串: 即不转义, 用 R 或 r 来指定, 如 r"Newlines are indicated by \n”, 会输出 "\n"" 字面值. 可用于书写正则表达式
放在一起的字符串就会被 Python 自动连接, 如 print ''What\'s' ''your name?’, 输出为 "What’s your name?”

Python 的命名规则有几个必须知道的: 类名和 Java 一样; 模块, 方法, 变量名用小写字母下划线分隔, 常量用大写加下划线. 单或双下划线开头是特殊用途. 命名规则请参考 Google Python Style Guide#Naming
Python 是纯面向对象的, 任何东西都是对象, 函数也是

Python 可以用 \ 来连接语句行, 像 Bash 一样, 如 阅读全文 >>