【廖雪峰】Python


Python是一种至关高级的语言。
TIOBE排行榜
高级编程语言一般都会提供一个比较完善的基础代码库,让你能直接调用,
许多大型网站就是用Python开发的,例如YouTube、Instagram,还有国内的豆瓣。
Python的哲学就是简单优雅

那Python适合开发哪些类型的应用呢?  
  • 网络应用,包括网站、后台服务等等; 
  • 脚本任务等等; 
  • 把其余语言开发的程序再包装起来,方便使用。
缺点:
  • 运行速度慢:解释型语言,执行的时候翻译称为机器代码。
  • 代码不能加密:靠网站和移动应用卖服务的模式愈来愈多了

安装

Python3.5
在Windows上运行Python时,请先启动命令行,而后运行python。  
在Mac和Linux上运行Python时,请打开终端,而后运行python3。

Python解释器

Cpython:官方

第一个Python程序

退出:exit()

命令行模式和Python交互模式

python calc.py

使用文本编辑器

    
    
    
    
#!/usr/bin/env python3print('hello, world')
$ chmod a+x hello.py
直接输入python进入交互模式,至关于启动了Python解释器,可是等待你一行一行地输入源代码,每输入一行就执行一行。  
直接运行.py文件至关于启动了Python解释器,而后一次性把.py文件的源代码给执行了,你是没有机会以交互的方式输入源代码的。

Python代码运行助手

网页上输入代码,经过本机的Python库进行解释。

输入和输出

print()会依次打印每一个字符串,遇到逗号“,”会输出一个空格,所以,输出的字符串是这样拼起来的:
print()也能够打印整数,或者计算结果

输入

name = input()
提示字符串:
name = input('please enter your name: ')

python基础

其余每一行都是一个语句,当语句以冒号:结尾时,缩进的语句视为代码块。
缩进的坏处就是“复制-粘贴”功能失效了,
最后,请务必注意,Python程序是大小写敏感的,若是写错了大小写,程序会报错。
在文本编辑器中,须要设置把Tab自动转换为4个空格,确保不混用Tab和空格。

变量和类型

字符串
 
字符串是以单引号'或双引号"括起来的任意文本
>>> print(r'\\\t\\')
用r''表示''内部的字符串默认不转义

字符串有不少行:
用\n写在一行里很差阅读,为了简化,Python容许用'''...'''的格式表示多行内容
>>> print('''line1 ... line2 ... line3''')
line1
line2
line3

布尔值

True、False
if age >= 18:
    print('adult')
else:
    print('teenager')

空值

空值是Python里一个特殊的值,用None表示。None不能理解为0,由于0是有意义的,而None是一个特殊的空值。

变量

理解变量在计算机内存中的表示也很是重要。当咱们写:
 
     
     
     
     
a = 'ABC'
时,Python解释器干了两件事情:
 
  • 在内存中建立了一个'ABC'的字符串; 
  • 在内存中建立了一个名为a的变量,并把它指向'ABC'。

a = 'ABC'
b = a
a = 'XYZ'print(b)

执行a = 'ABC',解释器建立了字符串'ABC'和变量a,并把a指向'ABC'javascript

py-var-code-1

执行b = a,解释器建立了变量b,并把b指向a指向的字符串'ABC'php

py-var-code-2

执行a = 'XYZ',解释器建立了字符串'XYZ',并把a的指向改成'XYZ',但b并无更改:css

py-var-code-3

因此,最后打印变量b的结果天然是'ABC'了。java

一种除法是//,称为地板除,两个整数的除法仍然是整数:
>>> 10 // 33
Python还提供一个余数运算,能够获得两个整数相除的余数:
>>> 10 % 31
Python的整数没有大小限制,而某些语言的整数根据其存储长度是有大小限制的,例如Java对32位整数的范围限制在-2147483648~-2147483647。
 
Python的浮点数也没有大小限制,可是超出必定范围就直接表示为inf(无限大)

字符串和编码

最先只有127个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码
可是要处理中文显然一个字节是不够的,至少须要两个字节,并且还不能和ASCII编码冲突,因此,中国制定了GB2312编码,用来把中文编进去。
Unicode应运而生。Unicode把全部语言都统一到一套编码里,这样就不会再有乱码问题了。

若是你写的文本基本上所有是英文的话,用Unicode编码比ASCII编码须要多一倍的存储空间,在存储和传输上就十分不划算。
把Unicode编码转化为“可变长编码”的UTF-8编码
经常使用的英文字母被编码成1个字节,汉字一般是3个字节,只有很生僻的字符才会被编码成4-6个字节
在计算机内存中,统一使用Unicode编码,当须要保存到硬盘或者须要传输的时候,就转换为UTF-8编码。

浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器:
web-utf-8
因此你看到不少网页的源码上会有相似<meta charset="UTF-8" />的信息,表示该网页正是用的UTF-8编码。
print('包含中文的str')
>>> ord('A')
65
>>> chr(66)
'B'
,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符:

