关于 python 的 __init__.py 最清楚的介绍

在写 python package 的时候,不可避免的和 __init__.py 打交道,查了很多资料,发觉下面的文章是介绍的最清楚的,来源 老王的python ,我重新整理了一下格式:

python 中的 Module 是比较重要的概念。常见的情况是,事先写好一个.py 文件,在另一个文件中需要 import 时,将事先写好的 .py 文件拷贝 到当前目录,或者是在 sys.path 中增加事先写好的 .py 文件所在的目录,然后 import。这样的做法,对于少数文件是可行的,但如果程序数目很多,层级很复杂,就很吃力了。

有没有办法,像 Java 的 Package 一样,将多个 .py 文件组织起来,以便在外部统一调用,和在内部互相调用呢?答案是有的。

主要是用到 python 的包的概念,python __init__.py 在包里起一个比较重要的作用。

要弄明白这个问题,首先要知道,python 在执行 import 语句时,到底进行了什么操作,按照 python 的文档,它执行了如下操作:

第1步,创建一个新的,空的 module 对象(它可能包含多个 module );
第2步,把这个 module 对象插入 sys.module 中
第3步,装载 module 的代码(如果需要,首先必须编译)
第4步,执行新的 module 中对应的代码。

在执行第3步时,首先要找到 module 程序所在的位置,其原理为:

如果需要导入的 module 的名字是 m1,则解释器必须找到 m1.py,它首先在当前目录查找,然后是在环境变量 PYTHONPATH 中查找。PYTHONPATH 可以视为系统的 PATH 变量一类的东西,其中包含若干个目录。如果 PYTHONPATH 没有设定,或者找不到 m1.py,则继续搜索 与 python 的安装设置相关的默认路径,在 Unix下,通常是 /usr/local/lib/python。

事实上,搜索的顺序是:当前路径 (以及从当前目录指定的 sys.path),然后是 PYTHONPATH,然后是 python的安装设置相关的默认路径。正因为存在这样的顺序,如果当前 路径或 PYTHONPATH 中存在与标准 module 同样的 module,则会覆盖标准 module。也就是说,如果当前目录下存在 xml.py,那么执行 import xml 时,导入的是当前目录下的 module,而不是系统标准的xml。

了解了这些,我们就可以先构建一个 package,以普通 module 的方式导入,就可以直接访问此 package 中的各个module 了。

Python 中的 package 定义很简单,其层次结构与程序所在目录的层次结构相同,这一点与 Java 类似,唯一不同的地方在于,python 中的 package 必须包含一个 __init__.py 的文件。
例如,我们可以这样组织一个 package:

package1/
__init__.py
subPack1/
__init__.py
module_11.py
module_12.py
module_13.py
subPack2/
__init__.py
module_21.py
module_22.py
……

__init__.py 可以为空,只要它存在,就表明此目录应被作为一个 package 处理。当然,__init__.py 中也可以设置相应的内容,下文详细介绍。

好了,现在我们在module_11.py中定义一个函数:

def funA():
    print "funcA in module_11"
    return

 

在顶层目录(也就是 package1 所在的目录,当然也参考上面的介绍,将 package1 放在解释器能够搜索到的地方)运行python:

>>>from package1.subPack1.module_11 import funcA
>>>funcA()
funcA in module_11

 

这样,我们就按照package的层次关系,正确调用了module_11中的函数。

细心的用户会发现,有时在 import 语句中会出现通配符 *,导入某个 module 中的所有元素,这是怎么实现的呢?
答案就在 __init__.py 中。我们在 ubPack1 的 __init__.py 文件中写

__all__ = ['module_13', 'module_12']

 

然后进入python

>>>from package1.subPack1 import *
>>>module_11.funcA()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named module_11

 

也就是说,以 * 导入时,package 内的 module 是受 __init__.py 限制的。

好了,最后来看看,如何在 package 内部互相调用。
如果希望调用同一个 package 中的 module,则直接 import 即可。也就是说,在 module_12.py 中,可以直接使用

import module_11

 

如果不在同一个 package 中,例如我们希望在 module_21.py 中调用 module_11.py 中的 FuncA,则应该这样:

from module_11 import funcA

 

mac osx 下建立 python package 以及在github 上开源项目的操作过程

