不想再像之前那样,什么都从头开始学习语法、总结语法,这样反而会过度纠结于语法,耽误了开发,毕竟语言的主要属性是工具,次要的属性是语言自己。html
因此仍是先熟练使用语言去进行开发,等足够熟悉了,再去研究语言自己(编译原理……)。python
另外对于算法、设计模式、数据结构、网络知识、操做系统…… 的学习能够专门针对性的学习,固然更好的方法是以使用语言为主线,经过具体的应用和实践来推进这些技术知识的学习。算法
本文是经过廖雪峰的网站学习而整理的(真的是很好的教程,免得我花钱买书了!),而后我没有去再整理总结语法,而是直接经过写出代码段来体现本身的学习,也方便之后的快速复习、回顾。毕竟学习一门语言不是一天能够完成的,因此本文也不是一蹴而就的,而是会一直更新。编程
也没有必要再对代码作过多的文字解释,一切都能经过代码自己体现。windows
在Python的交互式命令行写程序,好比>>>print('hello world')
,好处是一下就能获得结果,坏处是无法保存,下次还想运行的时候,还要再敲一遍。设计模式
因此实际的开发中,咱们使用一个文本编辑器来写代码,而后保存为一个文件,这样程序就能够反复运行了。好比将print('heool world')
写在文档里注意print前面不能有任何空格,并保存为 hello.py,而后使用命令行进入该文件所在的目录,执行python hello.py
就能够解析执行这个源文件了。数组
绝对不要使用windows自带的记事本:记事本会自做聪明地在文件开始的地方加上几个特殊字符(UTF-8 BOM),结果会致使程序运行出现莫名其妙的错误。安全
能不能像.exe文件那样直接运行.py文件呢?在Windows上是不行的,可是,在Mac和Linux上是能够的,方法是在.py文件(好比是hello.py)的第一行加上一个特殊的注释:网络
#!/usr/bin/env python3 print('hello, world')
接着,经过命令行给hello.py以执行权限:chmod a+x hello.py
,而后就能够在文件所在目录下直接输入./hello.py
运行。数据结构
这个和Shell有些像!
>>>print('测试一个运算式', '1+2=', 1+2) 测试一个运算式 1+2= 3
注意遇到print里面将参数分开的逗号会输出空格。
>>>name = input() xumenger
当你输入完name = input()
并按下回车后,Python交互式命令行就等待你的输入,以这个例子,输入xumenger,而后回车完成输入,这时候输入的字符串就存入到name
变量里了。
编写一个test.py文件
name = input('请你输入你的姓名: ') print('hello', name)
input里面的字符串参数做为输出提示用,而后等待输入,输入的字符串(好比xumenger)存入name,而后再输出 hello xumenger
。
input()
和print()
是在命令行下面最基本的输入和输出,可是,用户也能够经过其余更高级的图形界面完成输入和输出,好比,在网页上的一个文本框输入本身的名字,点击“肯定”后在网页上看到输出信息。
2015.09.06 23:40,明天开始学习Python基础,先去睡觉!
Python语法简单,采用缩进来控制逻辑。没有规定是几个空格仍是Tab,可是按照约定俗成的管理,应该始终坚持使用4个空格的缩进。在文本编辑器中,须要设置把Tab自动转换为4个空格,确保不混用Tab和空格。
# print absolute value of an integer: a = 100 if a >= 0: print(a) else: print(-a)
以#
开头的语句是注释,当语句以:
结尾时,缩进的语句视为代码块。
整数、浮点数
整数和浮点数在计算机内部存储的方式是不一样的,整数运算永远是精确的(除法难道也是精确的?是的!),而浮点数运算则可能会有四舍五入的偏差。
字符串(使用单引号或者双引号引发来)
若是字符串内部包含'
又包含"
怎么办,须要用\
来转义
print('I\'m \"OK\"!')
表示:I'm "OK"!
判断
if age==18 print('值为18') else print('值不是18')
布尔值(True、False,用and、or和not运算)
空值
用 None
表示,None不能理解为0,由于0是有意义的,而None是一个特殊的空值。
变量
=
是赋值号,另外Python是动态语言,变量自己类型不固定。与之对应的是静态语言,静态语言在定义变量的时候必须指定变量的类型,若是赋值的时候类型不匹配,就会报错,像C、C++、Java。
a='ABC'
Python解释器解释它时,,干了两件事
在内存中建立一个'ABC'
的字符串
在内存中建立了一个名为a
的变量,并将它指向'ABC'
也能够把一个变量a赋值给另外一个变量b,这个操做其实是把变量b指向变量a所指向的数据,例以下面的代码:
a = 'ABC' b = a a = 'XYZ' print(b)
最后一行打印出变量b的内容究竟是'ABC'呢仍是'XYZ'?若是从数学意义上理解,就会错误地得出b和a相同,也应该是'XYZ',但实际上b的值是'ABC'。一步一步理解代码
在内存中建立 'ABC' 字符串
在内存中建立 a 变量,并将 a 指向 'ABC'
在内存中建立 b 变量,由于将 a 值赋给 b,因此这是b也指向 'ABC'
而后又在内存中建立了 'XYZ' 字符串,并将 a 指向 'XYZ',可是此时b 仍是指向 'ABC'
10/3
获得的结果是 3.33333333333339/3
获得的结果是 3.010//3
获得的结果是 310%3
获得的结果是 1
这个知识点之前我一直存在疑惑,廖雪峰的教程里面讲得仍是很好的,点击这里认真看。重点是字符编码的原理、如今计算机系统通用的字符编码工做方式、Python中的编码方式、乱码的解释。
在最新的Python 3版本中,字符串是以 Unicode编码的,也就是说Python的字符串支持多种语言。
关于Python的字符串类型和bytes类型、在网络传输或者保存到磁盘时候字符串类型与bytes类型的转换,参考对应的廖雪峰的 字符串和编码 教程,有详细的讲解。
1个中文字符通过UTF-8编码后一般会占用3个字节,而1个英文字符只占用1个字节。
print('Hi, %s, you have $%d.' % ('loser', 10))
输出是:Hi, loser, you have $10
print('Age: %s, Gender: %s' % (25, True))
输出是:Age: 25, Gender: True
list是一种有序的集合用[]
,能够随时添加和删除其中的元素
mates = ['Machael', 'Bob', 'Tracy'] print('%s' % (len(mates)) print(mates[0]) #第一个元素 print(mates[3]) #越界报错 print(mates[-1]) #倒数第一个元素 print(mates[-2]) #倒数第二个元素 print(mates[-4]) #越界报错 mates.append('xumenger') #list是可变列表,如今是在list末尾追加元素 mates.insert(1,'Joker') #插入一个元素在第二个位置 mates.pop() #删除末尾的元素 mates.pop(1) #删除第二个元素 mates[1] = 'change' #替换第2个元素 L= ['test', 123, True] #list里面能够是不一样类型的元素 s= ['test', 1, ['asp', 2], True] #list的元素能够是另外一个list list1 = ['test', True] list2 = [2, False, list1] print(list2[2][0]) #能够当作是一个二维数组,相似的还有三维、四维……数组,不过不多用到。
tuple和list很类似,可是tuple是一旦初始化就不能再修改的,用()
mates= ('xumeng', 'joker', 'test')
如今,mates这个tuple不能变了,它也没有append(),insert()这样的方法。其余获取元素的方法和list是同样的,你能够正常地使用mates[0],mates[-1],但不能赋值成另外的元素。
不可变的tuple有什么意义?由于tuple不可变,因此代码更安全。若是可能,能用tuple代替list就尽可能用tuple。
t=(1)
定义的不是tuple,是1这个数!这是由于括号()既能够表示tuple,又能够表示数学公式中的小括号,这就产生了歧义,所以,Python规定,这种状况下,按小括号进行计算,计算结果天然是1。
要想定义只有一个元素的tuple,应该这样t=(1,)
“可变的tuple”,能够是tuple有一个list元素,而后里面的list可变,能够看教程中 对应部分 经过展现内存中的存储原理来讲明缘由:
t = ('a', 'b', ['A', 'B']) t[2][0] = 'X'
a = input('输入你的年龄: ') age = int(a) #由于input输入的是字符串,因此要转换成整型 if age >= 18: #注意冒号 : print('adult, your age is', age) elif age >= 6: #注意冒号 : print('teenager, your age is', age) else: #注意冒号 : print('kid')
names = ['nn', 'aa', 'mm']
for name in names: #注意冒号 :
print(name)
sum = 0
for x in[1, 2, 3, 4, 5]
sum = sum +x
print(sum)
sum = 0
for x in range(101): #range(101)就能够生成0-100的整数序列
sum = sum + x
print(sum)
sum = 0
n = 99
while n>0: #注意冒号:
sum = sum +n n= n-2
print(sum)
Python内置了字典:dict的支持,全称为dictionary,在其余语言中也成为map,使用键-值(key-value)存储,具备几块的查找速度,注意使用{}
。
d = {'key1': 95, 'key2': 54, 'key3': 100} print(d['key1']) #把数据放入dict的方法,除了初始化时指定外,还能够经过key放入 d['key4'] = 123 #因为一个key只能对应一个value,因此,屡次对一个key放入value,后面的值会把前面的值冲掉 d['key1'] = 111 if 'key2' in d: #判断某个键是否是在dict中 print('在') if get('key2', -1) == -1: #若是有'key2'则获取对应的value,不然就返回-1 print('不在') d.pop('key3') #使用pop删除对应的键和值
为何dict查找速度这么快?由于dict的实现原理和查字典是同样的。假设字典包含了1万个汉字,咱们要查某一个字,一个办法是把字典从第一页日后翻,直到找到咱们想要的字为止,这种方法就是在list中查找元素的方法,list越大,查找越慢。
第二种方法是先在字典的索引表里(好比部首表)查这个字对应的页码,而后直接翻到该页,找到这个字。不管找哪一个字,这种查找速度都很是快,不会随着字典大小的增长而变慢。
你能够猜到,这种key-value存储方式,在放进去的时候,必须根据key算出value的存放位置,这样,取的时候才能根据key直接拿到value。
请务必注意,dict内部存放的顺序和key放入的顺序是没有关系的。
dict能够用在须要高速查找的不少地方,在Python代码中几乎无处不在,正确使用dict很是重要,须要牢记的第一条就是dict的key必须是不可变对象。
这是由于dict根据key来计算value的存储位置,若是每次计算相同的key得出的结果不一样,那dict内部就彻底混乱了。这个经过key计算位置的算法称为哈希算法(Hash)。
要保证hash的正确性,做为key的对象就不能变。在Python中,字符串、整数等都是不可变的,所以,能够放心地做为key。而list是可变的,就不能做为key。
set和dict相似,也是一组key的集合,可是不存储value,因为key不能重复,因此在set中,没有重复的key。
#要建立一个set,须要提供一个list做为输入结合 s1 = set([1, 2, 3]) #重复元素在set中被过滤,好比下面的语句,其实只要1,2,3 s= set([1, 1, 2, 2, 2, 3, 3]) #经过add(key)添加元素,重复添加的元素只会保留一个 s.add(4) #remove(key) 删除元素 s.remove(1,2,3) #set能够当作数学意义上的无序和无重复元素的集合,所以,两个set能够作数学意义上的交集、并集等操做 s2 = set([1,2,3]) s3 = set([2,3,4]) s4 = s2 & s3 #s4是s2和s3的交集 s5 = s2 | s3 #s4是s2和s3的并集
set和dict的惟一区别仅在于没有存储对应的value,可是,set的原理和dict同样,因此,一样不能够放入可变对象,由于没法判断两个可变对象是否相等,也就没法保证set内部“不会有重复元素”。试试把list放入set,看看是否会报错。
廖雪峰的 讲解dict和set的文章 的最后经过说明内存里面的原理讲解了可变对象与不可变对象!很好的理解Python和内存机制的一个知识点!
tuple虽然是不变对象,但试试把(1, 2, 3)和(1, [2, 3])放入dict或set中,并解释结果。
s1 = set([(1,2,3), (2,3,4)]) #这样的tuple能够放进set s2 = set([(1,2, [1,2]), (2,3, [6,8])]) #这样的tuple不能放进set,这是“可变的tuple”
2015.09.07 23:45, 明天开始学习 Python的函数 ,如今赶忙睡觉,身体最重要!
要调用一个函数,须要知道函数的名称和参数,好比求绝对值的函数abs,只有一个参数。能够直接从Python的官方网站查看文档:http://docs.python.org/3/library/functions.html#abs
也能够在交互式命令行经过help(abs)查看abs函数的帮助信息。
print(abs(-1)) print(max(1,2,3,4)) print(max(1,2,3)) print(int('123')) #强制类型转换 print(float('12.34)) print(str(100)) print(bool(1)) #输出True print(bool('')) #输出False
函数名实际上是一个指向函数对象的引用,彻底能够把一个函数名赋值给一个变量,至关于给这个函数起了一个别名:
a=abs print(a(-1)) m=max print(max(1,2,3))
在Python中,定义一个函数要使用def
语句,依次写出函数名、括号、括号里的参数和冒号: ,而后,在缩进块中编写函数体,函数的返回值用return语句返回。
def my_abs(x): if not isinstance(x, (int, float)): raise TypeError('bad operand type') if x>=0: return x else: return -x
上面的函数中,对参数类型作检查,只容许整数和浮点数类型的参数,不然就raise一个异常。若是有必要,能够先对参数的数据类型作检查,就像这个函数定义,就能够保证只处理int和float,而假如传入的是str就会抛出异常。
请注意,函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。所以,函数内部经过条件判断和循环能够实现很是复杂的逻辑。
若是没有return语句,函数执行完毕后也会返回结果,只是结果为None。return None能够简写为return。
import math def move(x, y, step, angle=0): nx = x + step * math.cos(angle) ny = y - step * math.sin(angle) return nx, ny
import math语句表示导入math包,并容许后续代码引用math包里的sin、cos等函数。上面的这个函数有两个返回值,咱们能够这样调用
x, y = move(100, 100, 60, math, pi/6) print(x, y)
可是,其实这个只是假象,Python函数返回的仍然是单一值:
r = move(100, 100, 60, math.pi / 6) print(r) #获得和上面同样的结果
原来返回值是一个tuple!可是,在语法上,返回一个tuple能够省略括号,而多个变量能够同时接收一个tuple,按位置赋给对应的值,因此,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。
廖雪峰的函数的参数 这一章讲解了位置参数、默认参数、由于参数类型不是不变对象致使使用默认参数出现的"意外"、list和tuple与可变参数、dict与关键字参数、命名关键参数。
在Python中定义函数,能够用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数均可以组合使用,除了可变参数没法和命名关键字参数混合。可是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数/命名关键字参数和关键字参数。
这个太他妈强大了!!可是有点没看懂,确实纯粹的死记硬背仍是不太有效,能够等到具体项目应用的时候在参考这篇教程!结合具体的应用再来深刻的理解,绝对事半功倍,如今就须要知道Python中有很强大的函数参数的语法,等到具体用的时候知道到哪里去找相关的资料就好了!
def fact(n) if n==1: return 1 return n* fact(n-1)
使用递归函数须要注意防止栈溢出。在计算机中,函数调用是经过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。因为栈的大小不是无限的,因此,递归调用的次数过多,会致使栈溢出。能够试试fact(1000)。
解决递归调用栈溢出的方法是经过尾递归优化,具体应用见廖雪峰的递归函数 的教程。
尾递归调用时,若是作了优化,栈不会增加,所以,不管多少次调用也不会致使栈溢出。遗憾的是,大多数编程语言没有针对尾递归作优化,Python解释器也没有作优化,因此,即便把上面的fact(n)函数改为尾递归方式,也会致使栈溢出。
掌握了Python的数据类型、语句和函数,基本上就能够编写出不少有用的程序了。
好比构造一个1, 3, 5, 7, ..., 99的列表,能够经过循环实现:
L = [] n = 1 while n <= 99: L.append(n) n = n + 2
取list的前一半的元素,也能够经过循环实现。
可是在Python中,代码不是越多越好,而是越少越好。代码不是越复杂越好,而是越简单越好。用任何的语言编程都应该是这样。
基于这一思想,咱们来介绍Python中很是有用的高级特性,1行代码能实现的功能,决不写5行代码。请始终牢记,代码越少,开发效率越高。
L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] L1 = L[:3] #['Michael', 'Sarah', 'Tracy'] L2 = L[1:3] #['Sarah', 'Tracy'] L3 = L[-2:] #['Bob', 'Jack'] L4 = L[-2:-1] #['Bob'] #list的第一个元素的索引是0,倒数第一个元素的索引是-1 LL=list(range(100)) #[1,2,3,...,99] LL1=L[-10:] #[90,91,...,99] 后10个数 LL2=L[10:20] #[10,11,12,...,19] 前11-20个数 LL3=L[:10:2] #[0,2,4,6,8] 前10个数,每两个取一个 LL4=L[::5] #[0,5,10,...,90,95] 全部数,每5个取一个 LL5=L[:] #甚至什么都不写,只写[:]就能够原样复制一个list
tuple也是一种list,惟一区别是tuple不可变。所以,tuple也能够用切片操做,只是操做的结果还是tuple。
T=(0,1,2,3,4,5) T1=T[:3]
字符串'xxx'也能够当作是一种list,每一个元素就是一个字符。所以,字符串也能够用切片操做,只是操做结果还是字符串:
T='ABCDEFG' T1=T[:3] #'ABC' T2=T[::2] #'ACEG'
在不少编程语言中,针对字符串提供了不少各类截取函数(例如,substring),其实目的就是对字符串切片。Python没有针对字符串的截取函数,只须要切片一个操做就能够完成,很是简单。
有了切片操做,不少地方循环就再也不须要了。Python的切片很是灵活,一行代码就能够实现不少行循环才能完成的操做。
Python的for循环抽象程度要高于Java的for循环,由于Python的for循环不只能够用在list或tuple上,还能够做用在其余可迭代对象上。
list这种数据类型虽然有下标,但不少其余数据类型是没有下标的,可是,只要是可迭代对象,不管有无下标,均可以迭代,好比dict就能够迭代。
d= {'a':1, 'b':2, 'c':3} for key in d: print(key) #输出a c b
为何输出的结果是a c b,不是a b c,由于dict的存储不是按照list的方式顺序排列,因此,迭代出的结果顺序极可能不同。关于dict的存储的知识,请参见对应的dict教程。
默认状况下,dict迭代的是key。若是要迭代value,能够用for value in d.values()
,若是要同时迭代key和value,能够用for k, v in d.items()
。
因为字符串也是可迭代对象,因此能够用于for循环。
for ch in 'ABCD': print ch
因此,当咱们使用for循环时,只要做用于一个可迭代对象,for循环就能够正常运行,而咱们不太关心该对象到底是list仍是其余数据类型。那么,如何判断一个对象是可迭代对象呢?方法是经过collections模块的Iterable类型判断:
from collections import Iterable isinstance('abc', Iterable) # str是否可迭代 True isinstance([1,2,3], Iterable) # list是否可迭代 True isinstance(123, Iterable) # 整数是否可迭代 False
最后一个小问题,若是要对list实现相似Java那样的下标循环怎么办?Python内置的enumerate函数能够把一个list变成索引-元素对,这样就能够在for循环中同时迭代索引和元素自己:
for i, value in enumerate(['A', 'B', 'C']): print(i, value)
上面的for循环里,同时引用了两个变量,在Python里是很常见的,好比下面的代码:
for x, y in [(1, 1), (2, 4), (3, 9)]: print(x, y)
L=list(range(1,11)) #生成[1,2,3,4,5,6,7,8,9,10] L1=[x*x for x in range(1,11)] #生成[1*1,2*2,...,10*10]
写列表生成式时,把要生成的元素好比x * x
放到前面,后面跟for循环,就能够把list建立出来,十分有用,多写几回,很快就能够熟悉这种语法。
for循环后面还能够加上if判断,这样咱们就能够筛选出仅偶数的平方:
L= [x * x for x in range(1, 11) if x % 2 == 0] #[4, 16, 36, 64, 100]
还可使用两层循环,能够生成全排列:
L= [m + n for m in 'ABC' for n in 'XYZ'] #['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
三层和三层以上的循环就不多用到了。
例程,列出当前目录下的全部文件和目录名
import os # 导入os模块,模块的概念后面讲到 L=[d for d in os.listdir('.')] # os.listdir能够列出文件和目录 print(L)
for循环其实能够同时使用两个甚至多个变量,好比dict的items()能够同时迭代key和value:
d = {'x': 'A', 'y': 'B', 'z': 'C' } for k, v in d.items(): print(k, '=', v)
所以,列表生成式也可使用两个变量来生成list:
d = {'x': 'A', 'y': 'B', 'z': 'C' } L= [k + '=' + v for k, v in d.items()]
把一个list中全部的字符串变成小写:
L = ['Hello', 'World', 'IBM', 'Apple'] L1= [s.lower() for s in L]
经过列表生成式,咱们能够直接建立一个列表。可是,受到内存限制,列表容量确定是有限的。并且,建立一个包含100万个元素的列表,不只占用很大的存储空间,若是咱们仅仅须要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
因此,若是列表元素能够按照某种算法推算出来,那咱们是否能够在循环的过程当中不断推算出后续的元素呢?这样就没必要建立完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要建立一个generator,有不少种方法。第一种方法很简单,只要把一个列表生成式的[]改为(),就建立了一个generator:
L = [x * x for x in range(10)] print(L) #[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] g = (x * x for x in range(10)) #()是一个generator print(g) #<generator object <genexpr> at 0x1022ef630>
咱们能够直接打印出list的每个元素,但咱们怎么打印出generator的每个元素呢?若是要一个一个打印出来,能够经过next()函数得到generator的下一个返回值。咱们讲过,generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
可是,若是每次输出都调用next(g)实在是太变态了,正确的方法是使用for循环,由于generator也是可迭代对象:
g = (x*x for x in range(10)) for n in g print(n)
因此,咱们建立了一个generator后,基本上永远不会调用next(),而是经过for循环来迭代它,而且不须要关心StopIteration的错误。
generator很是强大。若是推算的算法比较复杂,用相似列表生成式的for循环没法实现的时候,还能够用函数来实现。这篇对应的教程中还讲了更为牛逼的generator的使用方法!
咱们已经知道,能够直接做用于for循环的数据类型有如下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
这些能够直接做用于for循环的对象统称为可迭代对象:Iterable。
可使用isinstance()判断一个对象是不是Iterable对象:
from collections import Iterable isinstance([], Iterable) #True isinstance({}, Iterable) #True isinstance('abc', Iterable) #True isinstance((x for x in range(10)), Iterable) #True isinstance(100, Iterable) #False
而生成器不但能够做用于for循环,还能够被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示没法继续返回下一个值了。
能够被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可使用isinstance()判断一个对象是不是Iterator对象:
from collections import Iterator isinstance((x for x in range(10)), Iterator) #True isinstance([], Iterator) #False isinstance({}, Iterator) #False isinstance('abc', Iterator) #False
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可使用iter()函数:
isinstance(iter([]), Iterator) #True isinstance(iter('abc'), Iterator) #True
你可能会问,为何list、dict、str等数据类型不是Iterator?
这是由于Python的Iterator对象表示的是一个数据流,Iterator对象能够被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。能够把这个数据流看作是一个有序序列,但咱们却不能提早知道序列的长度,只能不断经过next()函数实现按需计算下一个数据,因此Iterator的计算是惰性的,只有在须要返回下一个数据时它才会计算。
Iterator甚至能够表示一个无限大的数据流,例如全体天然数。而使用list是永远不可能存储全体天然数的。
凡是可做用于for循环的对象都是Iterable类型。
凡是可做用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列。
Python的for循环本质上就是经过不断调用next()函数实现的,例如:
for x in [1, 2, 3, 4, 5]: pass
实际上彻底等价于:
# 首先得到Iterator对象: it = iter([1, 2, 3, 4, 5]) # 循环: while True: try: # 得到下一个值: x = next(it) except StopIteration: # 遇到StopIteration就退出循环 break
函数式编程的一个特色就是,容许把函数自己做为参数传入另外一个函数,还容许返回一个函数!
Python对函数式编程提供部分支持。因为Python容许使用变量,所以,Python不是纯函数式编程语言。
那么函数名是什么呢?函数名其实就是指向函数的变量!对于abs()这个函数,彻底能够把函数名abs当作变量,它指向一个能够计算绝对值的函数!
若是把abs指向其余对象,会有什么状况发生?
abs = 10 abs(-10)
报错:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
把abs指向10后,就没法经过abs(-10)调用该函数了!由于abs这个变量已经不指向求绝对值函数而是指向一个整数10!
固然实际代码绝对不能这么写,这里是为了说明函数名也是变量。要恢复abs函数,请重启Python交互环境。
因为abs函数其实是定义在__builtin__模块中的,因此要让修改abs变量的指向在其它模块也生效,要用__builtin__.abs = 10。
既然变量能够指向函数,函数的参数能接收变量,那么一个函数就能够接收另外一个函数做为参数,这种函数就称之为高阶函数。例子
def add(x, y, f): return f(x) + f(y)
能够这样调用:
add(-5, 6, abs)
编写高阶函数,就是让函数的参数可以接收别的函数。函数式编程就是指这种高度抽象的编程范式。
咱们先看map。map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次做用到序列的每一个元素,并把结果做为新的Iterator返回。
def f(x): return x*x r = map(f, [1,2,3,4,5,6,7,8,9]) print(list(r)) #[1,4,16,25,36,49,64,81]
再看reduce的用法。reduce把一个函数做用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素作累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
例子,比方说对一个序列求和,就能够用reduce实现:
from functools import reduce
def add(x, y):
return x + y
print(reduce(add, [1, 3, 5, 7, 9])) #输出25
这个例子自己没多大用处,可是,若是考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),咱们就能够写出把str转换为int的函数:
from functools import reduce def fn(x, y): return x * 10 + y def char2num(s): return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s] print(reduce(fn, map(char2num, '13579'))) #输出13579
整理成一个str2int的函数就是:
from functools import reduce def str2int(s): def fn(x, y): return x * 10 + y def char2num(s): return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s] return reduce(fn, map(char2num, s))
还能够用lambda函数进一步简化成:
from functools import reduce def char2num(s): return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s] def str2int(s): return reduce(lambda x, y: x * 10 + y, map(char2num, s))
也就是说,假设Python没有提供int()函数,你彻底能够本身写一个把字符串转化为整数的函数,并且只须要几行代码!
Python内建的filter()函数用于过滤序列。
和map()相似,filter()也接收一个函数和一个序列。和map()不一样的时,filter()把传入的函数依次做用于每一个元素,而后根据返回值是True仍是False决定保留仍是丢弃该元素。
例如,在一个list中,删掉偶数,只保留奇数,能够这么写:
def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 结果: [1, 5, 9, 15]
可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数。
注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,因此要强迫filter()完成计算结果,须要用list()函数得到全部结果并返回list。
排序也是在程序中常常用到的算法。不管使用冒泡排序仍是快速排序,排序的核心是比较两个元素的大小。若是是数字,咱们能够直接比较,但若是是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,所以,比较的过程必须经过函数抽象出来。一般规定,对于两个元素x和y,若是认为x < y,则返回-1,若是认为x == y,则返回0,若是认为x > y,则返回1,这样,排序算法就不用关心具体的比较过程,而是根据比较结果直接排序。
Python内置的sorted()函数就能够对list进行排序:
sorted([36, 5, -12, 9, -21]) #[-21, -12, 5, 9, 36]
此外,sorted()函数也是一个高阶函数,它还能够接收一个key函数来实现自定义的排序,例如按绝对值大小排序:
sorted([36, 5, -12, 9, -21], key=abs) #[5, 9, -12, -21, 36] #key指定的函数将做用于list的每个元素上,并根据key函数返回的结果进行排序。
咱们再看一个字符串排序的例子:
sorted(['bob', 'about', 'Zoo', 'Credit']) #['Credit', 'Zoo', 'about', 'bob']
默认状况下,对字符串排序,是按照ASCII的大小比较的,因为'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。
如今,咱们提出排序应该忽略大小写,按照字母序排序。要实现这个算法,没必要对现有代码大加改动,只要咱们能用一个key函数把字符串映射为忽略大小写排序便可。忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较。
这样,咱们给sorted传入key函数,便可实现忽略大小写的排序:
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower) #['about', 'bob', 'Credit', 'Zoo']
要进行反向排序,没必要改动key函数,能够传入第三个参数reverse=True:
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True) #['Zoo', 'Credit', 'bob', 'about']
从上述例子能够看出,高阶函数的抽象能力是很是强大的,并且,核心代码能够保持得很是简洁。
高阶函数除了能够接受函数做为参数外,还能够把函数做为结果值返回。
咱们来实现一个可变参数的求和。一般状况下,求和的函数是这样定义的:
def calc_sum(*args): ax = 0 for n in args: ax = ax + n return ax
可是,若是不须要马上求和,而是在后面的代码中,根据须要再计算怎么办?能够不返回求和的结果,而是返回求和的函数:
def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum
当咱们调用lazy_sum()时,返回的并非求和结果,而是求和函数:
f = lazy_sum(1, 3, 5, 7, 9) f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
调用函数f时,才真正计算求和的结果:
f() #25
注意到返回的函数在其定义内部引用了局部变量args,因此,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,因此,闭包用起来简单,实现起来可不容易。
另外一个须要注意的问题是,返回的函数并无马上执行,而是直到调用了f()才执行。咱们来看一个例子:
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count()
在上面的例子中,每次循环,都建立了一个新的函数,而后,把建立的3个函数都返回了。
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:
f1() #9 f2() #9 f3() #9
所有都是9!缘由就在于返回的函数引用了变量i,但它并不是马上执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,所以最终结果为9。
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
若是必定要引用循环变量怎么办?方法是再建立一个函数,用该函数的参数绑定循环变量当前的值,不管该循环变量后续如何更改,已绑定到函数参数的值不变:
def count(): def f(j): def g(): return j*j return g fs = [] for i in range(1, 4): fs.append(f(i)) # f(i)马上被执行,所以i的当前值被传入f() return fs
再看看结果:
f1, f2, f3 = count() f1() #1 f2() #4 f3() #9
缺点是代码较长,可利用lambda函数缩短代码。
当咱们在传入函数时,有些时候,不须要显式地定义函数,直接传入匿名函数更方便。
在Python中,对匿名函数提供了有限支持。仍是以map()函数为例,计算f(x)=x2时,除了定义一个f(x)的函数外,还能够直接传入匿名函数:
list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])) #[1, 4, 9, 16, 25, 36, 49, 64, 81]
经过对比能够看出,匿名函数lambda x: x * x实际上就是:
def f(x): return x * x
关键字lambda表示匿名函数,冒号前面的x表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
用匿名函数有个好处,由于函数没有名字,没必要担忧函数名冲突。此外,匿名函数也是一个函数对象,也能够把匿名函数赋值给一个变量,再利用变量来调用该函数:
f = lambda x: x * x f #<function <lambda> at 0x101c6ef28> f(5) #25
一样,也能够把匿名函数做为返回值返回,好比:
def build(x, y): return lambda: x * x + y * y
def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*argc, **kw) return wrapper
观察上面的log,由于它是一个decorator,因此接受一个函数做为参数,并返回一个函数。咱们要借助Python的@语法,把decorator置于函数的定义处:
@log def now(): print('2015-3-25')
调用now()函数,不只会运行now()函数自己,还会在运行now()函数前打印一行日志:
now() #call now(): #2015-3-25
把@log放到now()函数的定义处,至关于执行了语句:
now = log(now)
因为log()是一个decorator,返回一个函数,因此,原来的now()函数仍然存在,只是如今同名的now变量指向了新的函数,因而调用now()将执行新函数,即在log()函数中返回的wrapper()函数。
wrapper()函数的参数定义是(args, *kw),所以,wrapper()函数能够接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式须要经过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator能够用函数实现,也能够用类实现。
decorator能够加强函数的功能,定义起来虽然有点复杂,但使用起来很是灵活和方便。
functools.partial就是帮助咱们建立一个偏函数的,不须要咱们本身定义int2(),能够直接使用下面的代码建立一个新的函数int2:
import functools int2 = functools.partial(int, base=2) print(int2('1000000')) #64 print(int2('1010101')) #85
Python的functools模块提供了不少有用的功能,其中一个就是偏函数(Partial function)。要注意,这里的偏函数和数学意义上的偏函数不同。
没太看懂,仍是等到具体研究一个项目源码,以及本身作开发的时候再去结合实践深刻理解吧!
2015.09.08 23:59:00 明天继续看 模块 的教程,今天对不少知识点并无真正理解,都是有一些印象,因此必须等到本身研究源码、本身开发的时候,结合运行的效果和理论知识去达到真正的深刻的理解。如今赶忙睡觉!
在计算机程序的开发过程当中,随着程序代码越写越多,在一个文件里代码就会愈来愈长,愈来愈不容易维护。
为了编写可维护的代码,咱们把不少函数分组,分别放到不一样的文件里,这样,每一个文件包含的代码就相对较少,不少编程语言都采用这种组织代码的方式。在Python中,一个.py文件就称之为一个模块(Module)。
引入了包之后,只要顶层的包名不与别人冲突,那全部模块都不会与别人冲突。如今,abc.py模块的名字就变成了mycompany.abc,相似的,xyz.py的模块名变成了mycompany.xyz。
请注意,每个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,不然,Python就把这个目录当成普通目录,而不是一个包。__init__.py能够是空文件,也能够有Python代码,由于__init__.py自己就是一个模块,而它的模块名就是mycompany。
相似的,能够有多级目录,组成多级层次的包结构。
不少强大的第三方库,要可以充分利用好它们为我服务!!!
面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象做为程序的基本单元,一个对象包含了数据和操做数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数经过切割成小块函数来下降系统的复杂度。
而面向对象的程序设计把计算机程序视为一组对象的集合,而每一个对象均可以接收其余对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
2015.09.22,明天继续学习《面向对象编程》
好比一个类的代码以下
class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print('%s: %s' % (self.name, self.score))
能够这样使用这个类建立对象
bart = Student('Bart Simpson', 59) lisa = Student('Lisa Simpson', 87) bart.print_score() lisa.print_score()
面向对象的设计思想是从天然界中来的,由于在天然界中,
类(Class)
和实例(Instance)
的概念是很天然的。Class是一种抽象概念,好比咱们定义的Class——Student,是指学生这个概念,而实例(Instance)则是一个个具体的Student,好比,Bart Simpson和Lisa Simpson是两个具体的Student。
因此,面向对象的设计思想是抽象出Class,根据Class建立Instance。
面向对象的抽象程度又比函数要高,由于一个Class既包含数据,又包含操做数据的方法。
数据封装、继承和多态是面向对象的三大特色,咱们后面会详细讲解。
关于面向对象设计其实和C++、Delphi……都很像,可是具体的语法可能不一样,不过这都是一些表面化的东西。具体去参考Python的编程规范、语法就行了。
这篇教程里面有关于类、实例、实例的内存地址……的讲解,因此要好好看看!
__init__
方法是类的构造方法,self
这个特殊变量的理解。
和普通的函数相比,在类中定义的函数只有一点不一样,就是第一个参数永远是实例变量self,而且,调用时,不用传递该参数。除此以外,类的方法和普通函数没有什么区别,因此,你仍然能够用默认参数、可变参数、关键字参数和命名关键字参数。
要定义一个方法,除了第一个参数是self外,其余和普通函数同样。要调用一个方法,只须要在实例变量上直接调用,除了self不用传递,其余参数正常传入。
由于Python是静态语言,因此语法上还会有其余更多的区别,因此必定要和其余的以前我了解的语言在语法方面区分开
一些关于变量的权限、访问限制、命名规范的说明。总的来讲就是,Python自己没有任何机制阻止你干坏事,一切全靠自觉。
在继承关系中,若是一个实例的数据类型是某个子类,那它的数据类型也能够被看作是父类。可是,反过来就不行。可使用isistance()函数来进行判断。
这篇教程很好的讲解了多态的表现形式!!具体的编程语法、代码实现的细节,认真参考这篇教程!!
type()
isinstance()
dir()
:若是要得到一个对象的全部属性和方法,可使用dir()函数,它返回一个包含字符串的list,好比,得到一个str对象的全部属性和方法。