若是要在网络上传输,或者保存到磁盘上,就须要把str变为以字节为单位的bytes。而在内存中用Unicode进行表示,一个字符对应多个字节
x = b'ABC'
注意区分'ABC'和b'ABC',前者是str,后者虽然内容显示得和前者同样,但bytes的每一个字符都只占用一个字节。

以Unicode表示的str经过encode()方法能够编码为指定的bytes
>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
在bytes中,没法显示为ASCII字符的字节,用\x##显示。
'中文'.encode('ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
由于中文编码的范围超过了ASCII编码的范围,Python会报错。
若是咱们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就须要用decode()方法:

 b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'
计算字符数: len( '中文' )
计算字节数:
>>> len(b'ABC')
3
>>> len('中文'.encode('utf-8'))
6
为了不乱码问题,应当始终坚持使用UTF-8编码对str和bytes进行转换。
按照UTF-8进行读取:
#!/usr/bin/env python3# -*- coding: utf-8 -*-

格式化

'Hello, %s' % 'world'
 'Hi, %s, you have $%d.' % ('Michael', 1000000)
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数
>>> '%2d-%02d' % (3, 1)
' 3-01'>>> '%.2f' % 3.1415926'3.14'
补零

'Age: %s. Gender: %s' % (25, True)
会把全部类型转换为字符串。
用%%来表示一个%:

list和tuple

list


。list是一种有序的集合,能够随时添加和删除其中的元素。

classmates = ['Michael', 'Bob', 'Tracy']
 len(classmates)
classmates[0]
记得最后一个元素的索引是len(classmates) - 1。
倒数第二个: classmates[-2]
list是一个可变的有序表,因此,能够往list中追加元素到末尾:
classmates.append('Adam')
要删除list末尾的元素,用pop()方法:
classmates.pop()
要删除指定位置的元素,用pop(i)方法,其中i是索引位置:
要把某个元素替换成别的元素,能够直接赋值给对应的索引位置: classmates[ 1 ] = 'Sarah'
list里面的元素的数据类型也能够不一样,好比: >>> L = [ 'Apple' , 123 , True ]
也能够是另外的list: s = [ 'python' , 'java' , [ 'asp' , 'php' ], 'scheme' ]
要拿到'php'能够s[2][1],

tuple


元组
一旦初始化之后就不能修改。

    
    
    
    
classmates = ('Michael', 'Bob', 'Tracy')
因此也没有append() , insert()这样的方法。
代码能够更安全,尽量使用。

t = (1)既多是表示元组,也多是表示括号。
 t = (1,)
消除歧义。
>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'>>> t[2][1] = 'Y'>>> t
('a', 'b', ['X', 'Y'])
tuple包含3个元素。


变的实际上是list的元素。
所谓的不变指的是指向永远不变。
小结
 
list和tuple是Python内置的有序集合,一个可变,一个不可变。


条件判断

age = 3if age >= 18:
    print('adult')
elif age >= 6:
    print('teenager')
else:
    print('kid')
注意else后面的冒号。
elif是else if 的缩写。
若是在某个判断上是True,把该判断对应的语句执行后,就忽略掉剩下的elif和else
birth = input('birth: ')
if birth < 2000:
    print('00前')
else:
    print('00后')
输入的是字符串。可是2000是数字,不能比较。

s = input('birth: ')
birth = int(s)
if birth < 2000:
    print('00前')
else:
    print('00后')

经过int(s)把字符串转换为数字。

循环

names = ['Michael', 'Bob', 'Tracy']
for name in names:
    print(name)
提供一个range()函数,能够生成一个整数序列,再经过list()函数能够转换为list。好比range(5)生成的序列是从0开始小于5的整数:
list(range(5))
range(101)就能够生成0-100的整数序列
sum = 0
n = 99while n > 0:
    sum = sum + n
    n = n - 2print(sum)
在循环内部变量n不断自减,直到变为-1时,再也不知足while条件,循环退出。

使用dict和set

dict

其余语言map,键值对。
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95
在字典的索引表里(好比部首表)查这个字对应的页码,而后直接翻到该页,找到这个字。
要避免key不存在的错误,有两种办法,一是经过in判断key是否存在:

>>> 'Thomas' in d
False
二是经过dict提供的get方法,若是key不存在,能够返回None,或者本身指定的value:
>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1
要删除一个key,用pop(key)方法,对应的value也会从dict中删除:
dict是用空间来换取时间的一种方法。
dict的key必须是不可变对象
经过key计算位置的算法称为哈希算法(Hash)。
而list是可变的,就不能做为key:

set


set和dict相似,也是一组key的集合,但不存储value。因为key不能重复,因此,在set中,没有重复的key。

s = set([1, 2, 3])
重复元素自动过滤
 s = set([1, 1, 2, 2, 3, 3])
经过add(key)方法能够添加元素到set中,能够重复添加,但不会有效果:
经过remove(key)方法能够删除元素:
set能够当作数学意义上的无序和无重复元素的集合

set和dict的惟一区别仅在于没有存储对应的value,可是,set的原理和dict同样,因此,一样不能够放入可变对象,由于没法判断两个可变对象是否相等,也就没法保证set内部“不会有重复元素”。

再议不可变对象

>>> a = ['c', 'b', 'a']
>>> a.sort()
>>> a
['a', 'b', 'c']
内部对象是会变化的。
>>> a = 'abc'>>> a.replace('a', 'A')
'Abc'>>> a
'abc'
虽然replace能够变出Abc,可是实际上a仍然是abc
a是变量,而'abc'才是字符串对象!有些时候,咱们常常说,对象a的内容是'abc',但实际上是指,a自己是一个变量,它指向的对象的内容才是'abc':

replace方法建立了一个新字符串'Abc'并返回,若是咱们用变量b指向该新字符串,就容易理解了,变量a仍指向原有的字符串'abc',但变量b却指向新字符串'Abc'了:

对于不变对象来讲,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会建立新的对象并返回,这样,就保证了不可变对象自己永远是不可变的。
最经常使用的key是字符串。

函数

定义函数

def my_abs(x):if x >= 0:
        return x
    else:
        return -x
from abstest import my_abs来导入my_abs()函数,注意abstest是文件名(不含.py扩展名):

空函数


使用pass语句。
def nop():pass
做为占位符。
不会对参数类型作检查。
用内置函数isinstance()实现:
def my_abs(x):if not isinstance(x, (int, float)):
        raise TypeError('bad operand type')
    if x >= 0:
        return x
    else:
        return -x

返回多个值

import math语句表示导入math包,并容许后续代码引用math包里的sin、cos等函数。
import math

def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny
返回仍然是单一值,是一个tuple。

函数的参数


位置参数

修改后的power(x, n)函数有两个参数:x和n,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数x和n。

默认参数

def power(x, n=2):
    s = 1while n > 0:
        n = n - 1
        s = s * x
    return s
一是必选参数在前,默认参数在后,不然Python的解释器会报错
二是如何设置默认参数。
当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就能够做为默认参数。

def add_end(L=[]):
    L.append('END')
    return L
此时默认参数是可变的list
第一次调用
>>> add_end()
['END']
后面调用就不对了。
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']

Python函数在定义的时候,默认参数L的值就被计算出来了,即[],由于默认参数L也是一个变量,它指向对象[],每次调用该函数,若是改变了L的内容,则下次调用时,默认参数的内容就变了,再也不是函数定义时的[]了。
 
因此,定义默认参数要牢记一点:默认参数必须指向不变对象!
能够经过不变对象None来实现。
def add_end(L=None):if L is None:
        L = []
    L.append('END')
    return L
>>> add_end()
['END']
>>> add_end()
['END']
因为对象不变,多任务环境下同时读取对象不须要加锁,同时读一点问题都没有。

可变参数

def calc(*numbers):
    sum = 0for n in numbers:
        sum = sum + n * n
    return sum
Python容许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去:
>>> nums = [1, 2, 3]
>>> calc(*nums)
14

关键字参数

可变参数容许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
关键字参数容许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)
能够传入任意个数的关键字参数
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
能够扩展函数的功能。
能够先组装为一个dict,而后传进去。
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
**extra表示把extra这个dict的全部key-value用关键字参数传入到函数的**kw参数,kw将得到一个dict,注意kw得到的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。