怎么建立一个 python 包,并且发布到 pypi,有很多文章介绍了。同样,怎么在 github 建立一个开源项目,也不难。

把这两个事情一起做,加上我用 pycharm 来辅助完成 python 包的建立,以及用 source tree 来管理 github 项目,事情可就不少了。

小结一下经验体会:

1 建立 python 包,网上大部分教程没什么问题,如果用 pycharm 会更加容易一些,在菜单 Tools 中,会看到 Create setup.py,pycharm 会做一些目录结构调整、生成最基本的需要的文件,以及输入版本作者之类的事情。

2 文件结构转换为包的形式后,pycharm 很聪明的会在菜单 Tools 中,增加一个 Run setup.py Task。可以节约一些工作量,比如 sdist,这是必须执行的,upload 上传也是必须执行的。不过这里有一个坑,在 sdist 执行好之后,就可以选择 install 命令安装自己的包,然后在程序里面 import 什么都可以了,但是如果这时候你想上传 pypi 可能会报错:

error: no dist file created in earlier command

 

当然,肯定已经在 pypi 注册过用户了,查了不少资料,原来是要先这样操作:

python setup.py sdist upload

 

没有在 pycharm 中找到相应的命令,在终端模式下输入吧。

然后就可以 upload 了。所以,如果你 upload 到 pypi 同样碰到上面的错误,不妨试试看先这样操作一下。

来源这个老外的博客文章,写的很有趣也很清楚。

补充:直接执行上面的命令,应该是连 sdist 和 upload 一起都运行了,这样是最方便的。

running sdist
running check
warning: sdist: manifest template 'MANIFEST.in' does not exist (using default file list)

writing manifest file 'MANIFEST'
creating fish_base-1.0.2
creating fish_base-1.0.2/fish_base
making hard links in fish_base-1.0.2...
hard linking README.txt -> fish_base-1.0.2
hard linking setup.py -> fish_base-1.0.2
hard linking fish_base/__init__.py -> fish_base-1.0.2/fish_base
Creating tar archive
removing 'fish_base-1.0.2' (and everything under it)
running upload
Submitting dist/fish_base-1.0.2.tar.gz to https://pypi.python.org/pypi
HTTP Error 500: Internal Server Error
error: HTTP Error 500: Internal Server Error

 

看这里的操作,当 upload 后,显示了错误的信息,但是到 pypi 上看,已经传上去了,略有奇怪,可能这里的网络不稳定。

3 github 中碰到的问题,就是 https 模式下,用户名密码始终通不过验证,然后换成用 ssh 证书模式,按照 github 上的操作是最靠谱的,并且他们区分了不同操作系统,个人私钥的文件名一定要叫 id_rsa,反正我这里不叫这个就是通不过。
严格按照 github 上的步骤,以及测试 ssh 是否通顺的方法,如果不能通过,肯定是不能用的。

4 在项目的 .git 目录下,有一个 config 文件,可以修改其中的 https 模式到 ssh 模式。

5 osx EI Capitan 在 Finder 中显示隐藏信息的方法和之前版本有所改变:

defaults write com.apple.finder AppleShowAllFiles -bool true
killall Finder  

 

关键是后面那句。

source tree 作为一个免费强大的版本管理软件,没什么好说的,使用起来也很方便。

好了,我要修改代码了,为了试验,所传的项目代码都很烂,需要好好修改一下。

python 3.x 下载说明以及自带编辑器 IDLE 使用

首先到 python 官网,找到下载链接,目前最新版本是 3.5.1,不建议再使用 2.x 系列了。从 download 链接看到下面这个:

python351_install_start

windows 操作系统只需要下载32位版本即可,对于 python 的学习不会有任何影响,mac 电脑下载对应的最新版本就可以了。

以 windows 操作系统举例,下载后,执行安装程序,如下:

python351_install_start

一般情况下不用做任何修改,几分钟就安装好了,如下:

python351_install_finish

安装好后,在开始菜单找到 python 3.5 程序组,看到其中的 IDLE(Python 3.5 32bit),运行后就会出现下面的 python shell:

python_IDLE

可以在这里输入简单的 python 程序,正确的话,会执行,错误的话,会报错。

python_IDLE_run

不过在这个 shell 里面编辑稍微长一点的 python 程序会比较麻烦,所以,可以在 shell 的 File 菜单里面选择第一项 New File,这时候,一个编辑器窗口就出现了。

