• 面向对象、类、元类、封装、异常处理


    一、

    1.1面向过程编程:过程二字为重点,是指解决问题的步骤。类似一条流水线,机械式的思维方式

        优点:复杂的流程流程化,进而简单化。

        缺点:可扩展性差

           面向对象编程:对象为重点,对象就是”数据“和”功能“的组成。

        优点:可扩展性强,

        缺点:编程复杂度高

      应用场景:用户需求经常变化,互联网应用,游戏,企业

    1.2类:是一系列对象相似特征与技能的结合体。强调:站的角度不一样,得到的分类是不一样的。

      现实中:是先有类,再有对象。

      在程序中:是先定义类,后调用类来产生对象。

    类的用途:属性----增、删、改、查、

    类的定义:

    #先定义类
    class LuffyStudent:
        school='luffycity' #数据属性
     
     
        def learn(self): #函数属性
            print('is learning')

      类体内的代码在类定义阶段就会执行!!!,可以使用__dict__来查看。

    #__init__方法用来为对象定制对象自己独有的特征
    class LuffyStudent:
        school='luffycity'
        def __init__(self,name,sex,age):  #对象的独有特征
            self.Name=name
            self.Sex=sex
            self.Age=age
     
        def learn(self):  # 对象的公共特征
            print('is learning')
     
        def eat(self):
            print('is sleeping')
     
     
    #后产生对象
    stu1=LuffyStudent('王二丫','',18) #LuffyStudent.__init__(stu1,'王二丫','女',18)
    print(stu1.__dict__)
     
    >>>>>>{'Name': '王二丫', 'Sex': '', 'Age': 18}
     
     
    #加上__init__方法后,实例化的步骤
    # 1、先产生一个空对象stu1
    # 2、LuffyStudent.__init__(stu1,'王二丫','女',18)

      类中的数据属性:是所以对象共有的

      类中的函数属性:是绑定给对象使用的,绑定到不同的对象是不同的绑定方法,对象调用绑定方式时,会把对象本身当作第一个传入,传给self

      pyrhon中一切皆对象。

      1.3继承:指的是类与类之间的关系,主要功能就是解决代码中用的问题;是一种创建新类的方式,新建的类可以继承一个或者多个父类,新建的类可称为子类和派生类,父类又可称为基类或者超类。

    内置属性可以用通过__bases__来查看类继承的所有父类

    属性查找:1.对象本身>>>2.对象的类>>>>3.父类

      继承多个父类时的属性查找顺序:python会解析顺序(mro)列表,其遵循的原则:1.子类会先于父类被检查;2.多个父类会根据他们在列表中的顺序被检查;3.如果下一个类存在两个合法的选择,会选择第一个父类。

      1.4派生:

      子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。

      在子类派生出新的方法中重用父类的方法:

        方式一:指名道姓(不依赖继承)【父类名称.方法名称(self,参数)】

        方式二:super()  (依赖继承)  Python2:---【super(对象本身的类名,self).方法名称(具体的数据)】    python3---【super().方法名称(具体的数据)】

      1.5组合:在一个类中以另外一个类的对象作为数据属性,称为类的组合

    class Date:
        def __init__(self,year,mon,day):
            self.year=year
            self.mon=mon
            self.day=day
    
        def tell_info(self):
            print('%s-%s-%s' %(self.year,self.mon,self.day))

    二、抽象类

    import abc
    
    class Animal(metaclass=abc.ABCMeta): #只能被继承,不能被实例化
        all_type='animal'
    
        @abc.abstractmethod
        def run(self):
            pass
    
        @abc.abstractmethod
        def eat(self):
            pass
    
    # animal=Animal()

    class People(Animal):
    def run(self):
    print('people is running')

    def eat(self):
    print('people is eating')

    三、多态与多态性

      多态指一类事物拥有多种形态

      多态性指在不考虑实例类型的情况下使用实例

      多态性分为静态多态性和动态多态性

      多态性的好处:

        1.增加了程序的灵活性

          以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)

        2.增加了程序额可扩展性

          通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

    四、封装

      封装指的是如何进行属性的隐藏

      如何进行封装:使用双下划线(__)开头的方式将属性隐藏起来(设置成私有的)

      其实所有的双下划线开头的名称都会在类定义阶段变形,且只变形一次。

    class A:
        __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
        def __init__(self):
            self.__X=10 #变形为self._A__X
        def __foo(self): #变形为_A__foo
            print('from A')
        def bar(self):
            self.__foo() #只有在类内部才可以通过__foo的形式访问到.
    
    #A._A__N是可以访问到的,
    #这种,在外部是无法通过__x这个名字访问到。

      变形的特点:

        1.在类的外部无法直接访问属性;可间接访问_类名__属性。如:_A__foo

        2.类的内部可以直接访问;直接访问:__属性

        3.子类无法覆盖父类__开头的属性。

      封装的意义:

        封装的数据属性:明确的区分内外。把数据隐藏起来,然后对外提供操作该数据的接口,在接口内附加上对该数据的限制,已完成对该数据属性操作的严格限制。

        封装的方法:目的是隔离复杂度

        封装与扩展性:封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。

      property(特性):property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值。

        如何使用:

    class People:
        def __init__(self,name,weight,height):
            self.name=name
            self.weight=weight
            self.height=height
    
        @property
        def bmi(self):
            return self.weight / (self.height**2)
    
    p1=People('jake',80,1.95)
    print(p1.bmi)

        

      绑定方法与非绑定方法

        一、绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入【自动传值】):

        1. 绑定到类的方法:classmethod装饰器装饰的方法。

                      为类量身定制

                      类.boud_method(),自动将类当作第一个参数传入

                    (其实对象也可调用,但仍将类当作第一个参数传入)

        2. 绑定到对象的方法在类内定义的没有被任何装饰器装饰的方法

                     为对象量身定制

                     对象.boud_method(),自动将对象当作第一个参数传入

                   (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

        二:非绑定方法:用staticmethod装饰器装饰的方法

            1. 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已

        注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说。

        反射:通过字符串映射到对象的属性

          hasattr(对象名/类,'属性')    判断有没有这个属性

          getattr(对象名/类,'属性')   拿到对象的属性

          setattr(对象名/类,'属性')    修改对象的属性

          delattr(对象名/类,'属性')    删除对象的属性

       

        内置方法介绍:链接

        item系列:

    class Foo: #Dict
        def __init__(self,name):
            self.name=name
    
        def __getitem__(self, item): #item='namexxx'
            # print('getitem...')
            return self.__dict__.get(item)
    
        def __setitem__(self, key, value):
            # print('setitem...')
            # print(key,value)
            self.__dict__[key]=value
    
        def __delitem__(self, key):
            # print('delitem...')
            # print(key)
            del self.__dict__[key]
    
    obj=Foo('egon')
    print(obj.__dict__)
    
    
    # 查看属性:
    obj.属性名
    print(obj['namexxx']) #obj.name
    
    
    # 设置属性:
    obj.sex='male'
    obj['sex']='male'
    
    print(obj.__dict__)
    print(obj.sex)
    
    
    # 删除属性
    del obj.name
    del obj['name']
    
    print(obj.__dict__)

        __str__内置方法:

    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def __str__(self):
            # print('====>str')
            return '<name:%s,age:%s>' %(self.name,self.age)
    
    obj=People('egon',18)
    print(obj) #res=obj.__str__()

        __del__内置方法:

    class Open:
        def __init__(self,filename):
            print('open file.......')
            self.filename=filename
    
        def __del__(self):
            print('回收操作系统资源:self.close()')
    
    f=Open('settings.py')
    del f #f.__del__()
    print('----main------') #del f #f.__del__()

    一切皆对象,对象可以怎么用?

      1.都可以被引用,x=obj

      2.都可以当做函数的参数传入

      3.都可以当做函数的返回值

      4.都可以当做容器类的元素,l = [func,time,obj,1]

      

      exec的用法:

    #exec:三个参数
    
    #参数一:包含一系列python代码的字符串
    
    #参数二:全局作用域(字典形式),如果不指定,默认为globals()
    
    #参数三:局部作用域(字典形式),如果不指定,默认为locals()
    
    #可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
    g={
        'x':1,
        'y':2
    }
    l={}
    
    exec('''
    global x,z
    x=100
    z=200
    
    m=300
    ''',g,l)
    
    print(g) #{'x': 100, 'y': 2,'z':200,......}
    print(l) #{'m': 300}

      元类:产生类的类称为元类,默认所有用class定义的类的元类是type

    # 定义类的两种方式:
    # 方式一:class
    class Chinese:  # Chinese=type(...)
        country = 'China'
    
        def __init__(self, namem, age):
            self.name = namem
            self.age = age
    
        def talk(self):
            print('%s is talking' % self.name)
    
    
    # print(Chinese)
    obj = Chinese('egon', 18)
    print(obj, obj.name, obj.age)
    
    # 方式二:type
    # 定义类的三要素:类名,类的基类们,类的名称空间
    class_name = 'Chinese'
    class_bases = (object,) # 默认为object
    
    class_body = """
    country='China'
    
    def __init__(self,namem,age):
        self.name=namem
        self.age=age
    
    def talk(self):
        print('%s is talking' %self.name)
    """
    
    class_dic = {}
    exec(class_body, globals(), class_dic)
    print(class_dic)
    Chinese = type(class_name,class_bases,class_dic)

      如何自定义元类来控制类的创建

    class Mymeta(type):
        def __init__(self, class_name, class_bases, class_dic):
            if not class_name.istitle():
                raise TypeError('类名的首字母必须大写')
    
            if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
                raise TypeError('必须有注释,且注释不能为空')  # 主动抛出异常
    
            super(Mymeta, self).__init__(class_name, class_bases, class_dic)  # 继承原来的属性
    
    
    class Chinese(object, metaclass=Mymeta):  # 指定元类
        """中文人的类"""
        country = 'China'
        
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def talk(self):
            print('%s is talking' % self.name)
    
    
    # Chinese=Mymeta(class_name,class_bases,class_dic)

      如何自定义元类来控制类的实例化

        __call__

    class Foo:
        def __call__(self, *args, **kwargs):
            print(self)
            print(args)
            print(kwargs)
    
    obj=Foo()
    #1、要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法,该方法会在调用对象时自动触发
    #2、调用obj的返回值就是__call__方法的返回值
    res=obj(1,2,3,x=1,y=2) 

      由此可得,调用一个对象,就是在触发对象所在类的__call__方法的执行,同理得类本身也存在一个__call__方法。分析如下:

    class Mymeta(type):  # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        def __call__(self, *args, **kwargs):  # self=<class '__main__.Foo'>
            # 1、调用__new__产生一个空对象obj
            obj = self.__new__(self)  # 此处的self是类Foo,必须传参,代表创建一个Foo的对象obj
    
            # 2、调用__init__初始化空对象obj
            self.__init__(obj, *args, **kwargs)
    
            # 3、返回初始化好的对象obj
            return obj
    
    
    class Foo(object, metaclass=Mymeta):
        school = 'beida'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print('%s says welcome to the BeiDa to learn Python' % self.name)
    
    
    # 调用Foo就是在调用Foo类中的__call__方法
    # 然后将Foo传给self,溢出的位置参数传给*,溢出的关键字参数传给**
    # 调用Foo的返回值就是调用__call__的返回值
    t1 = Foo('jake', 22)
    print(t1)  # 123
    View Code

    默认的在调用Foo时会发生三件事:  

        1.产生一个空对象

        2.调用__init__方法初始化对象obj

        3.返回初始化以后的对象obj

      异常处理:异常时错误发生的信号,一旦程序出错,并且程序没有处理这个错误,就会抛出异常,并且程序的运行就会终止。

      错误的类型,分成两种:

        1.语法错误,(在程序运行之前就会检测语法,需要先改正过来才可以运行程序);

        2.逻辑错误  

    AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
    IOError 输入/输出异常;基本上是无法打开文件
    ImportError 无法引入模块或包;基本上是路径问题或名称错误
    IndentationError 语法错误(的子类) ;代码没有正确对齐
    IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
    KeyError 试图访问字典里不存在的键
    KeyboardInterrupt Ctrl+C被按下
    NameError 使用一个还未被赋予对象的变量
    SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
    TypeError 传入对象类型与要求的不符合
    UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
    导致你以为正在访问它
    ValueError 传入一个调用者不期望的值,即使值的类型是正确的
    View Code

        

      异常处理:为了保证程序的容错性,在遇到错误时程序不会奔溃,我们就需要进行异常处理。

      1.如果错误发生的条件是可以预知的我们用if进行处理,在错误发生之前进行处理

    AGE=10
    while True:
        age=input('>>: ').strip()
        if age.isdigit(): #只有在age为字符串形式的整数时,下列代码才不会出错,该条件是可预知的
            age=int(age)
            if age == AGE:
                print('you got it')
                break
    View Code

      2.如果错误发生的条件是不可以预知的,则需要用try.......except....处理,在错误发生之后进行处理

    #基本语法为
    try:
        被检测的代码块
    except 异常类型:
        try中一旦检测到异常,就执行这个位置的逻辑
    #举例
    try:
        f=open('a.txt')
        g=(line.strip() for line in f)
        print(next(g))
        print(next(g))
        print(next(g))
        print(next(g))
        print(next(g))
    except StopIteration:
        f.close()
    View Code

      try....except...详细用法:

    #1 异常类只能用来处理指定的异常情况,如果非指定异常则无法处理。
    s1 = 'hello'
    try:
        int(s1)
    except IndexError as e: # 未捕获到异常,程序直接报错
        print e
    
    #2 多分支
    s1 = 'hello'
    try:
        int(s1)
    except IndexError as e:
        print(e)
    except KeyError as e:
        print(e)
    except ValueError as e:
        print(e)
    
    #3 万能异常Exception
    s1 = 'hello'
    try:
        int(s1)
    except Exception as e:
        print(e)
    
    #4 多分支异常与万能异常
    #4.1 如果你想要的效果是,无论出现什么异常,我们统一丢弃,或者使用同一段代码逻辑去处理他们,那么骚年,大胆的去做吧,只有一个Exception就足够了。
    #4.2 如果你想要的效果是,对于不同的异常我们需要定制不同的处理逻辑,那就需要用到多分支了。
    
    #5 也可以在多分支后来一个Exception
    s1 = 'hello'
    try:
        int(s1)
    except IndexError as e:
        print(e)
    except KeyError as e:
        print(e)
    except ValueError as e:
        print(e)
    except Exception as e:
        print(e)
    
    #6 异常的其他机构
    s1 = 'hello'
    try:
        int(s1)
    except IndexError as e:
        print(e)
    except KeyError as e:
        print(e)
    except ValueError as e:
        print(e)
    #except Exception as e:
    #    print(e)
    else:
        print('try内代码块没有异常则执行我')
    finally:
        print('无论异常与否,都会执行该模块,通常是进行清理工作')
    
    #7 主动触发异常
    try:
        raise TypeError('类型错误')
    except Exception as e:
        print(e)
    
    #8 自定义异常
    class EgonException(BaseException):
        def __init__(self,msg):
            self.msg=msg
        def __str__(self):
            return self.msg
    
    try:
        raise EgonException('类型错误')
    except EgonException as e:
        print(e)
    
    #9 断言:assert 条件
    assert 1 == 1  
    assert 1 == 2
    
    #10 总结try..except
    
    1:把错误处理和真正的工作分开来
    2:代码更易组织,更清晰,复杂的工作任务更容易实现;
    3:毫无疑问,更安全了,不至于由于一些小的疏忽而使程序意外崩溃了;
    View Code
  • 相关阅读:
    combo,raido
    块级作用域问题ES5 & ES6
    基本类型和引用类型_深拷贝和浅拷贝
    由数组扁平化切入generator函数
    Promise基本用法
    原生ajax请求之前端
    ExtJS组件window
    ORACLE(+) 号的用法,及与逗号运算符的差别
    centos7发布json中文显示乱码
    centos7 svn在repository在的情况下重装恢复
  • 原文地址:https://www.cnblogs.com/Holmes-98/p/14433464.html
Copyright © 2020-2023  润新知