命名关键字

在内部对传入的关键字进行检测。
def person(name, age, **kw):if 'city' in kw:
        # 有city参数passif 'job' in kw:
        # 有job参数pass
    print('name:', name, 'age:', age, 'other:', kw)
限制关键字参数的名字,能够用命名关键字。
def person(name, age, *, city, job):
    print(name, age, city, job)
若是函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就再也不须要一个特殊分隔符*了:
def person(name, age, *args, city, job):
    print(name, age, args, city, job)

参数组合


参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
def f1(a, b, c=0, *args, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

def f2(a, b, c=0, *, d, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
>>> f1(1, 2)
a = 1 b = 2 c = 0 args = () kw = {}
>>> f1(1, 2, c=3)
a = 1 b = 2 c = 3 args = () kw = {}
>>> f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>> f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
>>> f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}


经过一个tuple和dict,你也能够调用上述函数:
>>> args = (1, 2, 3, 4)
>>> kw = {'d': 99, 'x': '#'}
>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
>>> args = (1, 2, 3)
>>> kw = {'d': 88, 'x': '#'}
>>> f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
对于任意函数,均可以经过相似func(*args, **kw)的形式调用它,不管它的参数是如何定义的。

小结


默认参数必定要用不可变对象,若是是可变对象,程序运行时会有逻辑错误!
 
要注意定义可变参数和关键字参数的语法:
 
*args是可变参数,args接收的是一个tuple;  
**kw是关键字参数,kw接收的是一个dict。
以及调用函数时如何传入可变参数和关键字参数的语法:
 
可变参数既能够直接传入:func(1, 2, 3),又能够先组装list或tuple,再经过*args传入:func(*(1, 2, 3));  
关键字参数既能够直接传入:func(a=1, b=2),又能够先组装dict,再经过**kw传入:func(**{'a': 1, 'b': 2})。

命名的关键字参数是为了限制调用者能够传入的参数名,同时能够提供默认值。  
定义命名的关键字参数在没有可变参数的状况下不要忘了写分隔符*,不然定义的将是位置参数。

递归函数

def fact(n):if n==1:
        return 1return n * fact(n - 1)

防止栈溢出。
经过尾递归优化。和循环的效果同样。在函数返回的时候,调用自身。return不能包含表达式。
这样,编译器或者解释器就能够把尾递归作优化,使递归自己不管调用多少次,都只占用一个栈帧,不会出现栈溢出的状况。
要把每一步的乘积传入到递归函数中:

def fact(n):return fact_iter(n, 1)

def fact_iter(num, product):if num == 1:
        return product
    return fact_iter(num - 1, num * product)
能够看到,return fact_iter(num - 1, num * product)仅返回递归函数自己,num - 1和num * product在函数调用前就会被计算,不影响函数调用。

高级特性


构造列表:
    
    
    
    
L = []n = 1while n <= 99: L.append(n) n = n + 2

切片

取前N个元素。
>>> r = []
>>> n = 3>>> for i in range(n):
...     r.append(L[i])
... 

>>> L[0:3]
>>> L[-2:]
记住倒数第一个元素的索引是 -1
前11-20个数:
>>> L[10:20]
前10个数,每两个取一个:
>>> L[:10:2]
全部数每5个取一个
>>> L[::5]
复制应该 list
>>> L[:]
tuple也是一种list,惟一区别是tuple不可变。所以,tuple也能够用切片操做,只是操做的结果还是tuple:
>>> (0, 1, 2, 3, 4, 5)[:3]
字符串'xxx'也能够当作是一种list,每一个元素就是一个字符。所以,字符串也能够用切片操做,只是操做结果还是字符串
>>> 'ABCDEFG'[:3]
'ABC'>>> 'ABCDEFG'[::2]
'ACEG'
针对字符串提供了不少各类截取函数(例如,substring),其实目的就是对字符串切片。Python没有针对字符串的截取函数,只须要切片一个操做就能够完成,很是简单。

迭代


迭代:遍历list or tuple
Python中迭代是经过for in 实现的,java是经过下标实现的。
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
...     print(key)
...
默认dict迭代的是key
若是要迭代value,能够用for value in d.values(),若是要同时迭代key和value,能够用for k, v in d.items()。
因为字符串也是可迭代对象,所以,也能够做用于for循环:
如何判断一个对象是可迭代对象呢?方法是经过collections模块的Iterable类型判断:

>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代True>>> isinstance([1,2,3], Iterable) # list是否可迭代True>>> isinstance(123, Iterable) # 整数是否可迭代False
若是要实现相似Java那样的下标循环。
Python内置的enumerate函数能够把一个list变成索引-元素对,这样就能够在for循环中同时迭代索引和元素自己:
>>> for i, value in enumerate(['A', 'B', 'C']):
...     print(i, value)
...
0 A
1 B
2 C
上面的for循环里,同时引用了两个变量,在Python里是很常见的
   
   
   
   
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:... print(x, y)...1 12 43 9

列表生成式

List Comprehensions :内置的,用来建立list的生成式。
>>> list(range(1, 11))
若是要生成[1x1, 2x2, 3x3, ..., 10x10]怎么作?
>>> [x * x for x in range(1, 11)]
for循环后面还能够加上if判断,这样咱们就能够筛选出仅偶数的平方:
   
   
   
   
[x * x for x in range (1 , 11) if x %2 = 0]
还可使用两层循环,能够生成全排列:
   
   
   
   
[m + n for m in 'ABC' for n in 'abc']
列出当前目录下的文件和目录
   
   
   
   
import os [d for d in os.listdir ('.')]
for循环其实能够同时使用两个甚至多个变量,好比dict的items()能够同时迭代key和value:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> for k, v in d.items():
...     print(k, '=', v)
...
y = B
x = A
z = C
   
   
   
   
d = {'x' : 'A' , 'y' : 'B' , 'z' : 'C'} [k + '=' + v for k,v in d.items()]
最后把list的字符串变为小写。
L = ['Hello', 'World', 'IBM', 'Apple']
   
   
   
   
[s.lower() for s in L]

   
   
   
   
>>> L = ['Hello', 'World', 18, 'Apple', None]>>> [s.lower() for s in L]Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <listcomp>AttributeError: 'int' object has no attribute 'lower'
若是list中既包含字符串,又包含整数,因为非字符串类型没有lower()方法,因此列表生成式会报错:
使用内建的isinstance函数能够判断一个变量是否是字符串:

isinstance(x, str)

   
   
   
   
L2 = [s.lower() for s in L1 if isinstance (s , str)]

生成器


列表容量是有限的。
列表的元素按照算法推算出
generator:一边循环一边计算
把列表表达式中的[]变为()
打印:next ()
也可使用循环。
   
   
   
   
g = (x * x for x in range (10))for i in g print (i)
若是推算的算法比较复杂,用相似列表生成式的for循环没法实现的时候,还能够用函数来实现。
   
   
   
   
def fib (max) n , a , b = 0 , 0 , 1 while n < max : print(b) a , b = b , a + b n = n + 1 return 'done'
a , b = b  , a + b 
至关于
   
   
   
   
t = (b , a + b)a = t[0]b = t[1]
要把fib函数变成generator,只须要把print(b)改成yield b就能够了:
fib函数其实是定义了斐波拉契数列的推算规则,能够从第一个元素开始,推算出后续任意的元素,这种逻辑其实很是相似generator。
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
最难理解的就是generator和函数的执行流程不同。函数是顺序执行,遇到return语句或者最后一行函数语句就返回

而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
可是用for循环调用generator时,发现拿不到generator的return语句的返回值,由于每次在yield处就中断了。

若是想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

   
   
   
   
>>> g = fib(6)>>> while True:... try:... x = next(g)... print('g:', x)... except StopIteration as e:... print('Generator return value:', e.value)... break...
普通函数调用直接返回结果:
generator函数的“调用”实际返回一个generator对象:

迭代器

能够做用于for循环的主要有以下两种:
  •  集合数据类型:list,tuple,dict , set , str 
  • generator : 生成器和带yield的generate function

能够直接做用于for循环的对象,统称为可迭代对象:Iterable
可使用isinstance()判断一个对象是不是Iterable对象:

>>> from collections import Iterable
>>> isinstance([], Iterable)
生成器不但能够做用于for循环,还能够被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示没法继续返回下一个值了。

能够被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可使用iter()函数:
>>> isinstance(iter([]), Iterator)
True
为何list、dict、str等数据类型不是Iterator?

由于Python的Iterator对象表示的是一个数据流,Iterator对象能够被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。能够把这个数据流看作是一个有序序列,但咱们却不能提早知道序列的长度,只能不断经过next()函数实现按需计算下一个数据,因此Iterator的计算是惰性的,只有在须要返回下一个数据时它才会计算。
 
Iterator甚至能够表示一个无限大的数据流,例如全体天然数。而使用list是永远不可能存储全体天然数的。

小结

凡是可做用于for循环的对象都是Iterable类型;  
凡是可做用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;  
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过能够经过iter()函数得到一个Iterator对象。
for循环本质上就是经过不断调用next()函数实现的
   
   
   
   
# 首先得到Iterator对象:it = iter([1, 2, 3, 4, 5])# 循环:while True: try: # 得到下一个值: x = next(it) except StopIteration: # 遇到StopIteration就退出循环 break

函数式编程

而函数式编程(请注意多了一个“式”字)——Functional Programming,

在计算机的层次上,CPU执行的是加减乘除的指令代码,以及各类条件判断和跳转指令,因此,汇编语言是最贴近计算机的语言。
而计算则指数学意义上的计算,越是抽象的计算,离计算机硬件越远。
对应到编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,好比C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,好比Lisp语言。

高阶函数

Higher-order function

变量能够指向函数

abs(-10)是函数调用,而abs是函数自己。
f = abs
结论:函数自己也能够赋值给变量,即:变量能够指向函数。
>>> f = abs
>>> f(-10)
10
直接调用abs()函数和调用变量f()彻底相同。

函数名也是变量

对于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函数,请重启Python交互环境。
 
注:因为abs函数其实是定义在import builtins模块中的,因此要让修改abs变量的指向在其它模块也生效,要用import builtins; builtins.abs = 10。

传入函数


那么一个函数就能够接收另外一个函数做为参数,这种函数就称之为高阶函数。
def add(x, y, f):return f(x) + f(y)
add(-5, 6, abs)

x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11return 11

map/reduce


map()函数接收两个参数,一个是函数,一个是Iterable,
map将传入的函数依次做用到序列的每一个元素,并把结果做为新的Iterator返回。
咱们有一个函数f(x)=x2,要把这个函数做用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就能够用map()实现以下:


>>> def f(x):...     return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
因为结果r是一个Iterator,Iterator是惰性序列,所以经过list()函数让它把整个序列都计算出来并返回一个list。
map()做为高阶函数,事实上它把运算规则抽象了,所以,咱们不但能够计算简单的f(x)=x2,还能够计算任意复杂的函数
>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
reduce把一个函数做用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素作累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
序列求和:
>>> from functools import reduce
>>> def add(x, y):...     return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
若是要把序列[1, 3, 5, 7, 9]变换成整数13579,reduce就能够派上用场:
>>> from functools import reduce
>>> def fn(x, y):
...     return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579
果考虑到字符串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]...>>> reduce(fn, map(char2num, '13579'))13579
整理成一个str2int的函数就是:
   
   
   
   
from functools import reducedef 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 reducedef 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))
    
    
    
    
from functools import reducedef str2float(L): dotIndex=L.find('.') times=len(L)-dotIndex-1 s=L.replace('.','') 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 fn(x, y): return x * 10 + y L=reduce(fn, map(char2num, s)) print ('times = ', pow(10,times)) return L/pow(10 , times)print(str2float('123.456'))

