zoukankan      html  css  js  c++  java
  • Python基础教程读书笔记(第7章—第8章:更加抽象;异常)

    第七章:更加抽象

    1:对象的重要优点:多态(Polymorphism)、封装(Encapsulation)、继承(Inheritance)

    1)多态:多态意味着就算不知道变量所引用的对象类型是什么,还是能对它进行操作,而它也会根据对象(或类)类型的不同而表现出不同的行为。
    (1) 多态和方法——绑定到对象特性上面的函数称为方法。标准库random中包含choice函数,可以从序列中随机选出元素:

    >>> from random import choice
    >>> x=choice(['Hello,world!', [1,2,'e','e',4]]) #x=[1, 2, 'e', 'e', 4]
    >>> x.count('e')
    2

    很多函数和运算符都是多态的,唯一能毁掉多态的就是使用函数显式的检查类型,如:type、isinstance以及issubclass 函数等

    2):封装:封装是对全局作用域中其他区域隐藏多余信息的原则。听起来像多态——使用对象而不知道其内部细节,两者概念类似,因为它们都是抽象的原则——它们都会帮助处理程序组件而不用过多关心多余细节
    但封装不等同于多态。多态可以让用户对于不知道是什么类(或对象类型)的对象进行方法调用,而封装是可以不用关心对象是如何构建的而直接进行使用。

    3)继承——继承是另一个懒惰(褒义)的行为。

    2:类和类型
    1)类:python中,习惯使用单数名词来描述对象的类,如Bird和Lark

    2)创建类:新式类的语法中,需要在模块或脚本开始的地方放置语句 __metaclass__ = type

    View Code
    __metaclass__ = type #确定使用新式类
    
    #class语句会在函数定义的地方创建自己的命名空间
    class Person:
    
        #self是对于对象自身的引用
        def setName(self,name):
            self.name = name
    
        def getName(self):
            return self.name
    
        def greet(self):
            print("Hello,world! I'm %s." % self.name)
    
    
    #samples
    >>> foo = Person()
    >>> bar = Person()
    >>> foo.setName('Luke Skywalker')
    >>> bar.setName('Anakin Skywalker')
    >>> foo.greet()
    Hello,world! I'm Luke Skywalker.
    >>> bar.greet()
    Hello,world! I'm Anakin Skywalker.

    在调用foo的setName和greet函数时,foo自动将自己作为第一个参数传入函数中——因此形象的命名为self(习惯上叫做self,可以命名为其他)
    如果知道foo是Person的实例的话,还可以把foo.greet()看做Person.greet(foo)方便的简写

    3)特性、函数、和方法
    self 参数事实上正是方法和函数的区别。方法(更专业一点可称为绑定方法)将它们的第一个参数绑定到所属的实例上,因为这个参数可以不必提供。所以可以讲特性绑定到一个普通函数上,这样就不会有特殊的self参数了

    View Code
    >>> class Class:
        def method(self):
                    #print(self) => <__main__.Class object at 0x02D7AB50>
            print('I have a self!')
    
            
    >>> def function():
        print('I don\'t have a self!')
    
        
    >>> instance = Class()
    >>> instance.method()
    I have a self!
    >>> instance.method = function
    >>> instance.method()
    I don't have a self!

    注意,self 参数并不取决于调用方法的方式,目前使用的是实例调用方法,可以随意使用引用同一个方法的其他变量:

    >>> class Bird:
        song = 'Squaawk!'
        def sing(self):
            print(self.song)
    
            
    >>> bird = Bird()
    >>> bird.sing()
    Squaawk!
    >>> birdsong=bird.sing
    >>> birdsong()
    Squaawk!

    尽管最后一个方法调用看起来与函数调用十分相似,但是变量birdsong引用绑定方法bird.sing上,也就意味着这还是对self参数的访问(意即它仍旧绑定到类的相同实例上)。
    提示:在第九章将会介绍类是如何调用超类的方法的(具体来说就是超类的构造器)。这些方法直接通过类调用,它们没有绑定自己的self参数到任何东西上,所以叫做非绑定方法

    再论私有化:有些人(如SmallTalk之父,SmallTalk的对象特性只允许由同一个对象的方法访问)觉得这样破坏了封装的原则,他们认为对象的状态对于外部应该是完全隐藏(不可访问)的。
    使用私有(private)特性,这是对象外部无法访问,但getName和setName等访问器(accessor)能够访问的特性——属性是访问器的好选择

    Python不直接支持私有方式,可以用一些小技巧达到私有特性的效果:为了让方法或特性变为私有(从外部无法访问),只要在它的名字前面加上双下划线即可

    View Code
    >>> class Secretive:
        def __inaccessible(self):
            print("Bet you can't see me...")
    
        
    >>> class Secretive:
        def __inaccessible(self):
            print("Bet you can't see me...")
        def accessible(self):
            print("The secret message is:")
            self.__inaccessible()
    
            
    >>> s = Secretive()
    >>> s.__inaccessible()
    Traceback (most recent call last):
      File "<pyshell#124>", line 1, in <module>
        s.__inaccessible()
    AttributeError: 'Secretive' object has no attribute '__inaccessible'
    >>> s.accessible()
    The secret message is:
    Bet you can't see me...
    #在类的内部定义中,双下划线开始的名字被“翻译”成前面加下划线和类名的形式
    >>> Secretive._Secretive__inaccessible
    <function Secretive.__inaccessible at 0x02D7EA50>
    >>> s._Secretive__inaccessible
    <bound method Secretive.__inaccessible of <__main__.Secretive object at 0x02D7AD90>>
    #所以实际上还是能在类外访问这些私有方法的:
    >>> s._Secretive__inaccessible()
    Bet you can't see me...

    如果不需要使用这种方法但是又想让其他对象不要访问内部数据,那么可以使用单下划线。例如:前面有下划线的名字都不会被带星号的imports语句(from module import *)导入

    4)类的命名空间:定义类时,所有位于class语句中的代码都在特殊的命名空间中执行——类命名空间(class namespace)

    5)指定超类

    View Code
    >>> class Filter:
        def init(self):
            self.blocked = []
        def filter(self,sequence):
            return [x for x in sequence if x not in self.blocked]
    
         
    >>> class SPAMFilter(Filter): # SPAMFilter是Filter的子类
        def init(self): #重写Filter超类中的init方法
            self.blocked = ['SPAM']
    
            
    >>> #Filter是个用于过滤序列的通用类,事实上它不能过滤任何东西
    >>> f = Filter()
    >>> f.init()
    >>> f.filter([1,2,3])
    [1, 2, 3]
    >>> #Filter类的作用是它可以用作其他类的基类(超类),比如SPAMFilter类,可将序列中的“SPAM过滤除去”
    >>> s = SPAMFilter()
    >>> s.init()
    >>> s.filter(['SPAM','SPAM','SPAM','SPAM','eggs','bacon','SPAM'])
    ['eggs', 'bacon']

    6)调查继承:如果想要查看一个类是否是另一个的子类,可以使用内建的issubclass函数

    >>> issubclass(SPAMFilter,Filter)
    True

    如果想要知道已知类的基类,可以直接使用它的特殊特性:__bases__

    >>> SPAMFilter.__bases__
    (<class '__main__.Filter'>,)

    同样还能用isinstance 方法检查一个对象是否是一个类的实例:

    >>> s = SPAMFilter()
    >>> isinstance(s,SPAMFilter)
    True

    注意:使用isinstance并不是个好习惯,使用多态会更好一些
    如果想要知道一个对象属于哪个类,可以使用__class__特性:

    >>> s.__class__
    <class '__main__.SPAMFilter'>

    7)多个超类:

    class Calculator:
        def calculate(self,expression):
            self.value = eval(expression)
    
    class Talker:
        def talk(self):
            print('Hi,my value is ', self.value)
    
    class TalkingCalculator(Calculator, Talker):
        pass
    
    
    #sample
    >>> tc = TalkingCalculator()
    >>> tc.calculate('1+2*3')
    >>> tc.talk()
    Hi,my value is  7

    子类(TalkingCalculator)自己不做任何事,它从自己的超类继承所有的行为。这种行为称为多重继承(multiple inheritance)。使用多重继承时,如果一个方法从多个超类继承(也就是说有两个相同名字的不同方法),那么必须注意一下超类的顺序(在class语句中):先继承的类中的方法会重写后继承的类中的方法。所以如果前例中的Calculator类也有一个talk方法,那么它就会重写Talker的talk方法(使其不可访问)。如果把它们的顺序掉过来,像这样:
    class TalkingCalculator(Talker,Calculator):pass 就会让Talker类中的talk方法可用了。
    如果超类们共享一个超类,那么在查找给定方法或者特性时访问超类的顺序称为MRO(Method Resolution Order,方法判定顺序)

    8)接口和内省
    “接口”的概念和多态有关,在处理多态对象时,只要关心它的接口(或称“协议”)即可——也就是公开的方法和特性。在Python中,不用显示地指定对象必须包含哪些方法才能作为参数接收。不用(像在java中一样)显式地编写接口,可以在使用对象的时候假定它可以实现你所要求的行为。如果它不能实现的话,程序就会失败。

    可以检查所需方法是否已经存在:

    >>> hasattr(tc,'talk')
    True
    >>> hasattr(tc,'fnord')
    False

    还可以检查talk特性是否可调用:

    >>> callable(getattr(tc,'talk',None))
    True
    >>> callable(getattr(tc,'fnord',None))
    False

    getatrr函数允许提供默认值,以便在特性不存在时使用,然后对返回的对象使用callable函数。与getattr相对应的函数是setattr,可以用来设置对象的特性:

    >>> setattr(tc,'name','Mr.Gumby')
    >>> tc.name
    'Mr.Gumby'

    3:一些关于面向对象设计的思考

    1)将属于一类的对象放在一起。如果一个函数操纵一个全局变量,那么两者最好都在类内作为特性和方法出现.
    2)不要让对象过于亲密。方法应该只关心自己实例的特性,让其他实例管理自己的状态
    3)要小心继承,尤其是多重继承。继承机制有时很有用,但也会在某些情况下让事情变得过于复杂。多重继承难以正确使用,更难调试
    4)简单就好。让你的方法小巧。一般来说,多数方法都应能在30秒内被读完(以及理解)

    当考虑需要什么类以及类要有什么方法时,应该尝试下面的方法:
    1)写下问题的描述(程序要做什么?),把所有名词、动词和形容词加下划线
    2)对于所有名词,用作可能的类
    3)对于所有动词,用作可能的方法
    4)对于所有形容词,用作可能的特性
    5)把所有方法和特性分配到类

    现在已经有了面向对象模型的草图了。还可以考虑类和对象之间的关系(比如继承或协作)以及它们的作用,可以用以下步骤精炼模型:
    1)写下(或者想象)一系列的使用实例——也就是程序应用时的场景,试着包括所有的功能
    2)一步步考虑每个使用实例,保证模型包括所有需要的对象。如果有遗漏的话就添加进来。如果某处不太正确则改正。继续,直到满意为止。

    当认为已经有了可以应用的模型时,就可以开工了。

    4:小结

    对象:对象包括特性和方法。特性只是作为对象的一部分的变量,方法则是存储在对象内的函数。(绑定)方法和其他函数的区别在于方法总是将对象作为自己的第一个参数,这个参数一般称为self

    类:类代表对象的集合(或一类对象),每个对象(实例)都有一个类。类的主要任务是定义它的实例会用到的方法

    多态:多态是实现将不同类型和类的对象进行同样对待的特性——不需要指定对象属于哪个类就能调用方法

    封装:对象可以讲它们内部状态隐藏(或封装)起来。在一些语言中,这意味着对象的状态(特性)只对自己的方法可用。在Python中,所有的特性都是公开可用的

    继承:一个类可以是一个或者多个类的子类。子类从超类继承所有方法。

    接口和内省:程序员可以靠多态调用自己想要的方法。不过如果想哟知道对象到底有什么方法和特性,可以用getattr等函数

    面向对象设计

    第八章:异常

    1:什么是异常——Python用异常对象(exception object)来表示异常情况。遇到错误后,会引发异常。如果异常对象并未被处理或捕捉,程序就会用所谓的回溯(Traceback,一种错误信息)终止执行。事实上,每个异常都是一些类的实例,这些实例可以被引发,并且可以用很多种方法进行捕捉,使得程序可以捉住错误并且对其进行处理,而不是让整个程序失败

    2:按自己的方式出错——如何引发异常,甚至创建自己的异常类型

    1)raise 语句:为了引发异常,可以使用一个类(应该是Exception的子类)或者实例参数调用raise语句。使用类时,程序会自动创建实例。内建的异常有很多,都在exceptions模块中,可以用dir函数列表模块的内容

    2)自定义异常类:class SomeCustomException(Exception):pass

    3:捕捉异常:异常最有意思的地方就是可以处理它们(通常叫诱捕或捕捉异常)。这个功能使用try/except实现

    try:
        x=input('Enter the first number: ')
        y=input('Enter the second number: ')
        print(int(x)/int(y))
    except ZeroDivisionError as err :
        print('The second number can\'t be zero!')

    注意:如果没有捕捉到异常,它会被“传播”到调用的函数中。如果那里依然没有捕获,这写异常就会“浮”到程序的最顶层。也就是说你可以捕捉到在其他人的函数中所引发的异常

    如果捕捉到了异常,但是又想重新引发它(也就是说要传递异常),那么可以调用不带参数的raise(还能捕捉到异常时显式地提高具体异常)。举例来说,一个能“屏蔽”ZeroDivisionError(除0错误)的计算器类。如果整个行为被激活,那么计算器就会打印错误信息,而不是让异常传播。如果在与用户进行交互的过程中使用,就很有用了,但是如果是在程序内部使用,引发异常会更好。

    >>> class MuffledCalculator:
        muffled = False
        def calc(self,expr):
            try:
                return eval(expr)
            except ZeroDivisionError:
                if self.muffled:
                    print('Division by zero is illegal')
                else:
                    raise
    
                
    >>> calculator = MuffledCalculator()
    >>> calculator.calc('10/2')
    5.0
    #示例:分别打开和关闭了屏蔽
    >>> calculator.calc('10/0')
    Traceback (most recent call last):
      File "<pyshell#154>", line 1, in <module>
        calculator.calc('10/0')
      File "<pyshell#151>", line 5, in calc
        return eval(expr)
      File "<string>", line 1, in <module>
    ZeroDivisionError: division by zero
    >>> calculator.muffled = True
    >>> calculator.calc('10/0')
    Division by zero is illegal

    注意:如果除零行为发生而屏蔽机制被打开,那么calc方法会(隐式地)返回None。换句话说,如果打开了屏蔽机制,那么就不应该依赖返回值

    4:不止一个except子句

    try:
        x=input('Enter the first number: ')
        y=input('Enter the second number: ')
        print(x/y)
    except ZeroDivisionError :
        print('The second number can\'t be zero!')
    except TypeError:
        print('That wasn\'t a number,was it?')

    应该注意到,异常处理不会搞乱原来的代码,而增加一堆if语句检查可能的错误情况会让代码相当难读

    5:用一个块捕捉两个异常:

    try:
        x=input('Enter the first number: ')
        y=input('Enter the second number: ')
        print(x/y)
    except (ZeroDivisionError,TypeError,NameError): #注意不能没有括号,因为是元组
        print('......')

    6:捕捉对象:
    如果希望在except子句中访问异常对象本身,可以使用两个参数(注意,就算要捕捉多个异常,也只需向except子句提供一个参数——一个元组)。

    try:
        x=input('Enter the first number: ')
        y=input('Enter the second number: ')
        print(x/y)
    except (ZeroDivisionError,TypeError) as err:
        print(err)

    7:真正的全捕捉
    就算程序处理了好几种类型的异常,那有些异常还是会从眼皮底下溜走。比如上面的除法程序,在提示符下直接回车,不输入任何东西,会引发异常,这个异常逃过了try/except语句的检查——很正常。如果想捕捉所有异常,则在except子句中忽略所有的异常类:

    try:
        x=input('Enter the first number: ')
        y=input('Enter the second number: ')
        print(x/y)
    except :
        print('Something wrong happend')

    注意:这样捕捉所有异常是危险的,因为它会隐藏所有程序员未想到并且未做好准备处理的错误。使用except Exception,e会更好,或者对异常对象e进行一些检查

    8:万事大吉
    有些情况,一些坏事发生时执行一段代码是很有用的,可以像对条件和循环语句那样,给try/except语句加个else子句:

    while True:    
        try:
            x=input('Enter the first number: ')
            y=input('Enter the second number: ')
            print(int(x)/int(y))
        except Exception as e :
    print(e)
    print('Invalid input,please try again') else: break

    9:最后——Finally子句,它可以用来在可能的异常后进行清理。它和try子句联合使用

    10:异常和函数
    异常和函数能自然地一起工作。如果异常在函数内引发而不被处理,它就会传播至(浮到)函数调用的地方。如果那里也没有处理异常,它就会继续传播,一直到达主程序(全局作用域)。如果还没有异常处理程序,程序就会带着堆栈跟踪中止

    11:异常之禅
    有时,条件语句可以实现和异常处理同样的功能,但是条件语句可能在自然性和可读性上差些。而从另一方面来看,某些程序中使用if/else实现会比使用try/except要好。

    假设有个字典,我们希望打印出存储在特定的键下面的值。如果该键不存在,则说明也不做,代码可能这样:

    def describePerson(person):
        print('Description of', person['name'])
        print('Age', person['age'])
        if 'occupation' in person:
            print('Occupation:', persion['occupation'])

    代码很直观,但效率不高(此处效率其实微乎其微)。另外一种方案:

    def describePerson(person):
        print('Description of', person['name'])
        print('Age', person['age'])
        try:
            print('Occupation: ' + person['occupation'])
        except KeyError:pass

    这个程序假定‘occupation’键存在。如果的确存在,取出它的值打印即可——不用额外检查它是否真的存在。如果不存在,则会引发KeyError异常,而被except子句捕捉到
    try/except 语句在Python中的表现可以用这样一句话来解释:“请求宽恕易于请求许可”

    小结
    异常对象——异常情况(如发生错误)可以用异常对象表示。它们可以用几种方法处理,忽略的话,程序就会中止

    警告——类似异常,但是(一般来说)仅仅打印错误信息

    引发异常——可以使用raise语句引发异常。它接受异常类或异常实例作为参数。还能提供两个参数(异常和错误信息)。如果在except子句中就不要使用参数调用raise,它会“重新引发”该字句捕捉到的异常

    自定义异常类——用继承Exception类的方法可以创建自己的异常类

    捕捉异常——try/except 子句捕捉异常。如果except子句中部特别指定异常类,那么所有的异常都会被捕捉。异常可以放在元组中以实现多个异常的指定。如果给exceptexcept提供两个参数,第2个参数就会绑定到异常对象上。同样,try/except还可以包含多个except子句

     else子句——如果try块中没有引发异常,else子句就会被执行

    finally——如果需要确保某些代码不管是否有异常引发都要执行(如清理代码),可以放在finally子句中

    异常和函数——在函数中引发异常时,它就会被传播到函数调用的地方(对于方法也是一样)

  • 相关阅读:
    .net core Ocelot Consul 实现API网关 服务注册 服务发现 负载均衡
    .net core grpc 实现通信(一)
    Shell脚本
    LNMP学习内容总结①
    2018/12/18学习内容摘要
    2019/12/16学习内容摘要(Vim)
    第一周进度及学习总结
    2019/12/12学习内容摘要(Linux系统用户与用户组管理②)
    2019/12/13学习内容摘要(Linux磁盘管理①)
    2019/12/11学习内容摘要(Linux系统用户与用户组管理①)
  • 原文地址:https://www.cnblogs.com/mumue/p/2861737.html
Copyright © 2011-2022 走看看