文章和代码等已经归档至【Github仓库: https://github.com/timerring/dive-into-AI 】或者公众号【AIShareLab】回复 python数据分析 也可获取。
缩进
建议使用四个空格(tab)作为默认的缩进。
Python的语句不需要用分号结尾。但是,分号却可以用来给同在一行的语句切分:
a = 5; b = 6; c = 7
Python不建议将多条语句放到一行,这会降低代码的可读性。
函数和对象方法调用
你可以用圆括号调用函数,传递零个或几个参数,或者将返回值给一个变量:
result = f(x, y, z)
g()
几乎Python中的每个对象都有附加的函数,称作方法,可以用来访问对象的内容。可以用下面的语句调用:
obj.some_method(x, y, z)
变量和参数传递
在Python中,a和b实际上是同一个对象,即原有列表[1, 2, 3]。可以在a中添加一个元素,然后检查b:
In [8]: a = [1, 2, 3]
In [9]: b = a
In [10]: a.append(4)
In [11]: b
Out[11]: [1, 2, 3, 4]
赋值也被称作绑定,我们是把一个名字绑定给一个对象。变量名有时可能被称为绑定变量。
当你将对象作为参数传递给函数时,新的局域变量创建了对原始对象的引用,而不是复制。
def append_element(some_list, element):
some_list.append(element)
In [27]: data = [1, 2, 3]
In [28]: append_element(data, 4)
In [29]: data
Out[29]: [1, 2, 3, 4]
强类型
变量是在特殊命名空间中的对象的名字,类型信息保存在对象自身中。一些人可能会说Python不是“类型化语言”。这是不正确的,看下面的例子:
In [16]: '5' + 5
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-16-f9dbf5f0b234> in <module>()
----> 1 '5' + 5
TypeError: must be str, not int
在某些语言中,例如Visual Basic,字符串‘5’可能被默许转换(或投射)为整数,因此会产生10。但在其它语言中,例如JavaScript,整数5会被投射成字符串,结果是联结字符串‘55’。在这个方面,Python被认为是强类型化语言,意味着每个对象都有明确的类型(或类),默许转换只会发生在特定的情况下,例如:
In [17]: a = 4.5
In [18]: b = 2
# String formatting, to be visited later
In [19]: print('a is {0}, b is {1}'.format(type(a), type(b)))
a is <class 'float'>, b is <class 'int'>
In [20]: a / b
Out[20]: 2.25
你可以用isinstance
函数检查对象是某个类型的实例:
In [21]: a = 5
In [22]: isinstance(a, int)
Out[22]: True
isinstance
可以用类型元组,检查对象的类型是否在元组中:
In [23]: a = 5; b = 4.5
In [25]: isinstance(b, (int, float))
Out[25]: True
属性和方法
Python的对象通常都有属性(其它存储在对象内部的Python对象)和方法(对象的附属函数可以访问对象的内部数据)。可以用obj.attribute_name
的形式访问属性和方法:
In [1]: a = 'foo'
In [2]: a.<Press Tab>
a.capitalize a.format a.isupper a.rindex a.strip
a.center a.index a.join a.rjust a.swapcase
a.count a.isalnum a.ljust a.rpartition a.title
a.decode a.isalpha a.lower a.rsplit a.translate
a.encode a.isdigit a.lstrip a.rstrip a.upper
a.endswith a.islower a.partition a.split a.zfill
a.expandtabs a.isspace a.replace a.splitlines
a.find a.istitle a.rfind a.startswith
也可以用getattr
函数,通过名字访问属性和方法:
In [27]: getattr(a, 'split')
Out[27]: <function str.split>
在其它语言中,访问对象的名字通常称作“反射”。本书不会大量使用getattr
函数和相关的hasattr
和setattr
函数,使用这些函数可以高效编写原生的、可重复使用的代码。
鸭子类型
经常地,你可能不关心对象的类型,只关心对象是否有某些方法或用途。这通常被称为“鸭子类型”,来自“走起来像鸭子、叫起来像鸭子,那么它就是鸭子”的说法。例如,你可以通过验证一个对象是否遵循迭代协议,判断它是可迭代的。对于许多对象,这意味着它有一个__iter__
魔术方法,其它更好的判断方法是使用iter
函数:
def isiterable(obj):
try:
iter(obj)
return True
except TypeError: # not iterable
return False
这个函数会返回字符串以及大多数Python集合类型为True
:
In [29]: isiterable('a string')
Out[29]: True
In [30]: isiterable([1, 2, 3])
Out[30]: True
In [31]: isiterable(5)
Out[31]: False
我总是用这个功能编写可以接受多种输入类型的函数。
常见的例子是编写一个函数可以接受任意类型的序列(list、tuple、ndarray)或是迭代器。你可先检验对象是否是列表(或是NUmPy数组),如果不是的话,将其转变成列表:
if not isinstance(x, list) and isiterable(x):
x = list(x)
引入
在Python中,模块就是一个有.py
扩展名、包含Python代码的文件。假设有以下模块:
# some_module.py
PI = 3.14159
def f(x):
return x + 2
def g(a, b):
return a + b
如果想从同目录下的另一个文件访问some_module.py
中定义的变量和函数,可以:
import some_module
result = some_module.f(5)
pi = some_module.PI
或者:
from some_module import f, g, PI
result = g(5, PI)
使用as
关键词,你可以给引入起不同的变量名:
import some_module as sm
from some_module import PI as pi, g as gf
r1 = sm.f(pi)
r2 = gf(6, pi)
二元运算符和比较运算符
判断两个引用是否指向同一个对象,可以使用is
方法。is not
可以判断两个对象是不同的:
In [35]: a = [1, 2, 3]
In [36]: b = a
In [37]: c = list(a)
In [38]: a is b
Out[38]: True
In [39]: a is not c
Out[39]: True
因为list
总是创建一个新的Python列表(即复制),我们可以断定c是不同于a的。使用is
比较与==
运算符不同,如下:
In [40]: a == c
Out[40]: True
is
和is not
常用来判断一个变量是否为None
,因为只有一个None
的实例:
In [41]: a = None
In [42]: a is None
Out[42]: True
可变与不可变对象
Python中的大多数对象,比如列表、字典、NumPy数组,和用户定义的类型(类),都是可变的。意味着这些对象或包含的值可以被修改:
In [43]: a_list = ['foo', 2, [4, 5]]
In [44]: a_list[2] = (3, 4)
In [45]: a_list
Out[45]: ['foo', 2, (3, 4)]
其它的,例如字符串和元组,是不可变的:
In [46]: a_tuple = (3, 5, (4, 5))
In [47]: a_tuple[1] = 'four'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-47-b7966a9ae0f1> in <module>()
----> 1 a_tuple[1] = 'four'
TypeError: 'tuple' object does not support item assignment
标量类型
Python的标准库中有一些内建的类型,用于处理数值数据、字符串、布尔值,和日期时间。这些单值类型被称为标量类型,本书中称其为标量。日期和时间处理会另外讨论,因为它们是标准库的datetime
模块提供的。
数值类型
Python的主要数值类型是int
和float
。int
可以存储任意大的数:
In [48]: ival = 17239871
In [49]: ival ** 6
Out[49]: 26254519291092456596965462913230729701102721
浮点数使用Python的float
类型。每个数都是双精度(64位)的值。也可以用科学计数法表示:
In [50]: fval = 7.243
In [51]: fval2 = 6.78e-5
不能得到整数的除法会得到浮点数:
In [52]: 3 / 2
Out[52]: 1.5
要获得C-风格的整除(去掉小数部分),可以使用底除运算符//:
In [53]: 3 // 2
Out[53]: 1
字符串
可以用单引号或双引号来写字符串:
a = 'one way of writing a string'
b = "another way"
对于有换行符的字符串,可以使用三引号,'''或"""都行:
c = """
This is a longer string that
spans multiple lines
"""
字符串c
实际包含四行文本,"""后面和lines后面的换行符。可以用count
方法计算c
中的新的行:
In [55]: c.count('\n')
Out[55]: 3
Python的字符串是不可变的,不能修改字符串:
In [56]: a = 'this is a string'
In [57]: a[10] = 'f'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-57-5ca625d1e504> in <module>()
----> 1 a[10] = 'f'
TypeError: 'str' object does not support item assignment
In [58]: b = a.replace('string', 'longer string')
In [59]: b
Out[59]: 'this is a longer string'
经过以上的操作,变量a
并没有被修改:
In [60]: a
Out[60]: 'this is a string'
许多Python对象使用str
函数可以被转化为字符串:
In [61]: a = 5.6
In [62]: s = str(a)
In [63]: print(s)
5.6
字符串是一个序列的Unicode字符,因此可以像其它序列,比如列表和元组一样处理:
In [64]: s = 'python'
In [65]: list(s)
Out[65]: ['p', 'y', 't', 'h', 'o', 'n']
In [66]: s[:3]
Out[66]: 'pyt'
语法s[:3]
被称作切片,适用于许多Python序列。
反斜杠是转义字符,意思是它备用来表示特殊字符,比如换行符\n或Unicode字符。要写一个包含反斜杠的字符串,需要进行转义:
In [67]: s = '12\\34'
In [68]: print(s)
12\34
如果字符串中包含许多反斜杠,但没有特殊字符,这样做就很麻烦。幸好,可以在字符串前面加一个r,表明字符就是它自身:
In [69]: s = r'this\has\no\special\characters'
In [70]: s
Out[70]: 'this\\has\\no\\special\\characters'
r表示raw。
将两个字符串合并,会产生一个新的字符串:
In [71]: a = 'this is the first half '
In [72]: b = 'and this is the second half'
In [73]: a + b
Out[73]: 'this is the first half and this is the second half'
字符串的模板化或格式化,是另一个重要的主题。Python 3拓展了此类的方法,这里只介绍一些。字符串对象有format
方法,可以替换格式化的参数为字符串,产生一个新的字符串:
In [74]: template = '{0:.2f} {1:s} are worth US${2:d}'
在这个字符串中,
{0:.2f}
表示格式化第一个参数为带有两位小数的浮点数。{1:s}
表示格式化第二个参数为字符串。{2:d}
表示格式化第三个参数为一个整数。
要替换参数为这些格式化的参数,我们传递format
方法一个序列:
In [75]: template.format(4.5560, 'Argentine Pesos', 1)
Out[75]: '4.56 Argentine Pesos are worth US$1'
字符串格式化是一个很深的主题,有多种方法和大量的选项,可以控制字符串中的值是如何格式化的。推荐参阅Python官方文档。
字节和Unicode
在Python 3及以上版本中,Unicode是一级的字符串类型,这样可以更一致的处理ASCII和Non-ASCII文本。在老的Python版本中,字符串都是字节,不使用Unicode编码。假如知道字符编码,可以将其转化为Unicode。看一个例子:
In [76]: val = "español"
In [77]: val
Out[77]: 'español'
可以用encode
将这个Unicode字符串编码为UTF-8:
In [78]: val_utf8 = val.encode('utf-8')
In [79]: val_utf8
Out[79]: b'espa\xc3\xb1ol'
In [80]: type(val_utf8)
Out[80]: bytes
如果你知道一个字节对象的Unicode编码,用decode
方法可以解码:
In [81]: val_utf8.decode('utf-8')
Out[81]: 'español'
工作中碰到的文件很多都是字节对象,盲目地将所有数据编码为Unicode是不可取的。
虽然用的不多,你可以在字节文本的前面加上一个b:
In [85]: bytes_val = b'this is bytes'
In [86]: bytes_val
Out[86]: b'this is bytes'
In [87]: decoded = bytes_val.decode('utf8')
In [88]: decoded # this is str (Unicode) now
Out[88]: 'this is bytes'
None
None是Python的空值类型。如果一个函数没有明确的返回值,就会默认返回None:
In [97]: a = None
In [98]: a is None
Out[98]: True
In [99]: b = 5
In [100]: b is not None
Out[100]: True
None也常常作为函数的默认参数:
def add_and_maybe_multiply(a, b, c=None):
result = a + b
if c is not None:
result = result * c
return result
另外,None不仅是一个保留字,还是唯一的NoneType的实例:
In [101]: type(None)
Out[101]: NoneType
日期和时间
Python内建的datetime
模块提供了datetime
、date
和time
类型。datetime
类型结合了date
和time
,是最常使用的:
In [102]: from datetime import datetime, date, time
In [103]: dt = datetime(2011, 10, 29, 20, 30, 21)
In [104]: dt.day
Out[104]: 29
In [105]: dt.minute
Out[105]: 30
根据datetime
实例,你可以用date
和time
提取出各自的对象:
In [106]: dt.date()
Out[106]: datetime.date(2011, 10, 29)
In [107]: dt.time()
Out[107]: datetime.time(20, 30, 21)
strftime
方法可以将datetime格式化为字符串:
In [108]: dt.strftime('%m/%d/%Y %H:%M')
Out[108]: '10/29/2011 20:30'
strptime
可以将字符串转换成datetime
对象:
In [109]: datetime.strptime('20091031', '%Y%m%d')
Out[109]: datetime.datetime(2009, 10, 31, 0, 0)
表2-5列出了所有的格式化命令。
当你聚类或对时间序列进行分组,替换datetimes的time字段有时会很有用。例如,用0替换分和秒:
In [110]: dt.replace(minute=0, second=0)
Out[110]: datetime.datetime(2011, 10, 29, 20, 0)
因为datetime.datetime
是不可变类型,上面的方法会产生新的对象。
两个datetime对象的差会产生一个datetime.timedelta
类型:
In [111]: dt2 = datetime(2011, 11, 15, 22, 30)
In [112]: delta = dt2 - dt
In [113]: delta
Out[113]: datetime.timedelta(17, 7179)
In [114]: type(delta)
Out[114]: datetime.timedelta
结果timedelta(17, 7179)
指明了timedelta
将17天、7179秒的编码方式。
将timedelta
添加到datetime
,会产生一个新的偏移datetime
:
In [115]: dt
Out[115]: datetime.datetime(2011, 10, 29, 20, 30, 21)
In [116]: dt + delta
Out[116]: datetime.datetime(2011, 11, 15, 22, 30)
控制流
if、elif和else
if
后面可以跟一个或多个elif
,所有条件都是False时,还可以添加一个else
:
if x < 0:
print('It's negative')
elif x == 0:
print('Equal to zero')
elif 0 < x < 5:
print('Positive but smaller than 5')
else:
print('Positive and larger than or equal to 5')
如果某个条件为True,后面的elif
就不会被执行。当使用and和or时,复合条件语句是从左到右执行,也可以把比较式串在一起:
In [120]: 4 > 3 > 2 > 1
Out[120]: True
for循环
for循环是在一个集合(列表或元组)中进行迭代,或者就是一个迭代器。for循环的标准语法是:
for value in collection:
# do something with value
你可以用continue使for循环提前,跳过剩下的部分。看下面这个例子,将一个列表中的整数相加,跳过None。
break只中断for循环的最内层,其余的for循环仍会运行。
如果集合或迭代器中的元素序列(元组或列表),可以用for循环将其方便地拆分成变量:
for a, b, c in iterator:
# do something
While循环
while循环指定了条件和代码,当条件为False或用break退出循环,代码才会退出:
x = 256
total = 0
while x > 0:
if total > 500:
break
total += x
x = x // 2
pass
pass是Python中的非操作语句。代码块不需要任何动作时可以使用(作为未执行代码的占位符);因为Python需要使用空白字符划定代码块,所以需要pass:
if x < 0:
print('negative!')
elif x == 0:
# TODO: put something smart here
pass
else:
print('positive!')
range
range函数返回一个迭代器,它产生一个均匀分布的整数序列:
In [122]: range(10)
Out[122]: range(0, 10)
In [123]: list(range(10))
Out[123]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range的三个参数是(起点,终点,步进):
In [124]: list(range(0, 20, 2))
Out[124]: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
In [125]: list(range(5, 0, -1))
Out[125]: [5, 4, 3, 2, 1]
可以看到,range产生的整数不包括终点。range的常见用法是用序号迭代序列:
seq = [1, 2, 3, 4]
for i in range(len(seq)):
val = seq[i]
可以使用list来存储range在其他数据结构中生成的所有整数,默认的迭代器形式通常是你想要的。虽然range可以产生任意大的数,但任意时刻耗用的内存却很小。
三元表达式
Python中的三元表达式可以将if-else语句放到一行里。语法如下:
value = true-expr if condition else false-expr
true-expr
或false-expr
可以是任何Python代码。它和下面的代码效果相同:
if condition:
value = true-expr
else:
value = false-expr
虽然使用三元表达式可以压缩代码,但会降低代码可读性。