在编辑器里可以编写比较长的程序,选择菜单 Run 里面的 Run Module,就可以运行程序,结果会显示在一个 shell 窗口中,就像下面这样,注意在运行程序前是需要保存程序文件的:

python_IDLE_edit

python 作为目前几乎是最流行的脚本语言,有各种各样非常多的开发环境,以及我自己非常喜欢的 juypter(ipython),不过其自带 IDLE 和 python shell 对于初学者来说,已经足够了。

为了一些梦想

今天和一位东方财富网的朋友聊到编程语言,原来他以前也是用 delphi 的,我们都感叹道 delphi 的没落。

如今,我已经是 python 的忠实粉丝了。

一些机缘巧合,现在开始教一些小孩子 python,的确作为入门语言,python 非常不错,相对很容易教。

当然,python 也是入门容易精通难,自己也是觉得越学习越觉得不懂,但是这样的感觉很棒,值得努力,到什么高度是一回事情,只要登高即可。

于是对于 python 的学习,就落实到了两个方面,普及和钻研,会逐渐把自己在教小孩子的过程中整理的讲义、各类举例整理出来,分享给大家。而在公司中,随着 python 也用的越来越多,相对复杂的编程技术。

十多年来,一直努力做着这样 knowlege transfer 的事情,之前的理想空间计划,现在的 python教育,或许这是一个不那么实际的梦想,但是我喜欢,有家人朋友支持,也很开心。

情怀

这两年,最用情怀作为卖点的就是锤子了。

有时候想想自己,说来有趣,我从小最计较的情怀就是编程,在很长的时间里,这个几乎就是我的第一爱好了。

除了一开始工作的几年,其实我的工作也基本上可以不编程序,但是总舍不得这个情怀。

rpgmaker mv

从盗版用到正版,其实也不在乎真的靠这个赚钱之类,这就是一种情怀吧,

去年开始学习 python,其实也是放弃了跟随多年的 delphi,这个过程其实也挺难受的,如人生,有舍有得,那么多东西装在心里,怎么容得下新事物呢。

虽然有时候选择的过程是有点痛苦的,也还是充满了未知。

好在,这几年明白,世界是多元化的,人生不是只有左右。

Python中使用 sqlalchemy 访问数据库和简单操作

在我们的数据仓库设计中,python 是我们用来连接各个数据库和应用逻辑的纽带。python 几乎无所不能,反正性能上有要求的就交给 java,要快速开发的逻辑多变的就交给 python。

python 中访问数据库最有名的就是 sqlalchemy 包了。

from sqlalchemy import create_engine, MetaData, Table
from sqlalchemy.sql import select, insert

engine = create_engine('mysql+pymysql://macmini:***@192.168.199.***:3306/test?charset=utf8mb4', pool_recycle=3600)
meta = MetaData(bind=engine)
students = Table("students", meta, autoload=True, autoload_with=engine)

conn = engine.connect()

s = select([students])
result = conn.execute(s)

print(s)

for row in result:
    print(row)

 

通过 sqlalchemy 访问 mysql 数据库,需要先创建一个 engine 对象,输入 mysql 的 ip 地址和用户名密码。后面的就简单了。

sqlalchemy 访问数据库可以用 orm 或者称之为 Expression Language 的方法,上面例子里面的 select 方法就是属于 sqlaclchemy 的快速语言方法,s 变量是用来检验的生成的 sql 语句,

看一下结果就很明白了,列出了sql语句(一般是不需要的),以及 select 的查询结果:

SELECT students.id, students.name, students.sex, students.age, students.tel
FROM students

(1, ‘王刚’, ‘男’, 20, ‘13811371377’)
(2, ‘张三’, ‘男’, 30, ‘16512341234’)

下面则是 insert 的用法,是不是非常简洁,这样的写法避免了 sql 语句的啰嗦,抽象起来,就可以用在业务逻辑复杂多变的场景了,即便是所谓写死的业务逻辑,这样也比较容易看懂,而一般的 sql 语句非常不容易调试:

conn.execute(students.insert(), [
    {'name' : '张三', 'sex' : '男', 'age' : 30, 'tel' : '16512341234'},
    ])

 

PyCharm 开发环境中出现中文显示不正常怎么办

