zoukankan      html  css  js  c++  java
  • python基础学习笔记3

    特殊方法与多范式

     

    Python一切皆对象,但同时,Python还是一个多范式语言(multi-paradigm),你不仅可以使用面向对象的方式来编写程序,还可以用面向过程的方式来编写相同功能的程序(还有函数式、声明式等,我们暂不深入)。Python的多范式依赖于Python对象中的特殊方法(special method)。

    特殊方法名的前后各有两个下划线。特殊方法又被成为魔法方法(magic method),定义了许多Python语法和表达方式,正如我们在下面的例子中将要看到的。当对象中定义了特殊方法的时候,Python也会对它们有“特殊优待”。比如定义了__init__()方法的类,会在创建对象的时候自动执行__init__()方法中的操作。

    对于内置的对象来说(比如整数、表、字符串等),它们所需要的特殊方法都已经在Python中准备好了。而用户自己定义的对象也可以通过增加特殊方法,来实现自定义的语法。特殊方法比较靠近Python的底层,许多Python功能的实现都要依赖于特殊方法。  
     
    上下文管理器

     
    context manager ,是Python2.5之后开始支持一个中语法 用于规定某个对象的使用范围。一旦进入或者离开该使用范围,会有特殊操作被调用 (比如为对象分配或者释放内存)。它的语法形式是with...as...
     
    关闭文件
    我们会进行这样的操作:打开文件,读写,关闭文件。程序员经常会忘记关闭文件。上下文管理器可以在不需要文件的时候,自动关闭文件。
    下面我们看一下两段程序:
    # without context manager
    f = open("new.txt", "w")
    print(f.closed)               # whether the file is open
    f.write("Hello World!")
    f.close()
    print(f.closed)

    以及:

    # with context manager
    with open("new.txt", "w") as f:
        print(f.closed)
        f.write("Hello World!")
    print(f.closed)
    两段程序实际上执行的是相同的操作。我们的第二段程序就使用了上下文管理器 (with...as...)。上下文管理器有隶属于它的程序块。当隶属的程序块执行结束的时候(也就是不再缩进),上下文管理器自动关闭了文件 (我们通过f.closed来查询文件是否关闭)。我们相当于使用缩进规定了文件对象f的使用范围。
     
    上面的上下文管理器基于f对象的__exit__()特殊方法(还记得我们如何利用特殊方法来实现各种语法?参看特殊方法与多范式)。当我们使用上下文管理器的语法时,我们实际上要求Python在进入程序块之前调用对象的__enter__()方法,在结束程序块的时候调用__exit__()方法。对于文件对象f来说,它定义了__enter__()和__exit__()方法(可以通过dir(f)看到)。在f的__exit__()方法中,有self.close()语句。所以在使用上下文管理器时,我们就不用明文关闭f文件了。
     
    自定义:
    任何定义了__enter__()和__exit__()方法的对象都可以用于上下文管理器。
    文件对象f是内置对象,所以f自动带有这两个特殊方法,不需要自定义。
     
    对象的属性

     
    Python一切皆对象(object),每个对象都可能有多个属性(attribute)。Python的属性有一套统一的管理方案。

    对象的属性可能来自于其类定义,叫做类属性(class attribute)。类属性可能来自类定义自身,也可能根据类定义继承来的。一个对象的属性还可能是该对象实例定义的,叫做对象属性(object attribute)。

    对象的属性储存在对象的__dict__属性中。__dict__为一个词典,键为属性名,对应的值为属性本身。
     
    闭包

     
    闭包(closure)是函数式编程的重要的语法结构。函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式)。在面向过程编程中,我们见到过函数(function);在面向对象编程中,我们见过对象(object)。函数和对象的根本目的是以某种逻辑方式组织代码,并提高代码的可重复使用性(reusability)。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
     
    函数是一个对象,可以作为某个函数的返回结果:
    def line_conf():
        def line(x):
            return 2*x+1
        return line          # return a function object
    
    my_line = line_conf()
    print(my_line(5))
    上面的代码可以成功运行。line_conf的返回结果被赋给line对象。上面的代码将打印11。
     
    如果line()的定义中引用了外部的变量,会发生什么呢?
    def line_conf():
        b = 15
        def line(x):
            return 2*x+b
        return line            # return a function object
    b = 5
    my_line = line_conf()
    print(my_line(5))

    我们可以看到,line定义的隶属程序块中引用了高层级的变量b,但b信息存在于line的定义之外 (b的定义并不在line的隶属程序块中)。我们称b为line的环境变量。事实上,line作为line_conf的返回值时,line中已经包括b的取值(尽管b并不隶属于line)。

    上面的代码将打印25,也就是说,line所参照的b值是函数对象定义时可供参考的b值,而不是使用时的b值。
     
    下面看一个闭包的实际例子:
    def line_conf(a, b):
        def line(x):
            return ax + b
        return line
    
    line1 = line_conf(1, 1)
    line2 = line_conf(4, 5)
    print(line1(5), line2(5))
    这个例子中,函数line与环境变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个环境变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。
    如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。利用闭包,我们实际上创建了泛函。line函数定义一种广泛意义的函数。这个函数的一些方面已经确定(必须是直线),但另一些方面(比如a和b参数待定)。随后,我们根据line_conf传递来的参数,通过闭包的形式,将最终函数确定下来。
     
    装饰器

     
    我们先定义两个简单的数学函数,一个用来计算平方和,一个用来计算平方差:
    # get square sumdef square_sum(a, b):
        return a**2 + b**2
    # get square diffdef square_diff(a, b):
        return a**2 - b**2
    print(square_sum(3, 4))
      print(square_diff(3, 4))

    在拥有了基本的数学功能之后,我们可能想为函数增加其它的功能,比如打印输入。我们可以改写函数来实现这一点:
    # modify: print input
    
    # get square sumdef square_sum(a, b):
        print("intput:", a, b)
        return a**2 + b**2
    
    # get square diffdef square_diff(a, b):
        print("input", a, b)
        return a**2 - b**2
    
    print(square_sum(3, 4))
    print(square_diff(3, 4))
    我们修改了函数的定义,为函数增加了功能。
     
    现在,我们使用装饰器来实现上述修改:
    def decorator(F):
        def new_F(a, b):
            print("input", a, b)
            return F(a, b)
        return new_F
    
    # get square sum@decorator
    def square_sum(a, b):
        return a**2 + b**2
    
    # get square diff@decorator
    def square_diff(a, b):
        return a**2 - b**2
    
    print(square_sum(3, 4))
    print(square_diff(3, 4))

    装饰器可以用def的形式定义,如上面代码中的decorator。装饰器接收一个可调用对象作为输入参数,并返回一个新的可调用对象。装饰器新建了一个可调用对象,也就是上面的new_F。new_F中,我们增加了打印的功能,并通过调用F(a, b)来实现原有函数的功能。

    定义好装饰器后,我们就可以通过@语法使用了。在函数square_sum和square_diff定义之前调用@decorator,我们实际上将square_sum或square_diff传递给decorator,并将decorator返回的新的可调用对象赋给原来的函数名(square_sum或square_diff)。 所以,当我们调用square_sum(3, 4)的时候,就相当于:
    square_sum = decorator(square_sum)
    square_sum(3, 4)

    我们知道,Python中的变量名和对象是分离的。变量名可以指向任意一个对象。从本质上,装饰器起到的就是这样一个重新指向变量名的作用(name binding),让同一个变量名指向一个新返回的可调用对象,从而达到修改可调用对象的目的。

    与加工函数类似,我们可以使用装饰器加工类的方法。
     
    如果我们有其他的类似函数,我们可以继续调用decorator来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。
     
    总结

    总结
    • 上下文管理器
      • 在不需要文件的时候,自动关闭文件
      • with...as...
        • 有隶属于它的程序块
        • 当程序块结束后,自动关闭文件
    • 对象属性
    • 闭包
      • 函数line,和环境变量 a,b 构成了闭包,提高了代码的复用性。
    • 装饰器
      • 提高了程序的可重复利用性,并增加了程序的可读性
     
     
     
  • 相关阅读:
    [SHOI2015]零件组装机
    [AH2017/HNOI2017]影魔
    空指针RE第一次公开赛-笔记
    i春秋2020新春公益赛WP
    博客园Markdown编辑器修改代码配色、添加代码行号
    buuctf Writeup
    关于Tarjan的一些问题
    NOIP2013D1T3货车运输 (生成树+树链剖分)
    1051: [HAOI2006]受欢迎的牛 (tarjan强连通分量+缩点)
    CodeForces 438D The Child and Sequence (线段树 暴力)
  • 原文地址:https://www.cnblogs.com/Aiapple/p/5261504.html
Copyright © 2011-2022 走看看