本笔记记录了python的函数式编程、模块的使用、面向对象编程以及利用特殊方法定制类
目录
函数式编程
支持高阶函数:函数可以作为变量传入
支持闭包:用于返回函数
有限度地支持匿名函数
高阶函数
高阶函数:能够接收函数作参数的函数
- 变量可以指向函数
- 函数名其实是指向函数的变量
ex(例):
def add(x, y, f):
return f(x) + f(y)
map()函数
map() 参数: 一个函数 f 和一个 list
通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回
ex:
def f(x):
return x*x
print map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
⇒ [1, 4, 9, 10, 25, 36, 49, 64, 81]
reduce()函数
reduce() 参数: 一个函数 f 、一个 list、[一个初始值(可选)]
对list的每个元素反复调用函数f,并返回最终结果值
ex:
def f(x, y):
return x + y
reduce(f, [1, 3, 5, 7, 9], 100)
⇒125
filter()函数
filter() 参数: 一个函数 f 和一个 list
[这个函数 f 的作用是对每个元素进行判断,返回 True或 False]
filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新的list。
ex:
def is_odd(x):
return x % 2 == 1
filter(is_odd, [1, 4, 6, 7, 9, 12, 17])
⇒[1, 7, 9, 17]
sorted()函数
sorted() 参数: list、[f() 比较函数]
比较函数的定义:传入两个待比较的元素 x, y,如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0
#正序比较函数
ex:
def cmp(x, y):
if x < y:
return -1
if x > y:
return 1
return 0
ex:
>>> sorted([36, 5, 12, 9, 21], reversed_cmp) #倒序比较函数
[36, 21, 12, 9, 5]
返回函数
变量可以指向函数,函数名本身就是指向函数的变量,所以可以返回该变量即返回函数(达到延迟计算的作用——可以在后续代码中决定是否调用该函数)
x = f() # 调用f()、 x只是f返回的函数
return abs #返回函数
return abs(x) #返回函数值
ex:
def f():
print 'call f()...'
# 定义函数g:
def g():
print 'call g()...'
# 返回函数g:
return g
python闭包
在函数内部定义的函数和外部定义的函数是一样的,只是他们无法被外部访问
闭包(Closure):像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况
注:确保引用的局部变量在函数返回后不能变,所以返回函数不能引用循环变量
ex:
def calc_sum(lst):
def lazy_sum():
return sum(lst)
return lazy_sum
#若lst是循环变量,那么此式子不能达到预期效果
匿名函数
匿名函数 lambda x: x * x
关键字lambda 表示匿名函数,冒号前面的 x 表示函数参数
匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果
ex:
def f(x):
return x * x
<==> lambda x: x * x
>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]
#极大地简化了式子
装饰器
运行时动态添加功能
极大地简化代码,避免每个函数编写重复性代码
ex:
def f1(x):
return x*2
def new_fn(f): #装饰器函数
def fn(x):
print 'call %s()' % f.__name__
return fn
>>> g1 = new_fn(f1)
>>> g1(5)
⇒ 10
>>> f1 = new_fn(f1) #彻底隐藏f1原始定义函数
>>> f1(5)
等价于
@new_fn
def f1(x):
return x*2
>>> f1(5) #直接调用已经修饰过的f1
@装饰器的几个内置用法
- @log:打印日志
- @performance:检测性能
- @transaction:数据库事务
- @post(‘/register’):URL路由
无参decorator
@log() 搞定
考察一个@log的定义:
def log(f):
def fn(x):
print 'call ' + f.__name__ + '()...'
return f(x)
return fn
@log 写死了只含一个参数的返回函数, 对于有多个参数的函数会报错,需要将fn(x)改为fn(*args,**kw)
有参decorator
@log(参数) 搞定
需要多一个参数来区分不同的log,’Debug’还是’Info’
ex:
def log(prefix):
# 标准装饰器
def log_decorator(f):
def wrapper(*args, **kw):
print '[%s] %s()...' % (prefix, f.__name__)
return f(*args, **kw)
return wrapper
#返回装饰器
return log_decorator
@log('DEBUG')
def test():
pass
print test()
完善decorator
当装饰了f1()之后,调用f1.__name__*会返回装饰器中新函数的函数名,而非本函数!
修改:在标准装饰器代码后加上@functools.wraps(f)
ex:
import functools
def log(f):
@functools.wraps(f)
def wrapper(*args, **kw):
print 'call...'
return f(*args, **kw)
return wrapper
python中偏函数
当一个函数有很多参数时,调用者就需要提供多个参数。如果减少参数个数,就可以简化调用者的负担。
如:int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做 N 进制的转换:
ex:
>>> int('12345')
12345
#加入base参数
>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565
#定义int2使得调用它就能直接将二进制数转换成十进制数
def int2(x, base=2):
return int(x, base)
>>> int2('1000000')
64
# functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2
>>> import functools
>>> int2 = functools.partial(int, base=2) 【函数名,可选参数】
>>> int2('1000000')
64
#结论:functools.partial可以把一个参数多的函数变成一个参数少的新函数,少的参数需要在创建时指定默认值,这样,新函数调用的难度就降低了。
ex:(另外一个栗子)
import functools
sorted_ignore_case = functools.partial(sorted,cmp = lambda x,y: cmp(x.upper(), y.upper()))
print sorted_ignore_case(['bob', 'about', 'Zoo', 'Credit'])
模块
test.py <—— 自身模块名test
import math <——- 引用math模块
math.pow(2,5) <——– 调用math模块中的函数
注意:需要引入完整模块(ex: p1.math.pow)以防止函数名或变量名命名重复
类比于文件系统
包 <——-> 文件夹
模块 <——–> .py文件
包可以有多层
区别包和目录:包每层必须要有一个__init__*的.py文件
模块的导入及其动态导入
导入模块方法:
- import math.pow
- from math import pow,sin,cos
动态导入模块方法:
#若cStringIO模块存在,优先导入;否则导入StringIO
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
__future__*的使用
要“试用”某一新的特性,就可以通过导入_future_模块的某些功能来实现。
ex:
#在python2.7中引入python3.*的除法规则
>>> from __future__ import division
>>> print 10 / 3
3.3333333333333335
安装第三方模块
通过pip来完成第三方模块的安装
例: pip install web.py
通过访问https://pypi.python.org来查询第三方库
面向对象编程
python之定义类并创建实例
class Person(object):
def __init__(self, name):
self.name = name
p1 = Person('xiaoming')
python中创建实例属性
# 由于Python是动态语言,对每一个实例,都可以直接给他们的属性赋值
class Person(object):
pass
xiaoming = Person()
xiaoming.name = 'Xiao Ming'
xiaoming.gender = 'Male'
xiaoming.birth = '1990-1-1'
python中初始化实例属性
__init__*方法的第一个参数必须为self, python的实例方法的第一个参数也必须为self
class Person(object):
def __init__(self, name, gender, birth):
self.name = name
self.gender = gender
self.birth = birth
# 请定义Person类的__init__方法,除了接受 name、gender 和 birth 外,还可接受任意关键字参数,并把他们都作为属性赋值给实例?
# 要定义关键字参数,使用 **kw;
class Person(object):
def __init__(self, name, gender, birth, **kw):
self.name = name
self.gender = gender
self.birth = birth
for k, v in kw.iteritems():
setattr(self, k, v) #设置属性
xiaoming = Person('Xiao Ming', 'Male', '1990-1-1', job='Student')
print xiaoming.name
print xiaoming.job
python中访问限制
Python对属性权限的控制是通过属性名来实现的,如果一个属性由双下划线开头(__),该属性就无法被外部访问。
例如: __score(相当于私有成员)
python中创建类属性
实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。
实例无法修改类属性
class Person(object):
address = 'Earth' # 类属性
def __init__(self, name):
self.name = name
# 每创建一个实例类属性count就加一
class Person(object):
count = 0
def __init__(self, name):
self.name = name
Person.count = Person.count + 1
python中定义实例方法
类中的定义方法都要比调用时多一个self参数
类中方法和函数的区别在于是否需要传入self
python中定义类方法
class Person(object):
__count = 0
@classmethod # 定义类方法
def how_many(cls):
return cls.__count # 相当于 Person.__count
def __init__(self,name):
self.name = name
Person.__count = Person.__count + 1
print Person.how_many()
p1 = Person('Bob')
print Person.how_many()
继承
继承现有类的所有功能,只要编写现有类所缺少的功能
python继承一个类
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
# Student继承Person
class Student(Person):
def __init__(self, name, gender, score):
#初始化父类
super(Student, self).__init__(name, gender)
#将额外的score属性添上
self.score = score
python中判断类型
函数isinstance()可以判断一个变量的类型,既可以用在Python内置的数据类型如str、list、dict,也可以用在我们自定义的类
p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
>>> isinstance(p, Person) --> True
>>> isinstance(s, Person) --> True(子类是父类类型)
>>> isinstance(p, Student) --> False
>>> isinstance(s, Student) --> True
多态:方法的覆盖
python中多重继承
Python允许从多个父类继承,称为多重继承。
class D(B, C): #继承自两个父类
def __init__(self, a):
super(D, self).__init__(a)
print 'init D...'
python中获取对象信息
用 type() 函数获取变量的类型
用 dir() 函数获取变量的所有属性
用 getattr() 和 setattr( )函数获取和设置属性
>>> s = Student('Bob', 'Male', 88)
>>> type(s)
<class '__main__.Student'>
>>> dir(123) # 整数也有很多属性...
['__abs__', '__add__', '__and__', '__class__', '__cmp__', ...]
>>> getattr(s, 'name') # 获取name属性
'Bob'
>>> setattr(s, 'name', 'Adam') # 设置新的name属性
定制类
python的特殊方法
特殊方法定义在class中,不需要我们直接调用,而python的某些函数或者操作符会调用这些特殊方法
正确实现特殊方法:1、编写用到的特殊方法。2、有关联性的方法都得实现,例如:__setattr__*,__getattr__*,__delattr__*
常见的特殊方法:1、用于print的__str__*; 2、用于len的__len__*;3、用于cmp的__cmp__*。
__str__*、 __repr__*、__cmp__*和__len__*
如果要把一个类的实例变成str,就得实现__str__*
因为 Python 定义了 _str_*()和_repr_*()两种方法,_str_*()用于显示给用户,而_repr_*()用于显示给开发人员。
偷懒地定义__repr__*的方法:__repr__ = __str__
def __cmp__(self, s):
if self.score == s.score:
return cmp(self.name, s.name)
return -cmp(self.score, s.score)
def __len__(self):
return len(self.names)
斐波那契数列是由 0, 1, 1, 2, 3, 5, 8...构成。
请编写一个Fib类,Fib(10)表示数列的前10个元素,print Fib(10) 可以打印出数列的前 10 个元素,len(Fib(10))可以正确返回数列的个数10?
class Fib(object):
def __init__(self, num):
a,b,L = 0,1,[]
for n in range(num):
L.append(a)
a,b = b,a+b
self.numbers = L
def __str__(self):
return str(self.numbers)
__repr__ = __str__
def __len__(self):
return len(self.numbers)
f = Fib(10)
print f
print len(f)
python中数学运算(类)
- 加法运算: __add__*
- 减法运算: __sub__*
- 乘法运算: __mul__*
- 除法运算: __div__*
外部调用时直接用操作符+、-、*和/调用
python中类型转换
- (有理数实例)转换为整型: __int__*
- 转换为浮点数: __float__*
python中 @property
考虑到直接给属性赋值无法检查(属性)分数的有效性
# 用装饰器将getter和setter函数装饰成属性调用
class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self.__score = score
# 结果
>>> s = Student('Bob', 59)
>>> s.score = 60
>>> print s.score
60
>>> s.score = 1000
Traceback (most recent call last):
...
ValueError: invalid score
python中 __slots__*
slots是指一个类允许的属性列表:用于限制实例自行添加属性
class Student(object):
#类的实例只能操作这三个属性
__slots__ = ('name', 'gender', 'score')
def __init__(self, name, gender, score):
self.name = name
self.gender = gender
self.score = score
python中 __call__*
一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法call()。
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
# Person实例可以接受一个参数(字符串)进行调用
def __call__(self, friend):
print 'My name is %s...' % self.name
print 'My friend is %s...' % friend
>>> p = Person('Bob', 'male')
>>> p('Tim') # 调用p
My name is Bob...
My friend is Tim...
单看 p(‘Tim’) 你无法确定 p 是一个函数还是一个类实例,所以,在Python中,函数也是对象,对象和函数的区别并不显著
注
注:s.strip() #删去字符串s中首部和尾部的空字符(\n, \r等)
__str__*等这些特殊方法的*号没有任何意义,只是去掉后在markdown中直接显示为粗体的str,万分抱歉
while len(L) < num :
⇒ 等价于
for n in range(num) :