说明一下,不是说python程序代码中文显示为,也不是说python程序怎么输出中文,而是 pycharm,这个开发神器有时候也会莫名其妙的让开发环境 ide 里面的中文显示不正常,比如中文的文件名,或者 git 提交的中文注释,都变成了一个个框。

我再mac 下用新版本的 pycharm 5.0.3 ,一切正常,但是到了windows 下,就出现了上面说的问题。解决方法就是到 Settings-Appearance,下面有一个字体选择,修改为 微软雅黑,肯定就没事了,ide 会提示这是覆盖了默认的字体什么的,也不用管了。

好了,中文文件名等都正常了。和 utf-8 之类没有关系。

python和数据库学习笔记-在mac下配置python和mysql开发环境

一个小项目结束,于是可以继续研究。现在研究的课题和大数据有关,数据仓库目前是在vertica上面,然后计划用公有云的方案来处理计算能力的弹性问题,也就是将部分数据从vertica导入到hive+spark中,方案还是庞大复杂的,所以除了设计整个架构以外,也继续要考虑应用端的很多实践。和spark打交道的事情就交给java了,应用层交给php+js,中间最复杂的业务逻辑自然是我现在最为推崇的python了。

好,慢慢开始学习。

任务:

  1. 在mac上搭建一个vmware上的ubuntu虚拟机,安装mysql,这样从mac的开发环境访问,比较接近真实情况
  2. python在mac上设置为可以访问mysql,且不需要安装mysql

安装vmware,我是在 osx 10.11.2 上安装的 vmware 8.0,从 ubuntu 官网上下载的 ubuntu 15.04 desktop 版本,选择 desktop 版本其实作为 server 是不太好的,不过我对linux 的熟悉程度还不够,所以考虑有图形界面比较容易使用。

在虚拟机里面安装 ubuntu 还是比较容易的,然后安装 ubuntu 后再在命令上下安装 mysql:

sudo apt-get mysql-server

 

很多google上的资料说要注意 mysql 的 root 密码之类,我在安装的时候,mysql 是主动提醒要修改 root 密码的。我安装的 mysql 版本是 5.6.27。

在 ubuntu 下比较好用的 mysql 的图形管理客户端是官方的免费的 MySQL Workbench,我安装的版本是 6.2。用这个图形客户端设置用户名、数据库之类都非常方便。

mysql 安装后在默认情况下的确不允许其他电脑连接,这个弄了我很久,很多资料都说的不对,我觉得权限给的很高也不合适,很多资料说到的 bind ip 的修改,我发现是在 /etc/mysql/mysql.conf.d/mysqld.cnf 中修改,去掉 bindip = 127.0.0.1 的设置。

在 vmware 的虚拟机上安装好 ubuntu+mysql 之后,开始在mac 开发环境连接,同样是安装 MySQL Workbench 的 mac 版本,然后就可以测试连接之类,这个方法最方便实用。

这样,比起 localhost 当然是麻烦一点,主要是为之后多环境做好一些准备(开发环境、测试环境、准生产环境和生产环境)。在 mac 上用 xampp 也是不错的,改天再测试,更加适合笔记本电脑。

对于 python 怎么连接 mysql,发现 python 世界真的发展好快,方法很多。

因为准备之后要用 petl 来进行数据库的抽取和处理,所以按照 petl 的推荐,使用 pymysql 这个库。安装用标准的 pip 即可,我用的是conda,方法一样。

mac 开发环境下是不需要安装整个 mysql server 的,但是还是要安装一些驱动的,安装 mysql 标准的 connector,参考这里,下载后,在 mac 这里是一个 dmg 安装包,安装后,可以理解为 mysql 的驱动程序就安装好了。

import pymysql

connection = pymysql.connect(host='192.168.***.***',
                             user='***',
                             password='***',
                             db='test',
                             charset='utf8mb4',
                             cursorclass=pymysql.cursors.DictCursor)

cur = connection.cursor()

cur.execute('select * from students')

for data in cur.fetchall():
    print(data)

 

当然这个用法是比较简单的,输出结果,运行ok

{‘tel’: ‘13811371377’, ‘sex’: ‘男’, ‘id’: 1, ‘age’: 20, ‘name’: ‘王刚’}

