阅读笔记

作者:方跃文

Email: fyuewen@gmail.com

时间:始于2017年9月12日, 结束写作于

附录 A

附录A在原书最后,不过我自己为了复习python的一些命令,所以特意将这一部分提前到此。

python 解释器

python解释器通过“一次执行一条语句”的方式运行程序。多加利用Ipython。

通过使用 %run 命令,IPython 会在同个进程中执行指定文件中的代码。例如我在当年目录的下级目录appendix-A中创建了一个simple01.py的程序,它的内容是

a = 1
print(a)

下面我在jupyter notebook中执行


In [ ]:
%run appendix-A/simple01.py


1

基础知识

语言语义

python语言的设计特点是重视可读性、简洁性和明确性

缩进,而不是大括号

python是通过空白符(制表符或者空格)来阻止代码的,不像R、C++等用的是大括号。该书原作者建议使用4空格作为缩进量。

万物皆对象

python语言的一个重要特点就是其对象模型的一致性。任何数值、字符串等都待在自己的“盒子”里,而这些“盒子”就是python的对象。

注释

注释符使用#

函数调用和对象方法的调用

几乎所有的python对象都有一些附属函数(也就是方法),它们可以访问该对象的内部数据。方法的调用时这样写的:

obj.some_method(x, y, z)

变量和按引用传递

在python中对变量赋值时,你其实是在创建等号右侧对象的一个引用。用实际的例子来说,看看下面这个整数列表:


In [14]:
a = [1, 2, 3]
b = a
a.append(4)
print(a)
print(b)


[1, 2, 3, 4]
[1, 2, 3, 4]

用小卡特的爹(好吧,这其实是某本python书的作者,但是我实在想不起来名字了)的话就是:相当于把a和b都贴在了[1,2,3]这个上面,上面a.append[4]之后,相当于把又贴在了[1,2,3,4]上面,因为b是贴在a上的,所以b的值也被改变,成为了[1,2,3,4]。

赋值操作assignment也可以被叫做binding绑定,因为这其实是将一个名称和对象进行了捆绑。因此被赋值了得变量名有时也被称作binding variables。

当我们把对象以参数的形式传入函数时候,我们其实传入的只是引用而已,不会发生任何复制。这是python区别于一些其他语言的重要特征之一。

例如下面这个例子


In [15]:
def append_element(fanglist, element):
    fanglist.append(element)

data = [1,2,3]
append_element(data, 5)
print(data)


[1, 2, 3, 5]

动态引用,强类型

跟Java和C++相反, python中的对象引用没有与之关联的类型信息。从下面的例子可以看出:


In [23]:
a = 5
type(a)


Out[23]:
int

In [24]:
a = 2.5
type(a)


Out[24]:
float

变量其实是对象在特定命名空间中的名称而已。对象的类型信息是保存在它自己内部的。如果你因为这样就认为python不是类型语言,那就是错了。 例如我们发现把字符串和整型数据直接相加在python中是不能进行的:


In [26]:
a = 'hello'
b = 5
print('a is %s, and b is %s' % (type(a), type(b)))
c = a + b


a is <class 'str'>, and b is <class 'int'>
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-26-baac8ff1b682> in <module>()
      2 b = 5
      3 print('a is %s, and b is %s' % (type(a), type(b)))
----> 4 c = a + b

TypeError: must be str, not int

从上面可以看出类型必须一致才能进行四则运算。从上面我们可以看出type可以用来查看一个对象的类型;反过来,python中,可以通过isinstance函数来检查一个对象是否是某个数据类型的实例。


In [29]:
a = 5
isinstance(a, int)


Out[29]:
False

In [30]:
a = 2.2
isinstance(a, int)


Out[30]:
False

instance 可以接受由类型组成的元祖。如果想检查某个对象的类型是否属于元祖中所制定的类型,可以用如下例子中的方法:


In [8]:
a = 5
b = 2.2
isinstance(a,(int,float))


Out[8]:
True

In [10]:
a = 5
b = 2.2
isinstance(b,(int,float))


Out[10]:
True

属性和方法

时间: 2017年9月20日

Python中的对象通常具有属性 attribute,也具有方法。所谓方法,则是与对象有关并且能够访问对象内部数据的函数。它们通过形如 obj.attribute_name 这样的语法进行访问


In [11]:
a = 'foo' #定义了对象

In [18]:
a.count #访问对象的方法


Out[18]:
<function str.count>

In [19]:
#访问对象的方法还可以使用 getattr 函数 (getattr 是 getattribute 的缩写)
getattr(a, 'center')


