Python进阶笔记1-高阶函数


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):

#必须先初始化父类,否则将无法继承namegender

            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开发

发布了21 篇原创文章 · 获赞 15 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/liumingchun13/article/details/71158060