volnet

Head First Python(中文版)

知道Head First系列图书,怎么也有十年八年了,这是第一次阅读Head First系列,确实和别的图书风格不太一样。

一般介绍计算机语言类的图书,都是从语法、规则等入手,按照科班教学的思路,先是变量、然后是循环判断等、再后面是结构、类之类的高级语言特性。

而Head First以示例的方式来穿插讲解各种语言要点,同时更强调完成一件事所经历的思考过程,也正因为这种特殊的形式,它并不是很适合作为参考书,而需要顺序阅读。

不过我还是尽量从冗长的文章中提炼出语言要点,便于日后复习和参考使用。

不过这本书实在是浪费了大量的篇幅用来画图和重复表达,不知道是不是因为出版和稿费的问题。

回到Python本身,它的语法确实还需要我去适应,和C风格的语言相比,还是有一些不太一样。但是从语言概念来讲,现在主流的面向对象编程语言所拥有的常用语言特性在Python里面也都有对应。

Python给人直观的印象则是它拥有大量的社区资源用来完成一些特定模式的事情,而且确实现在越来越多的开源框架、书籍都以Python作为主要的示例语言,因此学习它至少是一个很不错的选择。

原书示例相关下载

1 初识Python

作为从C语系转过来的科班生,遇到没有{},没有类型声明,确实有点儿不适应。作者使用了以下例子来进行演示:


>>> the_list = ["a1", 2, ["b1", "b2"], 4, "a3", ["b3", ["c1", "c2"]]]
>>> def print_list(the_list):
	for item in the_list:
		if isinstance(item, list):
			print_list(item)
		else:
			print(item)

			
>>> print_list(the_list)
a1
2
b1
b2
4
a3
b3
c1
c2
>>> len(the_list)
6

知识点:

2 共享你的代码

本节主要通过完善上一节的例子,讲述了函数的编写、发布、本地副本、发布到PyPI等特性。

>>> this_list = ['a', 'b', ['c', ['d', 'e'], 'f']]
>>> import nester
>>> nester.print_list(this_list)
a
b
c
d
e
f
>>> nester.print_list(this_list, True)
a
b
	c
		d
		e
	f
>>> nester.print_list(this_list, True, 2)
		a
		b
			c
				d
				e
			f
>>> 
"""This is the "standard" way to include a multiple-line comment in your code."""

# This is a single line comment

def print_list(the_list, indent = False, level = 0):
	for item in the_list:
		if isinstance(item, list):
			print_list(item, indent, level + 1)
		else:
			if indent:
				for i in range(level):
					print("\t", end="")
			print(item)

知识点:

python3 setup.py register
python3 setup.py sdist upload
from distutils.core import setup

setup(
    name = 'nester_volnet',
    version = '1.0.6',
    py_modules = ['nester', 'testnester'],
    author = 'volnet',
    author_email = 'volnet@tom.com',
    url = 'http://volnet.github.io',
    description='A simple printer of nested lists, this version add the testnester.py',
)
python3 setup.py sdist
sudo python3 setup.py install
import nester
nester.print_list(this_list)
from nester import print_list
print_list(this_list)
for i in range(4):
	print(i)

等价于

i = 0
while i < 4:
	print(i)
	i = i + 1
def print_list(thislist, level = 0)

本节示例代码:

lastest src samples/chapter02/nester/

packages samples/chapter02/nester.pypi/

PyPI url

3 文件与异常

在本节中,作者用一个文本读取,并进行Split的例子,来说明程序中可能出现的错误,以及如何使用异常处理机制。

不过,读到这里,不知道作者是为了应对这一节的标题,还是真心喜欢异常处理。

在一些其他的语言,如C#中,异常,被认为是比较耗时的行为,一般推荐使用丰富的判断逻辑(if)来处理可预测的错误。但是本节作者更倾向于使用异常以让程序主体占用更多的篇幅。这也表达了该语言更倾向于编写易读、易写、性能要求不高的程序。

import os

