2020-02-02python学习记录(3)-函数式编程&模块

四、函数式编程:

函数式编程,有利于代码的管理,后期的代码的迭代和修改。函数的封装参数的传递,返回值的返回。

高阶函数:

变量可以指向函数、函数名也可能是变量(abs取绝对值)

def add(x, y, f):

    return f(x) + f(y)

把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。

map/reduce:

Python内建了map()和reduce()函数。

map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

map()传入的第一个参数是mix,即函数对象本身。由于结果r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。

一个序列求和,就可以用reduce实现:

运算可以直接用Python内建函数sum(),没必要动用reduce。

filter:

Python内建的filter()函数用于过滤序列。

通过filter去除某些不需要的值

只保留偶数

filter()的作用是从一个序列中筛出符合条件的元素。由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。

sorted:

主要是排序:

sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。

要进行反向排序,不必改动key函数,可以传入第三个参数reverse=Trues

sorted传入key函数,即可实现忽略大小写的排序:

返回函数:

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

def lazy_sum(*args):    def sum():        ax = 0        for n in args:            ax = ax + n        return ax    return sum

匿名函数:

在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。

list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))

相当于下面的代码:

转换代码:

def is_odd(n):    return n % 2 == 1  L = list(filter(is_odd, range(1, 20)))  x = list(filter(lambda x: x % 2 == 1, range(1,20))) print(x)

装饰器:

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

#!/usr/bin/env python3 # -*- coding: utf-8 -*-  import time, functools  def metric(fn):    print('%s executed in %s ms' % (fn.__name__, 10.24))    return fn  @metric def fast(x, y):    time.sleep(0.0012)    return x + y;  @metric def slow(x, y, z):    time.sleep(0.1234)    return x * y * z;  if __name__ == '__main__':  f = fast(11, 22)  s = slow(11, 22, 33)

偏函数:

Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。

int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换:

五、模块:

注意:自己创建模块时要注意命名,不能和Python自带的模块名称冲突。例如,系统自带了sys模块,自己的模块就不可命名为sys.py,否则将无法导入系统自带的sys模块。

使用模块:

Python本身就内置了很多非常有用的模块,只要安装完毕,这些模块就可以立刻使用。

#!/usr/bin/env python3 # -*- coding: utf-8 -*-  ' a test module '  __author__ = 'Michael Liao' __age__ = '23'  import sys  def test():    args = sys.argv    if len(args)==1:        print('Hello, %s!' % args[0])    elif len(args)==2:        print('Hello, %s!' % args[1])    else:        print('Too many arguments!')  if __name__=='__main__':    test()

作用域:

在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过_前缀来实现的。

正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,x123,PI等;

类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等;

安装第三方模块:

在Python中,安装第三方模块,是通过包管理工具pip完成的。

六、面向对象编程

主要是抽象对象的一些特性,实例化具体的一个里面的一个处理。

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

可以类比java的编程

类和实例:

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

class Student(object):    pass

class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

数据封装:

面向对象编程的一个重要特点就是数据封装。在上面的Student类中,每个实例就拥有各自的name和score这些数据.

>>> class stu(object):

...    def __init__(self,name,age):

...            self.name = name

...            self.age = age

...

>>> lisa = stu('lisa',12)

>>> lisa.name

'lisa'

访问限制:

在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:

>>> class stu(object):

...    def __init__(self,name,age):

...            self.__name = name

...            self.__age = age

...

前面添加两个__这个就不能外部修改内容:

改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name和实例变量.__age了:

>>> bart = Student('Bart Simpson', 59)

>>> bart.__name

Traceback (most recent call last):

  File "", line 1, in

AttributeError: 'Student' object has no attribute '__name'

双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量:

继承和多态:

在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

#!/usr/bin/env python3 # -*- coding: utf-8 -*-  class Animal(object):    def run(self):        print('Animal is running...')  class dog(Animal):  pass  if __name__=='__main__':    dog().run()

dog 继承Animal的属性,还有类是一个object,需要调run方法。

#!/usr/bin/env python3 # -*- coding: utf-8 -*-  class Animal(object):    def run(self):        print('Animal is running...')  class dog(Animal):  def dog1(self):  print('dog is ...')  if __name__=='__main__':    dog().dog1()

继承还可以一级一级地继承下来,就好比从爷爷到爸爸、再到儿子这样的关系。而任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。比如如下的继承树:

静态语言 vs 动态语言:

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了.

小结

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。

动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。

获取对象信息:

使用type():

使用isinstance():

使用dir():

对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数。

如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法.

实例属性和类属性:

由于Python是动态语言,根据类创建的实例可以任意绑定属性。

给实例绑定属性的方法是通过实例变量,或者通过self变量:

#!/usr/bin/env python3 # -*- coding: utf-8 -*-  class Student(object):    def __init__(self, name):        self.name = name  s = Student('Bob') s.score = 90  if __name__=='__main__':  s = Student('Bob')  s.score = 90  print(s.score)

小结

实例属性属于各个实例所有,互不干扰;

类属性属于类所有,所有实例共享一个属性;

不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。

七、面向对象高级编程

使用__slots__

正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。

正常的情况下可以随意的添加

为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

class Student(object):    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

>>> class student(object): ...    __slots__ = ('name','age') ... >>> stu = student() >>> stu.name = 'bob' >>> stu.age = 12 >>> stu.score = 12 Traceback (most recent call last):  File "", line 1, in AttributeError: 'student' object has no attribute 'score' >>>

由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。

使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:

class stu2(student): passs s2 = stu2() s2.score = 9999

除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__————

新的继承的不要添加__slots__属性

使用@property

可以通过一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数:

注意到这个神奇的@property,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。

@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

你可能感兴趣的:(2020-02-02python学习记录(3)-函数式编程&模块)