1、 函数式编程:变量可以指向函数,即f=abs,f(-10)=10; 高阶函数 指能接收函数作为参数的函数;
map(function,list):将函数function依次作用在list的每个元素上,得到一个新的list并返回;
reduce(function(x,y),list):函数中function必须接收两个参数,对list的每个元素反复调用function,并返回最终结果值;
filter(function,list):函数function对每个list元素进行判断,返回TRUE或FALSE,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list;
sorted(list,key(function),reverse):传入两个待比较的元素 x, y,如果x应该排在y的前面,返回-1,如果x应该排在y的后面,返回1。如果x和y相等,返回 0。key用列表元素的某个属性或函数(function)进行作为关键字;reverse= True 降序 或者reverse = False 升序
Python中调用函数返回函数:
闭包:内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,如上图:没法把lazy_sum移到 calc_sum 的外部,因为它引用了calc_sum的参数 lst。
匿名函数(lambda x: function):x为函数参数,function为函数表达式,只能写一个表达式。
2、 装饰器:打印日志(@log),检测性能(@performance),数据库事务(@transaction),URL路由(@post(‘/register’)
示例程序:
def new_f1(f)
x=f(5)
return x
@f1
def f(x)
return x*2
print(f) #输出10
偏函数functools.partial(function,默认值):通过偏函数来对function的默认值进行指定。
模块与包:就是py文件;包即是文件夹,包下面必须要有_init_.py文件;from _future_ import xxx指在Python2.7中引入3.x的规则。
3、 面向对象编程:指一种程序设计范式,把程序看作是不同对象的相互调用,对现实世界建立对象模型。其基本思想为类和实例,类用于定义抽象类型(如人),而实例是根据类的定义被创建出来(如小明,小军…)。在Python中类的定义用class Person(object): pass,类名以大写字母开头,object表示该类由哪个类继承下来;实例创建用类名+(),如xiaoming=Person() xiaojun=Person()…。其次是数据封装,在类中把每一个实例的属性封装起来,不同的实例拥有相同的数据类型但是他们拥有不同的属性。
定义类并创建实例:两种方法如下图
输出:<__main__.Person object at0x7f55178184d0>
<__main__.Person object at 0x7f551782d850>
其中pass是一条空语句,保证程序的完整性;__main__意思是调用模块本身;第二种初始化实例属性。
创建实例属性:从上例看出通过Person创建出的xiaoming等实例仅地址不同,在现实世界中,区分xiaoming、xiaohong要依靠他们各自的名字、性别、生日等属性。故创建实例属性如下xiaoming.name=’Xiao Ming’…,实例的属性可像普通变量一样操作
初始化实例属性:一种类型的实例应拥有相同类型的属性,如Person类应在创建时就拥有name,gender,birth…属性,则需要在Person类中添加_init_()来为每个实例统一添加属性,如下:
classPerson(object):
def __init__(self,name,gender): #self最好不要修改
self.name=name
self.gender=gender
xiaoming=Person(‘XiaoMing’,’Male’)
若要init任意关键字参数使用**kw,属性设置如下:
classPerson(object):
def __init__(self,**kw):
for k,v in kw.iteritems():
setattr(self , k, v)
xiaoming=Person(job=’student’)
解释器会将**kw拆分成对应的dict,其中setattr(对象,属性,属性的值)。另一种方式:self.__dict__.update(kw)(待验证)
实例属性权限:一个实例可以绑定很多属性,当某些属性不希望被外部访问时,可通过属性名来对属性权限控制,即当一个属性由双下划线开头(__),该属性就无法被外部访问,如self.__job= 'Student'(注意是双下划线,单下划线是可以访问的),但是从类的内部是可以访问的。如果一个属性以"__xxx__"定义,那它右可以被外部访问,以"__xxx__"定义的属性在Python的类中被称为特殊属性,通常在很多预定义的特殊属性中使用。
创建类属性:类是模板,而实例是根据类创建的对象。绑定在一个实例上的属性不会影响其他实例,但类本身也是一个对象,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个!也就是说:实例属性每个实例各自拥有,互相独立,而类属性有且只有一份且每个实例都可访问。类的定义如下:
classPerson(object):
address=’Earth’
def __init__(self,name):
self.name=name
对于p1=Person(‘Bob’)和p2=Person(‘Alice’)其p1.address和p2.adress都是一样的,都等于Person.adress为Earth。同时类的属性也可以动态添加和修改,如Person.address=’China’(注意定义时前面没有Person)。
类与实例属性名冲突:修改类属性会导致所有实例访问到的类属性全部都受影响,但如果在实例变量上修改类属性只是给当前实例绑定了个实例属性,当实例属性和类属性重名时,实例属性优先级高,如下例:
输出:
当删除(del p1.adress)当前实例的实例属性后,再访问其实例属性就会输出类属性。当把类属性改为私有时(__xxx),实例变量无法再外部修改其属性,即只能在class内进行。
定义实例方法:指在类中定义函数,它的第一个参数永远是self,指向调用该方法的实例本身,其他参数和一个普通函数完全一样。如下例:
classPerson(onbject):
def __init__(self, name)
self.__name=name
def get_name(self):
return self.__name
其中get_name(self) 就是一个实例方法,__init__(self, name)其实也可看做是一个特殊的实例方法。调用实例方法必须在实例上调用,如:
p1=Person(‘Bob’)
print(p1.get_name()) #输出Bob,注意有()
在实例方法内部,可以访问所有实例属性,这样,如果外部需要访问私有属性,可以通过实例方法调用获得,这种数据封装形式除了保护内部数据一致性外,也简化了外部调用难度。
实例绑定函数:实例方法也是属性,它实际上是一个函数对象,将一个函数变为一个实例方法采用types.MethodType() ,
这种方法并不常见,通常直接在class中定义。
定义类方法:和属性类似,方法也分为实例方法和类方法。类方法的定义如下:
输出:0 1
通过标记一个@classmethod将该方法绑定到Person类上而非类的实例上,类方法的第一个参数将传入类本身,通常将参数名命名为 cls,上面的 cls.count 实际上相当于 Person.count。因为是在类上调用,而非实例上调用,因此类方法无法获得任何实例变量,只能获得类的引用,如上例。
4、 类的继承:如果已经定义了Person类,需要定义新的Student类,可以直接从Person类继承。定义Student类时,只需要把额外的属性加上,例如父类已有了name和gender属性,需要为Student添加score属性:
classStudent(Person):
def __init__(self, name, gender, score):
#必须先初始化父类,否则将无法继承name和gender
super(Student,self).__init__(name, gender)
self.score=score
其中函数super(Student,self)将返回当前类继承的父类,即 Person ,然后调用__init__()方法,注意self参数已在super()中传入,在__init__()中将隐式传递,不能写出来。
我们把Person称为父类,基类或超类;Student称为子类,派生类或继承类。子类和父类是is关系,即一个实例是一个子类那么它也是一个父类,反之则不然。对于has关系的类应该使用组合而非继承,例如Student类和Book类是has关系,则应如下方式组合
classStudent(Person):
def __init__(self, bookName):
self.book=Book(bookNme)
Python总是从某个类继承,如果没有合适的类继承就从object继承。
判断类型:函数isinstance可以判断一个变量的类型,可用在Python内置的数据类型如str、list、dict,也可用在我们自定义的类。例如假设有Person父类和Student、Teacher两个子类,则令p=Person(…), s=Student(…), t=Teacher(…), 那么使用isinstance判断类型如下:
可以看出父类实例不能是子类类型,因为子类比父类多了一些属性和方法。一个实例可以看成它本身的类型,也可以看成它父类的类型。
多态:类具有继承关系,并且子类类型可以向上转型看做父类类型。多态意味着变量并不知道引用的对象是什么根据引用 的对象不同表现不同的行为方式,(常用于方法的多态)。一个最简单的例子,运算符多态:
A = 3
B = 2
print(A+B)
A=’世界’
B=(‘你好’)
print(A+B)
这是动态语言和静态语言(例如Java)最大的差别之一。动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。
多重继承:Python允许从多个父类继承,称为多重继承。如C同时继承A,B:
class C(A,B):
def __init__(self, a):
super(C, self).__init__(a)
print (‘init C’)
注意若A,B类中都有print(‘init A’)和print (‘init B’),则运行c=C(‘c’)后输出:init A init B init C,具体参考网址
多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用
获取对象信息:对一个变量,可以通过isinstance可以判断其属于那种类型的实例,也可以用type()函数获取变量的类型,它返回一个Type对象:
type(123) #输出<type ‘int’>
s=Student(‘Bob’)
type(s) #输出<class '__main__.Student'>
也可以用dir()函数获取变量的所有属性
对于实例变量,dir()返回所有实例属性,包括”__class__”这类特殊意义的属性,注意方法”whoAmI”也是s的一个属性。如果已知一个属性名称,要获取或设置对象的属性就需要用getattr()和setattr()函数:
getattr(s,‘name’) #获取name属性,输出,Bob
setattr(s,‘name’, ‘Adam’) #设置新的name属性
如果getattr(s, 'age')中age属性不存在则会报错,若getattr(s,'age', 20)则返回默认值20。
5、 定制类:定制类的目的是使我们编写的类能运用的普通的函数中,要实现这个目的则要实现特殊方法。
特殊方法:任何数据类型的实例都有一个特殊方法__str__(),即定义list=[1,2],则print list和print list.__str__()输出结果都为[1,2],Python的特殊方法有用于print的__str__,用于len的__len__,用于cmp的__cmp__……,特殊方法定义在class中且不需要直接调用,Python的某些函数或操作符会自动调用对应的特殊方法。正确实现特殊方法只需要编写用到的特殊方法,且有关联性的特殊方法都必须实现,如我们定义了__getattr__方法,就必须同时编写__setattr__和__delattr__这三个方法。例:
classPerson(object):
def __init__(self, name):
self.name=neme
def __str__(self):
return ‘(Person: %s)’ % (self.name)
p=Person(‘Bob’)
print(p) #输出(Person:Bob)
若直接敲变量p,即不加print则输出
<main.Personobject at 0x10c941890> (__str__()未被调用)
出现上面的情况是因为Python中定义了__str__()和__repr__()两种方法,__str__()用于显示给用户,而__repr__()用于显示给开发人员,有一个偷懒的定义__repr__的方法即__repr__=__str__。
类比较__cmp__():对int、str等内置数据类型排序时,可用sort()函数按照默认的比较函数cmp排序,但对于一组Student类的实例排序时就必须使用特殊方法__cmp__()。例如:
classStudent(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.name < s.name:
return -1
elif self.name > s.name:
return 1
else:
return 0
其中__cmp__()用实例自身self和传入的实例s进行比较,如下Student类实现了按name进行排序:
L=[Student(‘Tim’,99),Student(‘Bob’,88),Student(‘Alice’,79)]
print(sorted(L))
输出:[(Alice: 77), (Bob: 88),(Tim: 99)]
注意如果list不仅仅包含Student类,则__cmp__可能会报错,如:L=[Student('Tim',99), Student('Bob', 88), 100, 'Hello'],则可先判断isinstance(s,Student)==Ture
类个数__len__():与__cmp__()函数一样,要获取类元素个数则必须采用特殊方法__len__(),如:
classStudent(object):
def __init__(self, *args):
self.name=args
def __len__(self):
returnlen(self.name)
ss=Student(‘A’,’B’,’C’)
print(len(ss)) #输出为3
类的数学运算:Python中四则运算可用于int,float,,有理数,矩阵等,对于有理数的表示可用Rational类表示:
classRational(object):
def __init__(self, p, q): # p,q为整数,表示有理数p/q.
self.p=p
self.q=q
def __add__(self,r): #让Rational进行+运算(通分)
return Rational(self.p* r.q + self.q * r.p, self.q * r.q)
def __str__(self):
return '%s/%s' % (self.p, self.q)
__repr__ = __str__
R1=Rational(1,3)
R2=Rational(1,2)
print(R1+R2) # 输出5/6
其中+—*/运算分别为:__add__,__sub__,__mul__,__div__,欧几里得最大公约数算法实现:
defgcd(a, b):
if b == 0:
return a
return gcd(b, a % b)
g=gcd(p,q) #g为p,q的最大公约数
类型转换:对于将Rational转为int,使以下方法:
class Rational(object):
def __init__(self, p, q):
self.q=q
self.p=p
def __int__(self):
return self.p//self.q
print int(Rational(7,2)) #输出 3
同理float要用def__float__(self): return float(self.p) / self.q
类中装饰器:在函数式编程中有装饰器函数,同样也可将get/set方法“装饰”成属性调用 (?是否还可以装饰其他方法) 。如下两种装饰和未装饰的方法:
对于判断设置分数的有效性:第一种方法需要调用s.set_score(1000),加了装饰器后只需要输入s.score=1000,两者输出结果相同。注意: 第一个score(self)是get方法,用@property装饰,第二个score(self, score)是set方法,用@score.setter装饰,@score.setter是前一个@property装饰后的副产品。
限制属性添加__slots__:由于Python是动态语言,任何实例在运行期都可以动态地添加属性,如果要限制添加属性如Student类只允许添加name,gender这两个属性,则需要用__slots__来实现,如下例。如果不需要添加任意动态的属性,使用__slots__也能节省内存。
classStudent(object):
__slots__=(‘name’,’gender’)
def __init__(self, name, gender):
self.name=name
self.gender=gender
函数调用__call__():在Python中函数其实是一个对象,所有的函数都是可调用对象。对于一个类实例也可通过__call_()变成一个可调用对象。如下将Person类变成一个可调用对象:
classPerson(object):
def __init__(self, name, gender):
self.name=name
self.gender=gender
def __call__(self,friend):
print (‘My name is %s…’ % self.name)
print (‘My friend is %s…’ % friend)
p=Person(‘Bob’,‘male’)
p(‘Tim’) #主要Tim是__call__的第二个参数
输出:My name is Bob…
My friend is Tim…
单看 p('Tim') 你无法确定 p 是一个函数还是一个类实例,所以,在Python中,函数也是对象,对象和函数的区别并不显著。
枚举类:当定义常量时,可用大写变量通过整形定义,如月份:JUN=1……,这种方法比较简单,但类型是int,且仍是变量。另一种更好的方法就是用枚举类型来定义一个class类,每个常量都是class的一个唯一实例。如下:
from enum import Enum
Month= Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep','Oct', 'Nov', 'Dec'))
这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员:
for name,member in Month.__members__.items():
print(name, '=>', member, ',',member.value)
value属性是自动赋给成员的int常量,默认从1开始计数
若要更精确的控制枚举类型,可从Enum派生出自定义类:
from enum import Enum, unique
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
……
@unique装饰器检查保证没有重复值,访问这些类型的方法:
day1=Wenkday.Mon
print(Weekday.Tue) #输出Weekday.Tue
print(Weekday['Tue']) #输出WeekdayTue
print(Weekday.Tue.value) #输出2
print(day1== Weekday.Mon) #输出TRUE
print(day1== Weekday(1)) #输出TRUE
可见,既可以用成员名引用枚举变量,也可以用value值来获取枚举常量。
元类:动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。type()函数可以查看一个类型或变量的类型。type()函数既可以返回一个对象的类型,又可以创建出新的类型。如下:
deffn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
Hello= type('Hello', (object,), dict(hello=fn)) #创建Helloclass
该方法等效于:
class Hello(object):
defhello(self, name='world'):
print('Hello, %s.' % name)
type()函数创建class类依次传入3个参数:class的名称;继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
对于控制类的创建行为可以使用mataclass, metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。一般较少遇到。
6、 下一步学习IO:文件和Socket编程;多任务:进程和线程;数据库编程;Web开发