def getinfo():
    os.getcwd()
    os.chdir("../Documents/")
    #os.chdir("../volnet/volnet.github.io/docs/book/HeadFirstPython/samples/chapter03/")
    os.getcwd()

    try:
        data = open('sketch.txt')
        print(data.readline(), end='')
        print(data.readline(), end='')

        print("---------")

        data.seek(0)
        """
        # use if to deal with the error
        for each_line in data:
            if not each_line.find(":") == -1 :
                (role, line_spoken) = each_line.split(":", 1)
                print(role, end = '')
                print(" said: ", end = '')
                print(line_spoken, end='')
        """

        # use except to deal with the error
        for each_line in data:
            try:
                (role, line_spoken) = each_line.split(":", 1)
                print(role, end = '')
                print(" said: ", end = '')
                print(line_spoken, end='')
            except ValueError:
                pass

        data.close()

    except IOError:
        print("The data file is missing!")

知识点:

本节示例代码:

lastest src samples/chapter03/

PyPI url

4 持久存储

知识点:

except IOError as err:
    print(str(err))
try:
    data = open('missing.txt')
    print(data.readline(), end='')
except IOError as err:
    print('File error' + str(err))
finally:
    if 'data' in locals():
        data.close()
with open('data1.txt', 'r') as data1, open('data2.txt', 'w') as data2:
    print(data1.read(), file=data2)

本节示例代码:

lastest src samples/chapter04/

PyPI url

5 推导数据

知识点:

new_l = []
for t in old_l:
    new_l.append(len(t))

使用列表推导重写这个代码,可以写作:

new_l = [len(t) for t in old_l]
>>> my_list = [0,1,2,3,4,5,6,7,8]
>>> print(my_list[3:6])
[3, 4, 5]

本节示例代码:

lastest src samples/chapter05/

PyPI url

6 定制数据对象

class MyClass:
    NickName = ''
    def __init__(self, name, age):
        self.Name = name
        self.Age = age

    def GetName(self):
        return self.Name + '(' + self.NickName + ')'

    def GetAge(self):
        return self.Age

    def AddAge(self, age):
        self.Age += age
        return self.Age

class MyClass2(MyClass):
    def __init__(self, name):
        MyClass.__init__(self, name, 20)

    def AddAge(self, age):
        self.Age += (2 * age)
        return self.Age

obj1 = MyClass('Eric', 18)
obj2 = MyClass2('Volnet')

# Eric 18
print(obj1.GetName() + '-' + str(obj1.GetAge()))
# Volnet 20
print(obj2.GetName() + '-' + str(obj2.GetAge()))

obj1.AddAge(10)
# Eric 18+10=28
print(obj1.GetName() + '-' + str(obj1.GetAge()))

obj2.AddAge(10)
# Volnet 20+2*10=40
print(obj2.GetName() + '-' + str(obj2.GetAge()))

obj1.Name = 'Eric Kung'
obj1.NickName = 'volnet'
# Eric Kung(volnet) 18+10=28
print(obj1.GetName() + '-' + str(obj1.GetAge()))
# Volnet 20
print(obj2.GetName() + '-' + str(obj2.GetAge()))

知识点:

>>> person = { "Name":"volnet" }
>>> person["Name"]
'volnet'
>>> class Person:
	Name = 'volnet'

	
>>> a = Person()
>>> a.Name
'volnet'

本节示例代码:

lastest src samples/chapter06/

7 Web开发

知识点:

本节示例代码:

lastest src samples/chapter07/

8 移动应用开发

本节所涉及的SL4A已经从code.google.com迁移到Github

因Android模拟器的问题,本节未实现在Android手机上的调试。

知识点:

本节示例代码:

lastest src samples/chapter08/

原文中使用SL4A在Android上用Python2进行http调用,因此使用的urlopen和urlencode与Python3中不太一样。而这里我使用Python3进行client调用,因此代码上略有不同。

9 管理你的数据

import sqlite3

# create database 

conn = sqlite3.connect('coachdata.sqlite')
cursor = conn.cursor()
cursor.execute("""CREATE TABLE athletes(
    id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
    name TEXT NOT NULL,
    dob DATE NOT NULL
)""")
cursor.execute("""CREATE TABLE timing_data(
    athlete_id INTEGER NOT NULL,
    value TEXT NOT NULL,
    FOREIGN KEY(athlete_id) REFERENCES athletes
)""")

conn.commit()
conn.close()

# init database

conn = sqlite3.connect('coachdata.sqlite')
cursor = conn.cursor()