filter

过滤序列。
接收一个序列。
把传入的函数做用于每一个元素,根据返回值是ture和False决定保留仍是丢弃。
删除偶数,保留奇数。
def is_odd(n):return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
若是为false,则删除。
s.strip(rm)        删除s字符串中开头、结尾处,位于 rm删除序列的字符
当rm为空时,默认删除空白符(包括'\n', '\r',  '\t',  ' ')
把空字符串删掉。
def not_empty(s):return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
# 结果: ['A', 'B', 'C']
注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,因此要强迫filter()完成计算结果,须要用list()函数得到全部结果并返回list。

用filter求素数

埃氏筛法
首先,列出从2开始的全部天然数,构造一个序列:
取序列的第一个数2,它必定是素数,而后用2把序列的2的倍数筛掉:
取新序列的第一个数3,它必定是素数,而后用3把序列的3的倍数筛掉:
取新序列的第一个数5,而后用5把序列的5的倍数筛掉:
构造从3开始的奇数序列
def _odd_iter():
    n = 1while True:
        n = n + 2yield n
注意这是一个生成器,而且是一个无限序列。
定义一个筛选函数:
   
   
   
   
def _not_divisible(n): return lambda x: x % n > 0
最后,定义一个生成器,不断返回下一个素数:
   
   
   
   
def primes(): yield 2 it = _odd_iter() # 初始序列 while True: n = next(it) # 返回序列的第一个数 yield n it = filter(_not_divisible(n), it) # 构造新序列
这个生成器先返回第一个素数2,而后,利用filter()不断产生筛选后的新的序列。  
因为primes()也是一个无限序列,因此调用时须要设置一个退出循环的条件:
   
   
   
   
for n in primes(): if n < 1000: print(n) else: break
注意到Iterator是惰性计算的序列,因此咱们能够用Python表示“全体天然数”,“全体素数”这样的序列,而代码很是简洁。
filter()的做用是从一个序列中筛出符合条件的元素。因为filter()使用了惰性计算,因此只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。

