本文首发于“生信补给站”公众号 https://mp.weixin.qq.com/s/-x2V_41lJlQX4xp8GXPKLA
这三个在特定情况下都可以省去,我们来看看四种情况:
情况 1 - start :
print( l )print( l[3:] )print( l[-4:] )
[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1] [1000, 1, 3, 7, 2, 0, 1] [7, 2, 0, 1]
以 step 为 1 (默认) 从编号 start 往列表尾部切片。
情况 2 - : stop
print( l )print( l[:6] )print( l[:-4] )
[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1] [7, 2, 999, 1000, 1, 3] [7, 2, 999, 1000, 1, 3]
以 step 为 1 (默认) 从列表头部往编号 stop 切片。
情况 3 - start : stop
print( l )print( l[2:4] )print( l[-5:-1] )
[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1] [999, 1000] [3, 7, 2, 0]
以 step 为 1 (默认) 从编号 start 往编号 stop 切片。
情况 4 - start : stop : step
print( l )print( l[1:5:2] )print( l[:5:2] )print( l[1::2] )print( l[::2] )print( l[::-1] )
[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1] [2, 1000] [7, 999, 1] [2, 1000, 3, 2, 1] [7, 999, 1, 7, 0] [1, 0, 2, 7, 3, 1, 1000, 999, 2, 7]
以具体的 step 从编号 start 往编号 stop 切片。注意最后把 step 设为 -1,相当于将列表反向排列。
列表拼接
和元组拼接一样, 列表拼接也有两种方式,用「加号 +」和「乘号 *」,前者首尾拼接,后者复制拼接。
[1, 10.31, 'python'] + ['data', 11] + ['OK'][1, 10.31, 'python'] * 2
[1, 10.31, 'python', 'data', 11, 'OK'] [1, 10.31, 'python', 1, 10.31, 'python']
优点缺点
优点:灵活好用,可索引、可切片、可更改、可附加、可插入、可删除。
缺点:相比 tuple 创建和遍历速度慢,占内存。此外查找和插入时间较慢。
2.4 字典
创建字典
「字典」定义语法为
{元素1, 元素2, ..., 元素n}
其中每一个元素是一个「键值对」- 键:值 (key:value)
关键点是「大括号 {}」,「逗号 ,」和「分号 :」
- 大括号把所有元素绑在一起
- 逗号将每个键值对一一分开
- 分号将键和值分开
创建字典的例子如下:
d = {'Name' : 'Tencent','Country' : 'China','Industry' : 'Technology','Code': '00700.HK','Price' : '361 HKD'}print( d, type(d) )
{'Name': 'Tencent', 'Country': 'China', 'Industry': 'Technology', 'Code': '00700.HK', 'Price': '361 HKD'} <class 'dict'>
内置方法
字典里最常用的三个内置方法就是 keys(), values() 和 items(),分别是获取字典的键、值、对。
print( list(d.keys()),'\n' )print( list(d.values()), '\n' )print( list(d.items()) )
['Name', 'Country', 'Industry', 'Code', 'Price', 'Headquarter'] ['Tencent', 'China', 'Technology', '00700.HK', '359 HKD', 'Shen Zhen'] [('Name', 'Tencent'), ('Country', 'China'), ('Industry', 'Technology'), ('Code', '00700.HK'), ('Price', '359 HKD'), ('Headquarter', 'Shen Zhen')]
此外在字典上也有添加、获取、更新、删除等操作。
添加
比如加一个「总部:深圳」
d['Headquarter'] = 'Shen Zhen'd
{'Name': 'Tencent', 'Country': 'China', 'Industry': 'Technology', 'Code': '00700.HK', 'Price': '361 HKD', 'Headquarter': 'Shen Zhen'}
获取
比如想看看腾讯的股价是多少 (两种方法都可以)
print( d['Price'] )print( d.get('Price') )
359 HKD 359 HKD
更新
比如更新腾讯的股价到 359 港币
d['Price'] = '359 HKD'd
{'Name': 'Tencent', 'Country': 'China', 'Industry': 'Technology', 'Code': '00700.HK', 'Price': '359 HKD', 'Headquarter': 'Shen Zhen'}
删除
比如去掉股票代码 (code)
del d['Code']d
{'Name': 'Tencent', 'Country': 'China', 'Industry': 'Technology', 'Price': '359 HKD', 'Headquarter': 'Shen Zhen'}
或像列表里的 pop() 函数,删除行业 (industry) 并返回出来。
print( d.pop('Industry') )d
Technology {'Name': 'Tencent', 'Country': 'China', 'Price': '359 HKD', 'Headquarter': 'Shen Zhen'}
不可更改键
字典里的键是不可更改的,因此只有那些不可更改的数据类型才能当键,比如整数 (虽然怪怪的)、浮点数 (虽然怪怪的)、布尔 (虽然怪怪的)、字符、元组 (虽然怪怪的),而列表却不行,因为它可更改。看个例子
d = {2 : 'integer key',10.31 : 'float key',True : 'boolean key',('OK',3) : 'tuple key'}d
{2: 'integer key', 10.31: 'float key', True: 'boolean key', ('OK', 3): 'tuple key'}
虽然怪怪的,但这些 2, 10.31, True, ('OK', 3) 确实能当键。有个地方要注意下,True 其实和整数 1 是一样的,由于键不能重复,当你把 2 该成 1时,你会发现字典只会取其中一个键,示例如下:
d = {1 : 'integer key',10.31 : 'float key',True : 'boolean key',('OK',3) : 'tuple key'}d
{1: 'boolean key', 10.31: 'float key', ('OK', 3): 'tuple key'}
那么如何快速判断一个数据类型 X 是不是可更改的呢?两种方法:
- 麻烦方法:用 id(X) 函数,对 X 进行某种操作,比较操作前后的 id,如果不一样,则 X 不可更改,如果一样,则 X 可更改。
- 便捷方法:用 hash(X),只要不报错,证明 X 可被哈希,即不可更改,反过来不可被哈希,即可更改。
先看用 id() 函数的在整数 i 和列表 l 上的运行结果:
i = 1print( id(i) )i = i + 2print( id(i) )
1607630928 1607630992
l = [1, 2]print( id(l) )l.append('Python')print( id(l) )
2022027856840 2022027856840
- 整数 i 在加 1 之后的 id 和之前不一样,因此加完之后的这个 i (虽然名字没变),但是不是加前的那个 i 了,因此整数是不可更改的。
- 列表 l 在附加 'Python' 之后的 id 和之前一样,因此列表是可更改的。
先看用 hash() 函数的在字符 s,元组 t 和列表 l 上的运行结果:
hash('Name')
7230166658767143139
hash( (1,2,'Python') )
3642952815031607597
hash( [1,2,'Python'] )
TypeError: unhashable type: 'list'
字符 s 和元组 t 都能被哈希,因此它们是不可更改的。列表 l 不能被哈希,因此它是可更改的。
优点缺点
优点:查找和插入速度快
缺点:占内存大
2.5 集合
创建集合
「集合」有两种定义语法,第一种是
{元素1, 元素2, ..., 元素n}
关键点是「大括号 {}」和「逗号 ,」
- 大括号把所有元素绑在一起
- 逗号将每个元素一一分开
第二种是用 set() 函数,把列表或元组转换成集合。
set( 列表 或 元组)
创建集合的例子如下 (用两者方法创建 A 和 B):
A = set(['u', 'd', 'ud', 'du', 'd', 'du'])B = {'d', 'dd', 'uu', 'u'}print( A )print( B )
{'d', 'du', 'u', 'ud'} {'d', 'dd', 'u', 'uu'}
从 A 的结果发现集合的两个特点:无序 (unordered) 和唯一 (unique)。由于 set 存储的是无序集合,所以我们没法通过索引来访问,但是可以判断一个元素是否在集合中。
B[1]
TypeError: 'set' object does not support indexing
'u' in B
True
内置方法
用 set 的内置方法就把它当成是数学上的集,那么并集、交集、差集都可以玩通了。
并集 OR
print( A.union(B) ) # All unique elements in A or Bprint( A | B ) # A OR B
{'uu', 'dd', 'd', 'u', 'du', 'ud'} {'uu', 'dd', 'd', 'u', 'du', 'ud'}
交集 AND
print( A.intersection(B) ) # All elements in both A and Bprint( A & B ) # A AND B
{'d', 'u'} {'d', 'u'}
差集 A - B
print( A.difference(B) ) # Elements in A but not in Bprint( A - B ) # A MINUS B
{'ud', 'du'} {'ud', 'du'}
差集 B - A
print( B.difference(A) ) # Elements in B but not in Aprint( B - A ) # B MINUS A
{'uu', 'dd'} {'uu', 'dd'}
对称差集 XOR
print( A.symmetric_difference(B) ) # All elements in either A or B, but not bothprint( A ^ B ) # A XOR B
{'ud', 'du', 'dd', 'uu'} {'ud', 'du', 'dd', 'uu'}
优点缺点
优点:不用判断重复的元素
缺点:不能存储可变对象
你看集合的「唯一」特性还不是双刃剑,既可以是优点,又可以是缺点,所有东西都有 trade-off 的,要不然它就没有存在的必要了。
3条件语句 & 迭代循环
在编写程序时,我们要
- 在不同条件下完成不同动作,条件语句 (conditional statement) 赋予程序这种能力。
- 重复的完成某些动作,迭代循环 (iterative loop) 赋予程序这种能力。
3.1 条件语句
条件语句太简单了,大体有四种格式
- if 语句
- if-else 语句
- if-elif-else 语句
- nested 语句
看了下面四幅图 (包含代码) 应该秒懂条件语句,其实任何会说话的人都应该懂它。
if
if x > 0: print( 'x is positive' )
给定二元条件,满足做事,不满足不做事。
if-else
if x % 2 == 0: print( 'x is even' )else : print( 'x is odd' )
给定二元条件,满足做事 A,不满足做事 B。
if-elif-else
if x < y: print( 'x is less than y' )elif x > y: print( 'x is greater than y' )else: print( 'x and y are equal' )
给定多元条件,满足条件 1 做事 A1,满足条件 2 做事 A2,..., 满足条件 n 做事 An。直到把所有条件遍历完。
Nested if
if x == y: print( 'x and y are equal' )else: if x < y: print( 'x is less than y' ) else: print( 'x is greater than y' )
给定多元条件,满足条件 1 做事 A1,不满足就
给定多元条件,满足条件 2 做事 A2,不满足就
...
直到把所有条件遍历完。
3.2 迭代循环
对于迭代循环,Python 里面有「while 循环」和「for 循环」,没有「do-while 循环」。
while 循环
n = 5while n > 0: print(n) n = n-1print('I love Python')
5 4 3 2 1 I love Python
While 循环非常简单,做事直到 while 后面的语句为 False。上例就是打印从 n (初始值为 5) 一直到 1,循环执行了 5 次。
一般来说,在 「while 循环」中,迭代的次数事先是不知道的,因为通常你不知道 while 后面的语句从 True 变成 False了。
for 循环
更多时候我们希望事先直到循环的次数,比如在列表、元组、字典等容器类数据上遍历一遍,在每个元素层面上做点事情。这时候就需要「for 循环」了。
languages = ['Python', 'R', 'Matlab', 'C++']for language in languages: print( 'I love', language )print( 'Done!' )
I love Python I love R I love Matlab I love C++ Done!
读读 Python 里面的「for 循环」是不是很像读英文。通用形式的 for loop 如下
for ainA
do something with a
其中 for 和 in是关键词,A 是个可迭代数据 (list, tuple, dic, set),a 是 A 里面的每个元素,上句翻译成中文是
对于 A 里面的每个 a
对 a 搞点事
回到具体例子,for loop 里面的 language 变量在每次循环中分别取值 Python, R, Matlab 和 C++,然后被打印。
最后介绍一个稍微有点特殊的函数 enumerate(),和 for loop 一起用的语法如下
for i, ainenumerate(A)
do something with a
发现区别了没?用 enumerate(A) 不仅返回了 A 中的元素,还顺便给该元素一个索引值 (默认从 0 开始)。此外,用 enumerate(A, j) 还可以确定索引起始值为 j。 看下面例子。
languages = ['Python', 'R', 'Matlab', 'C++']for i, language in enumerate(languages, 1): print( i, 'I love', language )print( 'Done!' )
1 I love Python 2 I love R 3 I love Matlab 4 I love C++ Done!
总结
学习任何一种都要从最基本开始,基本的东西无外乎数据类型、条件语句和递推循环。
数据类型分两种:
- 单独类型:整型、浮点型、布尔型
- 容器类型:字符、元组、列表、字典、集合
按照 Python 里「万物皆对象」的思路,学习每一个对象里面的属性 (attributes) 和方法 (methods),你不需要记住所有,有个大概印象有哪些,通过 dir() 来锁定具体表达式,再去官网上查询所有细节。这么学真的很系统而简单。此外学的时候一定要带着“它的优缺点是什么”这样的问题,所有东西都有 trade-off,一个满身都是缺点的东西就没有存在的必要,既然存在肯定有可取之处。
条件语句 (if, if-else, if-elif-else, nested if) 是为了在不同条件下执行不同操作,而迭代循环 (while, for) 是重复的完成相同操作。
抓住上面大框架,最好还要以目标导向 (我学 python 就是为了搞量化交易希望能躺着赚钱),别管这目标难度如何,起码可以保证我累得时候还鸡血满满不会轻言放弃。这样我就足够主动的去学一样东西并学精学深,目标越难完成,我主动性就越强。
之后所有的细节都可以慢慢来,虽然我觉得本篇已经挖了不少细节了,像 hashability,但肯定还有更多等着去挖,半篇帖子就想覆盖 Python 所有内容不是开玩笑吗?但
抓住大框架,有目标导向,对有效学习任何内容都适用。
下篇接着函数 (function) 和解析式 (comprehension)。Stay Tuned!