这个demo数据库的建立参考了这里,一个不错的mysql的基础教程。

大致这样,真实的开发项目会越来越复杂,且数据库应用的复杂度比独立应用高很多,希望这些经验能够给初学者们启发。

———————–后续补充

看到廖雪峰网站,上面有非常不错的python 2和3 的教程,对于安装mysql connector 有更加简单的办法

$ pip install mysql-connector-python --allow-external mysql-connector-python

 

以及非常简明实用的mysql 测试:

# 导入MySQL驱动:
>>> import mysql.connector
# 注意把password设为你的root口令:
>>> conn = mysql.connector.connect(user='root', password='password', database='test')
>>> cursor = conn.cursor()
# 创建user表:
>>> cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')
# 插入一行记录,注意MySQL的占位符是%s:
>>> cursor.execute('insert into user (id, name) values (%s, %s)', ['1', 'Michael'])
>>> cursor.rowcount
1
# 提交事务:
>>> conn.commit()
>>> cursor.close()
# 运行查询:
>>> cursor = conn.cursor()
>>> cursor.execute('select * from user where id = %s', ['1'])
>>> values = cursor.fetchall()
>>> values
[('1', 'Michael')]
# 关闭Cursor和Connection:
>>> cursor.close()
True
>>> conn.close()

 

python中使用单元测试和代码覆盖率测试的一个小例子

因为大数据的关系,接触python,的确很棒的语言,简洁优雅。

今天下午给同事说要注意单元测试和代码覆盖率,于是随手举例,没想到演砸了,继而研究了一下,分享之。

首先进行的 run with coverrage 的结果,运行的是一个单元测试文件。(我们用的是pycharm)
可以看到,主程序 r2c1_mian.py 的覆盖率在 94%,还是很不错的。(当然程序相对简单)

屏幕快照 2015-12-09 17.11.28

看下面的这个函数,检查一个整数或者单词,设计上是2,然后前面单元测试也通过了,我于是说只要修改为3,那么这些业务逻辑就不对了,再跑单元测试就应该会有通不过的情况。

屏幕快照 2015-12-09 17.13.04

屏幕快照 2015-12-09 17.14.53

结果没想到,单元测试通过了,也就是这个业务逻辑是有问题的(代码没有贴全,后面会说到),因为单元测试中是有针对这类情况的断言测试的。

屏幕快照 2015-12-09 17.14.16

注意上面代码的第一个代码和下面的代码,左边都有绿色和红色,这就是前面覆盖率运行时候的标记,绿色表示单元测试执行到的代码,红色表示没有执行到。

上面的那个代码左边红色的恰恰是返回False的代码在单元测试中没有被测试到(这个test case是有的,其实是被其他代码执行到而跳过了),下面的代码是上面代码调用到的,从左边红颜色可以看到很明显,有一个内部的返回True和一个返回False都没有被测试到,所以需要修改单元测试代码,来保证测试覆盖率。

屏幕快照 2015-12-09 17.34.53

整个业务逻辑的修改过程不详细说了,后来发现不是单元测试用例问题,而是业务逻辑有问题,所以这部分代码重构了一下,再进行一次单元测试代码覆盖率测试,可以看到同样的代码左边全部是绿色了,也就是至少单元测试的代码都覆盖到了。(这样就比较心安)

屏幕快照 2015-12-09 18.36.24

然后运行刚才修改为3的代码部分(可以理解为修改错了,或者不小心改错了的模拟,如下单元测试告诉我们有一个test case无法通过,看列出的代码也的确是我们期望的错误。

屏幕快照 2015-12-09 18.40.14

最后是代码比较,为了这个本来想说明单元测试好处而做得演示结果真的发现错误然后重构了这部分逻辑的调整,用代码比较工具呈现的情况:

屏幕快照 2015-12-09 18.41.52

对于一个健壮的程序来说,单元测试还是需要的,对于单元测试来说,足够的test case很重要,而对于需要测试的代码的覆盖率检查也不能忽视。

python 3.5 发布

学习python晚,因此没有2.x的纠结,3上来用用就不错,需要的库也都支持3。如今3都已经进化到3.5了。又是一大堆特性,好像说dict类型用c重新写了编译器,比原来速度最快到100倍。所有新特性在这里,我大部分也都看不太懂。

持续迭代升级总是不错的。