sorted


高阶函数: sorted([ 36 , 5 , - 12 , 9 , - 21 ], key=abs)
默认状况下,对字符串排序,是按照ASCII的大小比较的,因为'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。

忽略大小写:只要咱们能用一个key函数把字符串映射为忽略大小写排序便可。忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写)
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
反向排序:
 sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。

返回函数

函数做为返回值。

   
   
   
   
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时,才真正计算求和的结果:

在这个例子中,咱们在函数lazy_sum中又定义了函数sum,而且,内部函数sum能够引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

当咱们调用lazy_sum()时,每次调用都会返回一个新的函数,即便传入相同的参数:
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False

闭包

注意到返回的函数在其定义内部引用了局部变量args,因此,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,因此,闭包用起来简单,实现起来可不容易
返回的函数并无马上执行,而是直到调用了f()才执行。

另外一个须要注意的问题是,返回的函数并无马上执行,而是直到调用了f()才执行
   
   
   
   
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fsf1, f2, f3 = count()
每次循环,都建立了一个新的函数,而后,把建立的3个函数都返回了。  
你可能认为调用f1(),f2()和f3()结果应该是1,4,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

返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

匿名函数

>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
匿名函数lambda x: x * x实际上就是:
   
   
   
   
def f(x): return x * x
关键字lambda表示匿名函数,冒号前面的x表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
此外,匿名函数也是一个函数对象,也能够把匿名函数赋值给一个变量,再利用变量来调用该函数