import glob
import athletemodel

data_files = glob.glob('../data/*.txt')
athletes = athletemodel.put_to_store(data_files)

for each_ath in athletes:
    name = athletes[each_ath].name
    dob = athletes[each_ath].dob

    cursor.execute("INSERT INTO athletes (name, dob) VALUES (?, ?)", (name, dob))
    conn.commit()

conn.close()

知识点:

#! /Library/Frameworks/Python.framework/Versions/3.5/bin/python3

import os
import time
import sys

addr = os.environ["REMOTE_ADDR"]
host = os.environ["REMOTE_HOST"]
serverport = os.environ["SERVER_PORT"]
method = os.environ["REQUEST_METHOD"]

cur_time = time.asctime(time.localtime())

print(os.environ, file=sys.stderr)
print('host = ' + host + ', addr = ' + addr + ', serverport = ' + serverport + ', cur_time = ' + cur_time + ', method = ' + method, file=sys.stderr)

本节示例代码:

lastest src samples/chapter09/

10 扩展你的Web应用

因Google App Engine(GAE)已经升级为Google Cloud Platform,因此本节涉及的内容可能不再适用。

知识点:

11 处理复杂性

知识点:

>>> userinput = input('Please enter a word:')
Please enter a word:Hi
>>> userinput
'Hi'
>>> results = []
>>> mylist = [1,2,3,4,5,6,7]
>>> for item in mylist:
	if item > 3:
		results.append(item)

		
>>> results
[4, 5, 6, 7]

等价于

>>> mylist = [1,2,3,4,5,6,7]
>>> results = [item for item in mylist if item > 3]
>>> results
[4, 5, 6, 7]

i 其他

作者推荐WingWare Python IDE作为专业的Python IDE工具。

以下代码将产生UnboundLocalError错误。

name = 'Head First Python'
def what_happen_here():

	print('1.' + name)
	
	name = name + ' is a great book!'
	
	print('2.' + name)

what_happen_here()
print('3.' + name)

"""
Traceback (most recent call last):
  File "/Users/volnet/volnet.github.io/docs/book/HeadFirstPython/samples/chapter12/test-i2-1.py", line 10, in <module>
    what_happen_here()
  File "/Users/volnet/volnet.github.io/docs/book/HeadFirstPython/samples/chapter12/test-i2-1.py", line 4, in what_happen_here
    print('1.' + name)
UnboundLocalError: local variable 'name' referenced before assignment
>>> 
"""

以下代码使用了global关键字修复了作用域问题:

name = 'Head First Python'
def what_happen_here():
    print('1.' + name)
    global name
    name = name + ' is a great book!'
    print('2.' + name)
what_happen_here()
print('3.' + name)

"""
1.Head First Python
2.Head First Python is a great book!
3.Head First Python is a great book!
"""

在全局范围定义的变量,在私有方法中,是可以读取的,但是不能修改。 所以print('1.' + name)的方法可以正确执行。但是后面的name = name + ' is a great book!'方法则会失败。

使用global关键字明确地表明接下来要访问和修改的是全局变量,而后就可以正确执行赋值语句了。这也避免了无意间修改了全局变量的值。

Python的标准测试框架分为两种:unittestdoctest

Python还支持下面这些语言特性(不止以下这些):

- 匿名函数

- 生成器

- 定义异常

- 函数修饰符

- 元类

常见的适用于Python的Web框架有: DjangoZopeTurboGearsWeb2pyPylons

下面这些技术可以构建基于GUI的Python应用:tkinter、PyGTK、PyKDE、wxPython和PyQT

Python使用一种称为全局解释器锁(Global Interpreter Lock,GIL)的技术来实现。它强制实行这样一个限制,要求Python只能在一个解释器进程中运行,即使有多个处理器可用。

对于你来说,这意味着,如果你的程序使用了线程,尽管它的设计和实现都很棒,但是即使有多个处理器这个程序也不会运行得更快,因为它根本无法使用多个处理器。你的线程应用会串行运行,而且在很多情况下,甚至比没有用线程开发同样的功能时慢得多。

要点:除非去除GIL限制(如果真的能去除)……否则不要在Python线程中使用线程。

本节示例代码:

lastest src samples/chapter12/