基本特性
Slice 切片
取list或tuple的部分元素
L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
print(L[0:3])
# 第一个索引为0 则可以省略
print(L[:3])
# 获取倒数第一个元素
print(L[-1:])
slice 简单操作
l1 = list(range(100))
# 获取前10个数
print(l1[:10])
# 获取后10个数
print(l1[-10:])
# 获取10到19之间的数
print(l1[10:20])
# 前10个数,每两个取一个
print(l1[:10:2])
# 所有的数,每5个获取一个
print(l1[::5])
# 原样复制一个list
print(l1[:])
tuple 也是一种list,也可以使用slice结果依然是tuple
t1 = tuple(range(100))
print(t1[::5])
字符串也是一种list, 也可以用slice结果仍是字符串
s1 = 'hello world'
print(s1[:3])
print(s1[::2])
list Comprehensions 列表生成式
列表生成式List Comprehensions,是Python内置的非常简单却强大的可以用来创建 list 的生成式
使用range生成list
print(list(range(1, 11)))
使用for循环生成列表
L = []
for x in range(1, 11):
L.append(x * x)
print(L)
使用list comprehensions 生成列表
L = [x * x for x in range(1, 11)]
print(L)
for 后可跟条件来过滤元素(if是一个筛选条件,不能带else)
L = [x * x for x in range(1, 11) if x % 2 == 0]
print(L)
for 前面是一个表达式可使用if-else
L = [x if x % 2 == 0 else -x for x in range(1, 11)]
print(L)
可以使用两层循环,可以生成全排列
L = [x + y for x in 'ABC' for y in 'abc']
print(L)
列出当前目录下的所有文件和目录名
d = [d for d in os.listdir('.')]
print(d)
列表生成式可以指定多个生成参数
d = {'x': 'A', 'y': 'B', 'z': 'C'}
L = [k + '=' + v for k, v in d.items()]
print(L)
把所有元素都变成小写
L = ['Hello', 'World', 'IBM', 'Apple']
print([item.lower() for item in L])
generator 列表生成器
创建 generator:[] 改为 ()
创建的g是一个对象
g = (x * x for x in range(10))
print(g)
print(next(g))
print(next(g))
# 运行结果
<generator object <genexpr> at 0x10f13fc60>
0
1
可以循环打印
for x in g:
print(x)
创建 generator:yield
定义一个 function
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
fib(5)
# 注意
a, b = b, a + b 的含义解释:
t = (b, a + b) # t是一个tuple
a = t[0]
b = t[1]
将Function改为generator,只需要在获取元素的地方改为yield,如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator:
def fib_g(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
f = fib_g(5)
print(f)
# 执行结果:
<generator object fib_g at 0x102d4c580>
generator函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
g = odd()
next(g)
next(g)
next(g)
执行结果:
step 1
step 2
step 3
迭代 generator,基本上不会用 next() 来获取下一个返回值,而是直接使用 for 循环来迭代
g = odd()
for n in g:
pass
执行结果:
step 1
step 2
step 3
获取 generator return 返回值
for 循环调用 generator 时,是拿不到 return 的返回值的。如果想要拿到,必须捕获 StopIteration 错误,返回值包含在StopIteration的 value 中:
g = odd()
while True:
try:
next(g)
except StopIteration as e:
print(e.value)
break
执行结果:
step 1
step 2
step 3
over
iterable 和 iterator
iterable
直接作用于for循环的数据类型有以下几种:
- 集合数据类型,如
list、tuple、dict、set、str等; generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
isinstance()判断一个对象是否是Iterable对象
print(isinstance('abc', Iterable)) # str是否可迭代
print(isinstance([1,2,3], Iterable)) # list是否可迭代
print(isinstance((x for x in range(10)), Iterable)) # list comprehensions 是否可迭代
print(isinstance(123, Iterable)) # 整数是否可迭代
迭代dict
from typing import Iterable
d = {'a': 1, 'b': 2, 'c': 3}
# 同时迭代key与value
for k, v in d.items():
print(k, v)
# 默认迭代的是key
for k in d:
print(k)
# 迭代value
for v in d.values():
print(v)
for循环中同时迭代索引和元素本身
# Python内置的enumerate函数可以把一个list变成索引-元素对
for i,v in enumerate([1,2,3]):
print(i, v)
iterator
iterator 不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
isinstance()判断一个对象是否是Iterator对象:
print(isinstance((x for x in range(10)), Iterator)) # True
print(isinstance([], Iterator)) # False
print(isinstance({}, Iterator)) # False
print(isinstance('', Iterator)) # False
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可以使用iter()函数:
print(isinstance(iter([]), Iterator)) # True
print(isinstance(iter({}), Iterator)) # True
为何list、dict、str等数据类型不是Iterator?
Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流。而使用list是永远不可能存储全体自然数的。
函数式编程
Higher-order function 高阶函数
函数名实际上就是指向函数的变量
f = abs
print(f(-10))
print(type(f))
执行结果:
10
<class 'builtin_function_or_method'>
abs指向10后,就无法通过abs(-10)调用该函数,因为abs不指向求绝对值函数而是指向一个整数10!
abs = 10
print(abs(-10))
注:由于abs函数是定义在import builtins模块中的,所以要让修改abs变量的指向在其它模块也生效,要用import builtins; builtins.abs = 10。
传入函数
一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
def operate(x, y, f):
return f([x,y])
print(operate(1, 2, sum))
print(operate(1, 2, min))
map/reduce
《MapReduce: Simplified Data Processing on Large Clusters》是由Jeffrey Dean和Sanjay Ghemawat于2004年发表的经典论文,介绍了Google提出的MapReduce编程模型和它在大规模数据处理中的应用。这篇论文的主要内容是如何通过MapReduce简化分布式计算任务,使其能够在大型计算集群上高效运行。
map
将m_func应用到每一个list元素中
# m 是一个`Iterator`,`Iterator`是惰性序列,
# 因此通过`list()`函数让它把整个序列都计算出来并返回一个list。
m = map(m_func, [1, 2, 3, 4, 5])
list(m)
reduce
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
print(reduce(add, [1,2,3]))
print(input("reduce function=>###########"))
将str转换为int
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return DIGITS[s]
return reduce(fn, map(char2num, s))
print(str2int('67656'))
ambda函数进一步简化
def lambdaStr2int(s):
def char2num(s):
return DIGITS[s]
return reduce(lambda x, y: x * 10 + y, map(char2num, s))
print(str2int('1234'))
filter
和map()类似,filter()也接收一个函数和一个序列,将函数作用于序列中的每个元素,来过滤元素。
过滤序列中的基数
def isOdd(n):
return n % 2 == 1
# filter()函数返回的是一个Iterator,是一个惰性序列,所以需要用list()函数获取结果并返回list。
l1 = list(filter(isOdd, [1, 2, 4, 5, 6, 9, 10, 15]))
print(l1)
过滤字符串
def notEmpty(s):
# s:"" 或 None 为False
# s.strip():返回一个去除字符串两端空白字符的新字符串,包括空格、制表符(tab)、换行符等
return s and s.strip()
l2 = list(filter(notEmpty, ['A', '', 'B', None, 'C', ' ']))
print(l2)
sorted
排序的核心是比较两个元素的大小。如果是数字,可直接比较,如果是字符串或者两个dict直接比较就是没有意义的,因此,比较的过程需要通过函数抽象出来。
python 内置排序
l1 = sorted([36, 5, -12, 9, -21])
print(l1)
# 可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序
l1 = sorted([36, 5, -12, 9, -21], key=abs)
print(l1)
# 默认字符串排序是按照ASCII的大小比较
l1 = sorted(['bob', 'about', 'Zoo', 'Credit'])
print(l1)
忽略大小写,倒序排序
# sorted传入key函数,即可实现忽略大小写的排序
# reverse=True 倒序
# l1 = sorted(['bob', 'about', 'Zoo', 'Credit'],key=lambda x:x.lower, reverse=True)
l1 = sorted(['bob', 'about', 'Zoo', 'Credit'],key=str.lower, reverse=True)
print(l1)
元组序列排序
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
# 按照名称排序
def by_name(t):
return t[0]
L1 = sorted(L, key=by_name)
print(L1)
# 按照成绩排序
def by_score(t):
return t[1]
L1 = sorted(L, key=by_score)
print(L1)
return function
直接计算返回结果
def calc_sum(*args):
ax = 0
for n in args:
ax = ax + n
return ax
s = calc_sum(1, 3, 5, 7, 9)
print(s)
懒加载,返回的是一个函数,触发调用
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
s = lazy_sum(1, 3, 5, 7, 9)()
print(s)
closure
返回的函数持有创建函数的变量
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
# 执行结果为 9 9 9 原因是返回函数内部的变量 i 已经变成了 3
f1, f2, f3 = count()
print(f1(), f2(), f3())
解决方案:创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变
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()
print(f1(), f2(), f3())
nonlocal
nonlocal 关键字用于声明一个变量来自外层函数作用域(但不是全局作用域)。
def inc():
x = 0
def fn():
# 仅读取x的值:
return x + 1
return fn
f = inc()
print(f()) # 1
print(f()) # 1
使用 nonlocal 关键字声明
def inc():
x = 0
def fn():
# 声明 x 来自外层函数作用域,而非全局作用域
nonlocal x
x = x + 1
return x
return fn
f = inc()
print(f()) # 1
print(f()) # 2
anonymous function
不需要显式地定义函数,直接传入匿名函数。lambda x: x * x就是一个匿名函数,lambda表示匿名函数,冒号前面的x表示函数参数。匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
l = list(map(lambda x: x * x, [1, 2, 3, 4, 5]))
print(l)
匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数
f = lambda x: x * x
print(f(3))
decorator 装饰器
def now():
print(datetime.now())
f = now
# __name__属性,可以拿到函数名称
print(now.__name__) # now
print(f.__name__) # now
在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数
def log(func):
# 接收任意数量的位置参数和关键字参数
def wrapper(*args, **kw):
# % 运算符用于字符串格式化,它允许我们将变量或表达式的值插入到字符串的特定位置
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
# @log相当于执行了如下语句
log(now)()
@log
def now():
print(datetime.now())
now()
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数
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('invoke method')
def now():
print(datetime.now())
now()
# @log('invoke method') 相当于执行了如下语句
log('invoke method')(now())
functools.wraps 是 Python 标准库 functools 模块中的一个装饰器工具,它的主要作用是保留被装饰函数的元信息(如函数名、文档字符串、注解等)。这就可以避免因装饰器覆盖原函数而丢失重要的元信息。
@log('invoke method')
def now():
print(datetime.now())
# now 被 decorator __name__的值变成了 wrapper
print(now.__name__) # wrapper
一个完整的decorator的写法
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
带有参数的decorator写法
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
Partial function 偏函数
当函数的参数个数太多,需要简化时functools.partial可把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数
调用有默认参数的函数
print(int('101010', base=2))
print(int('12345', base=8))
print(int('12345', base=16))
自定义函数调用
def int2(x, base=2):
return int(x, base)
print(int2("101010"))
使用偏函数调用
import functools
int2 = functools.partial(int, base=2)
print(int2("101010"))
实际上可以接收函数对象、*args和**kw这3个参数
int2 = functools.partial(int, base=2)
相当于
kw = { 'base': 2 }
int('10010', **kw)
# 会把`10`作为`*args`的一部分自动加到左边
max2 = functools.partial(max, 10)
当调用max2函数
max2(5, 6, 7)
相当于
args = (10, 5, 6, 7)
max(*args)
Module 模块
Module 介绍
使用Module可以提高代码的可维护性,当一个模块编写完毕,就可以被其他地方引用。 一个.py文件就称之为一个模块(Module)。使用模块可以避免函数名和变量名冲突。但尽量不要与内置函数名字冲突。
Module包括Python内置的模块和来自第三方的模块。
mycompany
├─ web
│ ├─ __init__.py
│ ├─ utils.py
│ └─ www.py
├─ __init__.py
├─ abc.py
└─ utils.py
__init__.py文件是必须的,否则Python就将这个目录当成普通目录,而不是一个包目录。__init__.py内容可以为空,也可以有 python 代码,因为__init__.py本身也是一个模块,而它的名字就是mycompanywww.py的模块名就是mycompany.web.www,两个文件utils.py的模块名分别是mycompany.utils和mycompany.web.utils。- 创建模块时不能和Python自带的模块名称冲突。先查看系统是否已存在该模块,检查方法是在Python交互环境执行
import abc。如果系统自带了sys模块,自己的模块也为sys.py,则将无法导入系统的sys模块。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 导入 sys 模块
# sys模块有一个argv变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称
import sys
# 写入作者
__author__ = 'dawn'
' 模块文档注释 '
def test():
args = sys.argv
print(args)
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
# 运行文件时,解释器将Python解释器把一个特殊变量
#__name__置为__main__,
# 而如果在其他地方导入该hello模块时,if判断将失败。
if __name__=='__main__':
test()
正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,x123,PI等;
而__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author__,__name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问。我们自己的变量一般不要用这种变量名;
_xxx和__xxx这样的函数或变量是private,不该被直接引用(可以引用,因为Python并没有一种方法可以完全限制访问private函数或变量,而从编程习惯上不应该引用private函数或变量);
def _private_1(name):
return 'Hello, %s' % name
def _private_2(name):
return 'Hi, %s' % name
def greeting(name):
if len(name) > 3:
return _private_1(name)
else:
return _private_2(name)
第三方模块
在Python中,安装第三方模块,是通过包管理工具pip完成的,安装一个第三方库——Python Imaging Library,这是Python下非常强大的处理图像的工具库。不过,PIL目前只支持到Python 2.7,并且有年头没有更新了,而基于PIL的Pillow项目开发非常活跃,并且支持最新的Python 3。
一般,第三方库都会在Python官方的pypi.python.org网站注册,可以在官网或者pypi上搜索,比如Pillow
安装模块:pip install Pillow