装饰器

函数对象有一个__name__属性,能够拿到函数的名字:
假设咱们要加强now()函数的功能,好比,在函数调用先后自动打印日志,但又不但愿修改now()函数的定义,这种在代码运行期间动态增长功能的方式,称之为“装饰器”(Decorator)。

decorator就是一个返回函数的高阶函数。因此,咱们要定义一个能打印日志的decorator,能够定义以下:
   
   
   
   
def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
观察上面的log,由于它是一个decorator,因此接受一个函数做为参数,并返回一个函数。咱们要借助Python的@语法,把decorator置于函数的定义处:

   
   
   
   
@logdef now(): print('2015-3-25')
把@log放到now()函数的定义处,至关于执行了语句:  
now = log(now)
因为log()是一个decorator,返回一个函数,因此,原来的now()函数仍然存在,只是如今同名的now变量指向了新的函数,因而调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

wrapper()函数的参数定义是(*args, **kw),所以,wrapper()函数能够接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

若是decorator自己须要传入参数,那就须要编写一个返回decorator的高阶函数,写出来会更复杂。好比,要自定义log的文本:

   
   
   
   
def log(text): def decorator(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator
    
    
    
    
@log('execute')def now(): print('2015-3-25')

偏函数


Partial function
int('12345', base=8)
能够定义一个int2()的函数,默认把base=2传进去:
functools.partial就是帮助咱们建立一个偏函数的,不须要咱们本身定义int2(),能够直接使用下面的代码建立一个新的函数int2:
   
   
   
   
>>> import functools>>> int2 = functools.partial(int, base=2)>>> int2('1000000')64>>> int2('1010101')85
至关于
kw = { 'base': 2 }
int('10010', **kw)
max2 = functools.partial(max, 10)
实际上会把10做为*args的一部分自动加到左边,也就是
max2(5, 6, 7)
至关于
   
   
   
   
args = (10, 5, 6, 7)max(*args)

模块


咱们把不少函数分组,分别放到不一样的文件里面。
本身在编写模块时,没必要考虑名字会与其余模块冲突
若是不一样的人编写的模块名相同怎么办?为了不模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。
方法是选择一个顶层包名,好比mycompany,
每个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,不然,Python就把这个目录当成普通目录,而不是一个包。__init__.py能够是空文件,也能够有Python代码,由于__init__.py自己就是一个模块,而它的模块名就是mycompany。

使用模块

Mac或Linux上有可能并存Python 3.x和Python 2.x,所以对应的pip命令是pip3。
确保安装时勾选了pip和Add python.exe to Path。
pip install Pillow
生成缩略图
   
   
   
   
>>> from PIL import Image>>> im = Image.open('test.png')>>> print(im.format, im.size, im.mode)PNG (400, 300) RGB>>> im.thumbnail((200, 100))>>> im.save('thumb.jpg', 'JPEG')
其余经常使用的第三方库还有MySQL的驱动: mysql-connector-python,用于科学计算的NumPy库:numpy,用于生成文本的模板工具Jinja2,等等。

当咱们试图加载一个模块时,Python会在指定的路径下搜索对应的.py文件
Python解释器会搜索当前目录、全部已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:
>>> import sys
>>> sys.path
添加本身的目录:
  • 修改sys.path
  • 第二种方法是设置环境变量PYTHONPATH

面向对象编程

class Student(object):pass
class后面紧接着是类名,即Student,类名一般是大写开头的单词,紧接着是(object),表示该类是从哪一个类继承下来的,继承的概念咱们后面再讲,一般,若是没有合适的继承类,就使用object类,这是全部类最终都会继承的类。
   
   
   
   
bart = Student()
 注意:特殊方法“init”先后有两个下划线!!!
   
   
   
   
class Student(object): def __init__(self, name, score): self.name = name self.score = score
注意到__init__方法的第一个参数永远是self,表示建立的实例自己,所以,在__init__方法内部,就能够把各类属性绑定到self,由于self就指向建立的实例自己。
 
有了__init__方法,在建立实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不须要传,Python解释器本身会把实例变量传进去:
和普通的函数相比,在类中定义的函数只有一点不一样,就是第一个参数永远是实例变量self,而且,调用时,不用传递该参数

访问限制

内部属性不被外部访问。把属性的名称前加上两个下划线__,
原先那种直接经过bart.score = 59也能够修改啊,为何要定义一个方法大费周折?由于在方法中,能够对参数作检查,避免传入无效的参数:

继承和多态

新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。
   
   
   
   
class Cat(Animal):

继承原来的代码,能够作必定改进。
子类的run()覆盖了父类的run()
多态:
由于Dog是从Animal继承下来的,当咱们建立了一个Dog的实例c时,咱们认为c的数据类型是Dog没错,但c同时也是Animal也没错,Dog原本就是Animal的一种!

当咱们定义一个class的时候,咱们实际上就定义了一种数据类型
在继承关系中,若是一个实例的数据类型是某个子类,那它的数据类型也能够被看作是父类。可是,反过来就不行:
多态的好处就是,当咱们须要传入Dog、Cat、Tortoise……时,咱们只须要接收Animal类型就能够了,由于Dog、Cat、Tortoise……都是Animal类型,而后,按照Animal类型进行操做便可。因为Animal类型有run()方法,所以,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:

对于一个变量,咱们只须要知道它是Animal类型,无需确切地知道它的子类型,就能够放心地调用run()方法,而具体调用的run()方法是做用在Animal、Dog、Cat仍是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力: 调用方只管调用,无论细节,而当咱们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

对扩展开放:容许新增Animal子类;  
对修改封闭:不须要修改依赖Animal类型的run_twice()等函数。

静态语言VS动态语言

对于静态语言(例如Java)来讲,若是须要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,不然,将没法调用run()方法。
 
对于Python这样的动态语言来讲,则不必定须要传入Animal类型。咱们只须要保证传入的对象有一个run()方法就能够了:

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就能够被看作是鸭子

获取对象信息


使用type()

果咱们要在if语句中判断,就须要比较两个变量的type类型是否相同:

   
   
   
   
>>> type(fn)==types.FunctionTypeTrue>>> type(abs)==types.BuiltinFunctionTypeTrue>>> type(lambda x: x)==types.LambdaTypeTrue>>> type((x for x in range(10)))==types.GeneratorTypeTrue

isinstance()

object -> Animal -> Dog -> Husky
>>> a = Animal()
>>> d = Dog()
>>> h = Husky()
isinstance(h, Husky)
>>> isinstance(h, Animal)
True

dir()

若是要得到一个对象的全部属性和方法,可使用dir()函数,它返回一个包含字符串的list,

好比__len__方法返回长度。在Python中,若是你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,因此,下面的代码是等价的:
   
   
   
   
>>> len('ABC')3>>> 'ABC'.__len__()3
仅仅把属性和方法列出来是不够的,配合getattr()、setattr()以及hasattr(),咱们能够直接操做一个对象的状态:

咱们能够对任意一个Python对象进行剖析,拿到其内部的数据。要注意的是,只有在不知道对象信息的时候,咱们才会去获取对象信息
能够用
sum = obj.x + obj.y

就不要写:python

sum = getattr(obj, 'x') + getattr(obj, 'y')
假设咱们但愿从文件流fp中读取图像,咱们首先要判断该fp对象是否存在read方法,若是存在,则该对象是一个流,若是不存在,则没法读取。hasattr()就派上了用场。

实例属性和类属性

   
   
   
   
>>> class Student(object):... name = 'Student'...>>> s = Student() # 建立实例s>>> print(s.name) # 打印name属性,由于实例并无name属性,因此会继续查找class的name属性Student>>> print(Student.name) # 打印类的name属性Student>>> s.name = 'Michael' # 给实例绑定name属性>>> print(s.name) # 因为实例属性优先级比类属性高,所以,它会屏蔽掉类的name属性Michael>>> print(Student.name) # 可是类属性并未消失,用Student.name仍然能够访问Student>>> del s.name # 若是删除实例的name属性>>> print(s.name) # 再次调用s.name,因为实例的name属性没有找到,类的name属性就显示出来了Student

从上面的例子能够看出,在编写程序的时候,千万不要把实例属性和类属性使用相同的名字,由于相同名称的实例属性将屏蔽掉类属性,可是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。


使用__slots__

   
   
   
   
class Student(object): pass
    
    
    
    
>>> s = Student()>>> s.name = 'Michael' # 动态给实例绑定一个属性>>> print(s.name)Michael
可是,给一个实例绑定的方法,对另外一个实例是不起做用的:
为了给全部实例都绑定方法,能够给class绑定方法:
一般状况下,上面的set_score方法能够直接定义在class中,但动态绑定容许咱们在程序运行的过程当中动态给class加上功能,这在静态语言中很难实现。
可是若是想限制实例的属性
定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

   
   
   
   
class Student(object): __slots__ = ('name', 'age') # 用tuple定义容许绑定的属性名称
使用__slots__要注意,__slots__定义的属性仅对当前类实例起做用,对继承的子类是不起做用的:

除非在子类中也定义__slots__,这样,子类实例容许定义的属性就是自身的__slots__加上父类的__slots__。

访问数据库


在Mac或Linux上,须要编辑MySQL的配置文件,把数据库默认的编码所有改成UTF-8。MySQL的配置文件默认存放在/etc/my.cnf或者/etc/mysql/my.cnf:
   
   
   
   
[client]default-character-set = utf8[mysqld]default-storage-engine = INNODBcharacter-set-server = utf8collation-server = utf8_general_ci
mysql> show variables like '%char%';
安装驱动:
 pip install mysql-connector
   
   
   
   
# 导入MySQL驱动:>>> import mysql.connector# 注意把password设为你的root口令:>>> conn = mysql.connector.connect(user='root', password='password', database='test')>>> cursor = conn.cursor()# 建立user表:>>> cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')# 插入一行记录,注意MySQL的占位符是%s:>>> cursor.execute('insert into user (id, name) values (%s, %s)', ['1', 'Michael'])>>> cursor.rowcount1# 提交事务:>>> conn.commit()>>> cursor.close()# 运行查询:>>> cursor = conn.cursor()>>> cursor.execute('select * from user where id = %s', ('1',))>>> values = cursor.fetchall()>>> values[('1', 'Michael')]# 关闭Cursor和Connection:>>> cursor.close()True>>> conn.close()






















附件列表

相关文章
相关标签/搜索