Out[19]:
<function str.center>

"鸭子"类型

(鸭子在这里是哲学隐喻。意思是说:对于一只鸟类动物,不用管它是不是鸭子,只要看它像不像鸭子就可以了。)

一般上,我们并不总是关心对象属于什么类型,但是很多时候我们想知道的是它到底有没有某些方法。比如,只要一个对象实现额迭代器协议(iterator protocol),就可以确认它是可迭代的。对于大部分对象而言,这就意味着它拥有一个


In [ ]:
__inter__

魔术方法。当然,还有一个更好一些的验证办法,即利用 iter 函数:


In [24]:
def isiterable(obj):
    try:
        iter(obj)
        return True
    except TypeError: #不可迭代
        return False
#对于字符串以及大部分Python集合类型,该函数返回True:
print(isiterable([1,2]))
print(isiterable((1,2)))
print(isiterable('1'))
print(isiterable(1))


True
True
True
False

常常在编写需要处理多类型输入的函数时使用这个功能,此外还有一个常见的应用场景是:编写可以接受任何序列(列表、元祖、ndarry)或迭代器的函数。例如我们可以先检查对象是不是列表(或NumPy数组),如果不是,就将其转换成是:


In [38]:
x = (1,2) #定义了一个tuple
print(type(x))
print(isinstance(x,list) )#因为x是tuple,不是list,所以返回 False
print(isiterable(x))
if not isinstance(x,list) and isiterable(x):
#if not isiterable(x):
    x = list(x)
    print(x)


<class 'tuple'>
False
True
[1, 2]

引入 import

2017年9月21日

在python中,module模块就是一个含有函数和变量定义及从其他.py文件引入的此类东西的.py文件。也许这句话很拗口,所以还是让我用例子说话:

我在appedix-A文件夹中创建了一个新python脚本,取名为simple02.py,它的内容是(注意原书使用的是Python 2.7, 我使用的是python 3.6所以代码细节上有微小的差别)


In [ ]:
#set up a module

PI = 3.14159
def f(x):
    return (x + 2 )

def g(a, b):
    return (a + b)

如果我们想要引入 simple02.py 中定义的变量和函数,我们可以在同目录下创建另一个文件,取名为 simple03.py


In [1]:
import simple02 as s2
result = s2.f(5)
print(result)
pi = s2.PI
print(pi)
sumvalue = s2.g(2,2)
print(sumvalue)


  File "<ipython-input-1-292b4b5c581e>", line 1
    import '../appendix-A/simple02’ as s2
                                         ^
SyntaxError: EOL while scanning string literal

运行它:python simple03.py

(如果是在windows系统的git bash中运行python,需要先设置一下python的路径,方法是在git bash terminal中运行PATH=$PATH:/c/ProgramData/Anaconda3/,然后就可以使用python了, see referene)

或者我们也可以直接在jupyter notebook中运行,但是需要增加一些代码让系统知道module在什么位置。此处我使用了import sys的方法,这种方法参考自名为shuang的github用户:链接

以下是我摘自这位用户的wiki:

为了让我们自己写的模块能够被 Python 解释器知道,需要用sys.path.append("~/sample.py")。其实,在 Python 中,所有模块都被加入到了 sys.path 里面了。用下面的方法可以看到模块所在位置:

import sys

import pprint

pprint.pprint(sys.path)


In [20]:
#Fang code
import sys
sys.path.append("./appendix-A/simple02.py")
import simple02

result=simple02.f(5)
print(result)


7

二元运算符和比较运算符

一些常用的运算符如下:


In [ ]:
a + b # 求和
a - b
a*b
a/b
a//b # a 除以 b后向下圆整,丢弃小数部分
a**b  # a 的 b次方
a & b # 如果a和b均为True,则结果为True. 对于整数,执行按位与操作 https://zh.wikipedia.org/wiki/%E4%BD%8D%E6%93%8D%E4%BD%9C
a|b #如果a或者b为True, 则结果为True. 对于整数,执行按位与操作
a^b #对于布尔值,如果a或者b为True(但不都为True),则结果为True. 对于整数,执行按位异或操作
a==b #如果a等于b,则结果为True
a !=b #不等,则True
a <= ba<b # 如果a小于等于(或小于)b,则结果为True
a > ba >= b # 如果a大于(或大于等于)b,则结果为True
a is b #如果引用a和引用b都指向同一个python对象,则结果为True
a is not b #如果引用a和引用不指向同一个python对象,则结果为True

