Python进阶
函数式编程、模块、面向对象编程、定制类
函数式编程
- 把计算视为函数而不是一系列的指令
- 纯函数式编程:不需要变量,没有副作用,测试简单
- 支持高阶函数,代码简洁
python支持的函数式编程
- 不是纯函数式编程:允许有变量
- 支持高阶函数:函数也可以作为变量传入
- 支持闭包:有了闭包就能返回函数
- 有限度的支持匿名函数
高阶函数
- 变量可以指向函数
#求-10的绝对值
abs(-10)
#等价于如下,定义一个变量f
f = abs
f(-10)
- 函数名其实就是指向函数的变量
abs = len
abs([1,2,3])
#输出就是3,abs就指向了len函数对象
高阶函数:能接收函数做参数的函数
- 变量可以指向函数
- 函数的参数可以接收变量
- 一个函数可以接受另外一个函数作为参数
- 能接收函数做参数的函数就是高阶函数
#例子利用add(x,y,f)函数计算绝对值x+绝对值y
def add(x,y,f):
return f(x)+f(y)
print add(-5,9,abs)
#打印出的结果是14
Python中内置的高阶函数:
- map()函数:
接收一个函数 f 和一个 list,把函数 f 依次作用在list的每个元素上,得到一个新的list 并返回。
注:map不改变原有的list,而是返回一个新的list
#例子
def f(x):
return x*x
print map(f,[1,2,3])
#打印结果是:[1,4,9]
- reduce()函数:
接收一个函数 f ,一个list,reduce()传入的函数f必须接收两个参数,reduce()对list的每个元素反复调用 f ,并返回最终的结果值。
#例子
def f(x,y):
return x+y
print reduce(f,[1,2,3])
#结果是6
- filter()函数:
接收一个函数 f 和一个list,这个函数f的作用是对每个元素进行判断,filter()将依据判断结果自动过滤掉不符合条件的元素,返回值由符合条件的元素组成新的list。
#例子,从一个list里面删除偶数,保留奇数
def is_odd(x):
return x % 2 == 1
print filter(is_odd,[1,2,4,5])
#输出结果为:[1,5]
- Python中的返回函数
Python中的函数不仅可以返回int,str,list,dict等数据类型,还可以返回函数
#例子:编写一个函数calc_prod(list),它接收一个list,返回一个函数,返回函数可以计算参数的乘积
def calc_prod(list):
def lazy_calc():
def f(x,y):
return x*y
return reduce(f,list,1)
return lazy_calc
f = calc_prod([1,2,3]) #仅仅是返回了函数
print f() #对返回的函数进行了调用,才返回了结果
- 闭包:
内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(closure)
闭包的特点是返回的函数还引用了外层函数的局部变量,所以要正确的使用闭包,就要保证引用的局部变量在函数返回后不能变。 - 匿名函数:
高阶函数可以接收函数做参数,有时候不需要显式地定义函数,直接传入匿名参数更方便。
关键字 lambda表示匿名函数,冒号前面的x表示函数参数
#举例子
map(lambda x : x*x,[1,2,3])
#得到的结果是[1,4,9]
decorator装饰器
起因:定义了一个函数,想在运行时动态增加功能,又不想改动函数本身的代码
python内置的@语法就是为了简化装饰器调用
#例子
@new_fn
def f1(x):
return x*2
#等价于如下
def f1(x):
return x*2
f1 = new_fn(f1)
装饰器的作用:可以极大地简化代码,避免每个函数编写重复的代码
- 打印日志:@log
- 检测性能:@performance
- 数据库事务:@transaction
- URL路由:@post(’/register’)
python中的偏函数
当一个函数有很多参数的时候,调用者就需要提供多个参数,如果减少参数个数,就可以简化调用者的负担。
#例子
int('12345')
#输出是12345
#int()函数还提供额外的base参数,more职位10
int('12345',16)
#输出是74565
functools.partical就是帮助我们创建一个偏函数的:
#直接使用下面代码创建一个int2()
import functools
int2 = functools.partial(int,base=2)
模块和包
为解决模块名冲突,python将同名的模块放入不同的包中,模块就是一个个.py文件。
在文件系统中,包就是文件夹,模块就是xxx.py文件,包可以有多级
- 如何区分包和普通的目录:
包下面有个__init__.py文件 (注意每层包都有该文件)
python导入模块:使用import语句导入一个模块
python提供的模块管理工具:easy_install、pip(可以使用二者来安装最新版的模块)
python面向对象编程
类和实例:
类用于定义抽象类型(class来定义类);
实例根据类的定义被创建出来。
- 定义类并创建实例:
#定义一个类(括号中表示该类是从哪个类继承下来的)
#定义类并创建实例
class Person(object):
pass
xiaoming = Person()
xiaohong = Person()
print xiaoming
print xiaohong
print xiaoming==xiaohong
- 创建实例属性:
#创建实例属性
class Person(object):
pass
p1 = Person()
p1.name = 'Bart'
p2 = Person()
p2.name = 'Adam'
p3 = Person()
p3.name = 'Lisa'
L1 = [p1, p2, p3]
L2 = sorted(L1,lambda p1,p2:cmp(p1.name,p2.name))
print L2[0].name
print L2[1].name
print L2[2].name
- 初始化实例属性:
#初始化实例属性
class Person(object):
#记住self,个人理解像Java和c++中的this
def __init__(self,name,sex,date,job):
self.name=name
self.sex=sex
self.date=date
self.job=job
xiaoming = Person('Xiao Ming', 'Male', '1990-1-1', job='Student')
print xiaoming.name
print xiaoming.job
- 访问限制:
Python对属性权限的控制是通过属性名来实现的,如果一个属性由双下划线开头(__),该属性就无法被外部访问。
#例子
class Person(object):
def __init__(self, name, score):
self.name=name
self.__score=score
p = Person('Bob', 59)
print p.name
print p.__score
- 创建类属性:
由于Python是动态语言,类属性也是可以动态添加和修改的。
#创建count属性,每创建一个实例count属性就+1
class Person(object):
count=0
def __init__(self,name):
Person.count=Person.count+1
self.name=name
p1 = Person('Bob')
print Person.count
p2 = Person('Alice')
print Person.count
p3 = Person('Tim')
print Person.count
- 定义实例的方法:
#请给 Person 类增加一个私有属性 __score,表示分数,再增加一个实例方法 get_grade(),能根据 __score 的值分别返回 A-优秀, B-及格, C-不及格三档。
class Person(object):
def __init__(self, name, score):
self.__name=name
self.__score=score
def get_grade(self):
if self.__score >=80:
return 'A'
if self.__score >=60:
return 'B'
return 'C'
p1 = Person('Bob', 90)
p2 = Person('Alice', 65)
p3 = Person('Tim', 48)
print p1.get_grade()
print p2.get_grade()
print p3.get_grade()
- 定义类方法:
#如果将类属性 count 改为私有属性__count,则外部无法读取__score,但可以通过一个类方法获取,请编写类方法获得__count值。
#注意类方法需要添加 @classmethod
class Person(object):
__count = 0
@classmethod
def how_many(cls):
return cls.__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
class Teacher(Person):
def __init__(self, name, gender, course): super(Teacher,self).__init__(name,gender)
self.course=course
t = Teacher('Alice', 'Female', 'English')
print t.name
print t.course
- python中多重继承
除了从一个父类继承外,python允许从多个父类继承,称为多重继承。
python允许多重继承
定制类
- 特殊方法:
特殊方法定义在class中,不需要直接去调用,python的某些函数或操作符会调用对应的特殊方法。 - 特殊方法__str__()和__repr__()
#请给Student 类定义__str__和__repr__方法,使得能打印出<Student: name, gender, score>
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
class Student(Person):
def __init__(self, name, gender, score):
super(Student, self).__init__(name, gender)
self.score = score
def __str__(self):
return '(Student:%s,%s,%s)' % (self.name,self.gender,self.score)
__repr__=__str__
s = Student('Bob', 'male', 88)
print s
- 特殊方法__cmp__
#请修改 Student 的 __cmp__ 方法,让它按照分数从高到底排序,分数相同的按名字排序
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return '(%s: %s)' % (self.name, self.score)
__repr__ = __str__
def __cmp__(self, s):
if self.score==s.score:
return cmp(self.name,s.name)
return -cmp(self.score,s.score)
L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 99)]
print sorted(L)
- 特殊方法__len__
#斐波那契数列是由 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):
self.lst=[0,1]
for i in range(2,num):
self.lst.append(self.lst[i-2]+self.lst[i-1])
def __str__(self):
return str(self.lst)
__repr__=__str__
def __len__(self):
return len(self.lst)
f = Fib(10)
print f
print len(f)
- 特殊方法__slots__
如果要限制添加的属性,例如,Student类只允许添加 name、gender和score 这3个属性,就可以利用Python的一个特殊的__slots__来实现。
#假设Person类通过__slots__定义了name和gender,请在派生类Student中通过__slots__继续添加score的定义,使Student类可以实现name、gender和score 3个属性
class Person(object):
__slots__ = ('name', 'gender')
def __init__(self, name, gender):
self.name = name
self.gender = gender
class Student(Person):
__slots__ = ('score',)
def __init__(self,name,gender,score):
super(Student,self).__init__(name,gender)
self.score=score
s = Student('Bob', 'male', 59)
s.name = 'Tim'
s.score = 99
print s.score
- 特殊方法__call__
你无法确定 p 是一个函数还是一个类实例,所以,在Python中,函数也是对象,对象和函数的区别并不显著。
#改进一下前面定义的斐波那契数列:
#class Fib(object):
# ???
#请加一个__call__方法,让调用更简单:
#>>> f = Fib()
#>>> print f(10)
#[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
class Fib(object):
def __call__(self,num):
self.lst=[0,1]
for i in range(2,num):
self.lst.append(self.lst[i-1]+self.lst[i-2])
return self.lst
f = Fib()
print f(10)