In [37]:
#举例子来运用这些算符
a = (1,2,3)
b = a
c = 1.2
d = 2.0
e = 2
f = 3

sumtuple = a + b
print (sumtuple)
suminteger = e + f
print(suminteger)
print(c/d)
print(c//d)
print(e/f)
print(e//f)
print(e & f)
print (True & True)
print(e is f)
print (e is not f)
print (True^True)
print (True^False)

if a == b:
    print('a=b')


(1, 2, 3, 1, 2, 3)
5
0.6
0.0
0.6666666666666666
0
2
True
False
True
False
True
a=b

严格与懒惰

无论使用什么变成语言,都需要了解一下表达式是何时被求值的。

在python中,例如这样一个例子:


In [ ]:
a=b=c=5
d=a+b*c

在python中,只要这些句子被求值,相关计算就会立即发生。上述的例子中,d的值会被立即设定为30。而在其他一些语言(eg. Haskell), d的值在被使用之前是不会被计算出来的。

在一些特定环境下,我们希望求值计算并不是立即进行的。特别是在密集型计算中,计算负荷重,延迟计算就可以发挥作用。Python 提供了一些技术来实现这样的计算,但是在本书中并没有做介绍。

可变和不可变对象

大部分python对象是可变的(mutable),比如列表、字典、NumPy数组以及大部分用户自定义类型(类)。也就是说,它们所包含的对象或者值是可被修改的。


In [39]:
a = [1,2,3,4]
a[0]=2
print(a)


[2, 2, 3, 4]

另外一些对象,诸如tuple元组和字符串是不可变的immutable。 下面这个例子中,我们试图去改变tuple,但是python告诉我们这样的操作是不被支持的。


In [43]:
a_tuple = (2,3,4)
a_tuple[1]=2


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-43-3c89a108ffa5> in <module>()
      1 #另外一些对象,诸如tuple元组和字符串是不可变的。
      2 a_tuple = (2,3,4)
----> 3 a_tuple[1]=2

TypeError: 'tuple' object does not support item assignment

标准类型

Python 有一些用于处理数值数据、字符串、布尔值(True or False)以及日期/时间的内置类型。

这里我们列出主要的标量类型。后面我们将单独讨论日期/时间的处理,因为它们是由标准库中的datetime模块提供的。

标准的Python标量类型

类型 说明
None Python的'null'值(None只存在一个实例对象)
str 字符串类型。Python 2.x 中只有 ASCII 值,而Python 3中则是Unicode
unicode Unicode 字符串类型
float 双精度(64)浮点数。注意这里没有专门的doubl类型
bool True或者False
int 有符号整数,其最大值由平台系统决定
long 任意精度的有符号整数。大的itn值会被自动转换为long

因为我在这里使用了Markdown输入表格,所以顺便介绍下Markdown中关于表格的一些小细节

语法说明:

  1. |、-、:之间的多余空格会被忽略,不影响布局。

  2. 默认标题栏居中对齐,内容居左对齐。

  3. -:表示内容和标题栏居右对齐,:-表示内容和标题栏居左对齐,:-:表示内容和标题栏居中对齐。

  4. 内容和|之间的多余空格会被忽略,每行第一个|和最后一个|可以省略,-的数量至少有一个。

数值类型

在 Python 中,用于表述数据的类型主要是 int 和 float.

1)能被保存为int的整数的大小由平台决定,但是Python会把非常大的证书转换为long,它可以存储任意大小的整数。

2)float型可以写成小数形式,也可以写作科学技术法形式如 1e-5

3)python3中整数除法除不尽时就会产生浮点,例如


In [1]:
3/2


Out[1]:
1.5

但是在python2.x中,并不能默认产生这个浮点解。要产生浮点解,需要在python2.x的程序中写入


In [ ]:
from __future__ import division

如果不加这句话,也可以用显示的方式来转化,如


In [2]:
3/float(2)


Out[2]:
1.5

4)如果要得到C风格的整数除法,即除不尽时候丢弃小树,可以使用“除后圆整运算符//”


In [3]:
3//2


Out[3]:
1

5)在python中,复数的虚部用j表示,例如


In [5]:
a=1+2j
b=3-3j
c=a-b
print(c)


(-2+5j)

字符串

字符串在python中的使用十分灵活。可以用单引号或者双引号。对于大段的字符串则可以使用三重引号。例如


In [6]:
a='Hi, this is Yue-Wen FANG from NYU SHANGHAI'
b="He is a visiting researcher!"
c='''
##########################
##########################
##########################
'''
print(a)
print(b)
print(c)


Hi, this is Yue-Wen FANG from NYU SHANGHAI
He is a visiting researcher!

##########################
##########################
##########################

前面我们已经提到过,字符串在python中是不可以被改变的,即immutable,如果要修改字符串就必须创建一个新的。


In [7]:
a="He is a visiting researcher!"
b=a.replace('is a visiting researcher','graduated from East China Normal University') #创建了一个新的字符串,b引用了这个新的字符串
print(a)
print(b)


He is a visiting researcher!
He graduated from East China Normal University!

In [ ]:
python中允许将其他类型转化成字符串用str函数例如

In [8]:
a=5
str(a)
print(a)


5

由于字符串其实是一串字符序列,所以可以被当作某种序列类型(类似列表、元祖等)进行处理


In [18]:
a="ALOHA!"
print(a)
list(a)
print(a[:3])


ALOHA!
ALO

如果一个字符串包含很反斜杠,这会很让人懊恼,因为反斜杠常常会将字符进行转移。在python中,为了使得反斜杠就代表反斜杠本身而不去进行转移,可以在字符串前面加上字母r。这个在matplot里面画图添加希腊字母标注时候会显得比较有用。


In [23]:
s = 'This\\is\\a\\flower'
s1 = r'This\\is\\a\\flower'
print(s,'\t',s1)


This\is\a\flower 	 This\\is\\a\\flower

这里重点讲一讲python 3带来的一些新的字符串格式化的手段,其实我在平时也已经在用了,但是这里再进行简短的讨论。因为在数据分析中这个经常会用到。

这里简单说一下主要的机制:以一个%开头且后面跟着一个或者多个格式字符的字符串是需要插入值的目标。这句话读起来也很拗口,让我们看例子


In [24]:
template = '%s was born in %d' 
template % ('yue-wen',1990)


Out[24]:
'yue-wen was born in 1990'

上述,%s代表将参数格式化为字符串,%d将参数格式化为整数,这些地方都是需要实际参数来替换的,因此在上述代码的第二行中,我们使用了由值组成的元组,将这些值传入形参的位置。


In [1]:
# 再看一个例子
a=2e6
b=2.9
print('a is %d and b is %f' %(a, b))


a is 2000000 and b is 2.900000

布尔值

比较运算和表达式都可以产生True或者False. 布尔值可以用and和or关键字进行连接


In [2]:
True and False


Out[2]:
False

In [4]:
True or False


Out[4]:
True

** 几乎所有内置的python类型以及任何定义了nonzero魔术方法的类都能在if语句中被解释为True或者False:


In [16]:
a = [1,2,3]
b = a[:]
c = []
if a:
    print('a is %s' % a)

if not b:
    print('b is %s' % b)

if not c: # 因为c是空的,所以会当作False处理,not False则为True
    print('c is none')


a is [1, 2, 3]
c is none

如果想知道一个对象会被强制转换成哪个布尔值,可以使用bool函数。


In [17]:
a = [1,2,3]
b = a[:]
c = []
print('The bool values of a, b, and c are %s, %s, and %s, respectively' % (bool(a), bool(b), bool(c)))


The bool values of a, b, and c are True, True, and False, respectively

In [18]:
bool([]), bool([1]), bool('hello'), bool()


Out[18]:
(False, True, True, False)

类型转换

str、 bool、 int 以及 float 等类型也可用作将值转换成该类型的函数. 我觉得,通过下面的例子不难发现,这些函数并不会从本质上改变原始值,而只是创建了新的引用。


In [47]:
import math
s = math.pi
a = str(s)
print(type(s))
print(type(a))
b = float(a)
int(float(a))
print(type(a))


<class 'float'>
<class 'str'>
<class 'str'>

None

None是python的空值类型。如果一个函数无显示的返回值,则隐式返回None


In [48]:
a = None
a is None


Out[48]:
True

In [56]:
b = 5
print(b is not None)
if b is None:
    print(b)


True

None 还是函数可选参数的一种常见默认值;(不过,虽然我知道可以这样用,但是我暂时联想不到有什么场景可以用到)


In [59]:
def add_and_maybe_multipy(a, b, c=None):
    if a is not None and b is not None and c is None:
        return(a+b)
    else:
        return(a*c)

x = add_and_maybe_multipy(1, 1, None)
y = add_and_maybe_multipy(1, 2, 3)
print('x = %d and y = %d' % (x, y))


x = 2 and y = 3

值得注意的是, None并非是保留关键字,它只是NoneType的一个实例

日期和时间

Python 内置的 datetime 模块提供了 datetime, date 以及 time 等类型。datetime 类型是用的最多的,它合并了保存在 date 和 time 中的信息。


In [64]:
from datetime import datetime, date, time

dt = datetime(2017, 10, 28, 19, 20, 22)
day = dt.day
print(day)
min = dt.minute
print(min)


28
20

给定 datetime 一个实例。你可以通过调用其date和time方法提取相应的date和time对象


In [65]:
dt.date()


Out[65]:
datetime.date(2017, 10, 28)

In [66]:
dt.time()


Out[66]:
datetime.time(19, 20, 22)

strftime 方法用于将datetime格式化为字符串:


In [14]:
from datetime import datetime, date, time

dt = datetime(2017, 9, 22, 12, 30, 20)
print(dt.day)

datetime.strptime('20170923', '%Y%m%d')
#完整的格式化定义请参考表书本10-2


22
Out[14]:
datetime.datetime(2017, 9, 23, 0, 0)

In [ ]:
#Page 417

控制流

if, elif 和 else

if语句是常见的控制语句类型。它用于判断一个条件,如果为 True, 则执行紧跟其后的代码块:


In [32]:
x=-1
if x<0:
    print(repr("hello"))
    print("%s" % ("hello"))

a = [2, 3, 4, 5, 6, 8, 10]
if type(a)==list:
    print(a)


'hello'
hello
[2, 3, 4, 5, 6, 8, 10]

if 可以和多个 elif 以及一个“滴水不漏”的 else 块(如果所有条件都为False),例如


In [34]:
x = 10

if x<0:
    print('x is negative')
elif x == 0:
    print('x = 0')
elif x > 0:
    print('x > 0')
else:
    print('x is a complex')


x > 0

for 循环

for 循环用于对集合(比如列表或者元祖)或迭代器进行迭代。for 循环的标准语法是:

for value in collection:

continue关键字用于使for循环提前进入下一次迭代,即跳过本次循环中剩余的代码块。我们来看个例子来理解下continue的功能,其功能是对列表中证书求和并跳过None值.


In [38]:
sequence = [1, 2, None, 4, None, 5]
total = 0
for value in sequence:
    if value is None:
        continue
    total += value
    print('total is %f in this cyle' % (total))


total is 1.000000 in this cyle
total is 3.000000 in this cyle
total is 7.000000 in this cyle
total is 12.000000 in this cyle

后面我们会看到,如果集合或者迭代器的元素是序列类型,例如元组或者列表,那么还可以方便将这些元素拆散成for语句中的多个变量.


In [53]:
#使用的语法是
#for a,b,c in iterator:
#    do something

#我这里只写一个简单的例子,但是这个例子可能还不是上述语法最正统的例子。
# date: 20170923
sequence = [(1,2,3),(4,5,6)]
total = 0
for (i,j,k) in sequence:
    print((i,j,k))
    coor = (i,j,k)
    for i in coor:
        i=i+1
        print(i, end=' ')
    print('\n')


(1, 2, 3)
2 3 4 

(4, 5, 6)
5 6 7 

while 循环

while 循环定义了一个条件和一个代码块,只要条件不为False或者循环没有被break显示终止,则代码会一直执行下去:


In [55]:
x = 256
total = 0
while x > 0:
    if total > 500:
        break
    total += x
    print('total = %d' %total)
    x = x//2
    print('x = %d' %x)


total = 256
x = 128
total = 384
x = 64
total = 448
x = 32
total = 480
x = 16
total = 496
x = 8
total = 504
x = 4

pss

pass 是 python 中的“空操作”语句。它可以被用在那些没有任何功能的代码块中。由于python是根据空白符划分代码块的,所以它的存在是很有必要的。


In [ ]:
if x < 0:
    print 'negative'
elif x==0:
    #TO DO: here you can add codes in future
    pass
else:
    print 'positive'

在开发一个新功能时,常常会将pass用作代码中的占位符:


In [ ]:
def f(x,y,z):
    #TODO: realize a function here
    pass

异常处理

优雅地处理Python错误或异常是构建健壮程序的重要环节。 译者翻译的这句真有意思,感觉铺面而来一种“知乎”风格。

在data science中,许多函数只对特定数据类型有效。例,python中的float函数可将字符串转换为浮点数,但是如果输入值不正确就会产生 ValueError


In [57]:
float('1.2345')


Out[57]:
1.2345

In [58]:
float('something')


---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-58-439904410854> in <module>()
----> 1 float('something')

ValueError: could not convert string to float: 'something'

In [ ]: