一、引入


 

1.1 概述

  python支持函数式编程也支持面向对象编程

1.2 什么是面向对象?

  下面以一个例子来说明面向对象的问题

class Bar:--函数变为对象
            def foo(self,name,age,gender,content):
                print(name,age,gender,content)            
        obj=Bar()  
        obj.foo(小明,10岁,男,上山去砍柴)

  面向对象定义:

    class class_name:

      def def_name(self,arg):

        print(arg)

        return 1

    中间人=类名()    ret=中间人.方法名()       print(ret)为1,谁调用这个函数这个值变返回给谁 

  由以上可知想执行函数要通过类对象间接访问 

    原函数执行:函数名(参数)

    现函数执行:面向对象:要间接访问里面的内容 o=Bar() ----对象,实例,o.foo()

    对比函数与对象:创建一个类,加载时在内存里有一个类的内存,在这个内存里再分个内存做方法,在另一个内存里创建一个中间人指向这个类内存,

            它只能找到这个类里的方法

class Bar:
            def foo(self,arg):
                print(self,arg)
            z1 = Bar()//z1是指向Bar这个类的地址
            print(z1)//是一个对象的内存地址
            z1.foo(111)---self就是z1的内存地址
            z2 = Bar()---也会传给self,不同的对象
            
        中间人对象:中间人内部也能放东西,引用其地址加变量等
              class Bar:
                  def foo(self,arg):
                      print(self,self.name,self.age,arg)
              z=Bar()---对象,在对象里添加值如下
              z.name='alex'---注这里中间人里加东西不是中间人调用类里面的方法
              z.age=18
              print(z.foo(111))----中间人调用方法,同时方法也能取中间人放在里面的东西并打印,二者是相互的
        上个实例:--封装
            class Bar:
                def add(self,content):
                    print(self.name,self.age,content)---可以拿不同对象里的值
                def delete(self,content):
                    print(self.name,self.age,content)   
                def update(self,content):
                    print(self.name,self.age,content)
            obj=Bar()
            obj.name='小明'
            obj.age=10
            obj.add(上山去砍柴)
            obj.delete(开车去东北)------这时相同的值就不用改了,被装在了中间人里,只会那边会变的就行,不用相同的值重复打
        构造方法:
            特殊作用:
               在 obj=类名()----做了二个事情:第一:创建对象  第二:通过对象执行类中的一个特殊方法,特殊方法用_init_(self)来定义的函数
           class Bar:
                def _init_(self):
                    print('123')
                def add(self,content):
                    print(self.name,self.age,content)
           z=Bar()
           print(z)---会打印二个一个是内存地址一个是123,也可以在_init_函数里加参数,做初始化
                def _init_(self,name,age):-----这个方法就叫构造方法                    
                    self.n=name
                    self.a=age
                    self.b=1                    
                def add(self,content):
                    print(self.n,content)                    
                z=Bar('alex',18)---除了给他值alex与18还有一个默认值1,类里的参数传给构造函数初始化数据
                z.add('home')

 

二、面向对象三大特性


  类里的方法创建如下:

    构造方法,__init__(self,arg)

      obj = 类('a1')----字段

    普通方法

      obj = 类(‘xxx’)

      obj.普通方法名()    

2.1 面向对象三大特性之一:封装

class Bar:
            def __init__(self, n,a):
            self.name = n
            self.age = a
            self.xue = 'o'
    
            b1 = Bar('alex', 123)

            b2 = Bar('eric', 456)

  适用场景:如果多个函数中有一些相同参数时,转换成面向对象,封装到_init_

class DataBaseHelper:

        def __init__(self, ip, port, username, pwd):--连数据库需要的
            self.ip = ip
            self.port = port
            self.username = username
            self.pwd = pwd

        def add(self,content):
            # 利用self中封装的用户名、密码等   链接数据
            print('content')
            # 关闭数据链接

        def delete(self,content):
            # 利用self中封装的用户名、密码等   链接数据
            print('content')
            # 关闭数据链接

        def update(self,content):
            # 利用self中封装的用户名、密码等   链接数据
            print('content')
            # 关闭数据链接
    
        def get(self,content):
            # 利用self中封装的用户名、密码等   链接数据
            print('content')
            # 关闭数据链接

            s1 = DataBaseHelper('1.1.1.1',3306, 'alex', 'sb')

 

2.2 面向对象三大特性之二:继承

  1. 继承:可多层继承

class 父类:
            pass
        
        class 子类(父类):
            pass
        例:class father:----父类,基类
                def 篮球(self):
                   pass
            class son(father):---son就是子类,也叫派生类,这时父类的所有def子类都可以执行
                def 抽烟(self):
                   pass

  2.重写:如何重写父类的方法?

    父类里的某些方法,子类自己定义一个与其名字一样的,那只能执行子类的

  3.self永远是执行改方法的调用者

  4.如何子类与父类的二个相同方法都要执行?

class father:----父类,基类
            def 篮球(self):
               pass

        class son(father):---son就是子类,也叫派生类,这时父类的所有def子类都可以执行
            def 抽烟(self):
               pass    
            def 篮球(self):
               super(son,self).篮球()---也可以是父类的其它方法,子类执行父类的方法
               pass                       
        法1:super(子类, self).父类中的方法(...)
        法2:父类名.父类中的方法(self,...)

  5.python支持多继承 

a. 左侧优先
           class son(father,father1):------如果二个都是一个方法刚左侧优先,里面都是有方法f1
           
           obj=son()
           obj.f1()---则执行左边
        b. 一条道走到黑
           如上father的f1是继承它的父类的就一条道走到黑
        c. 同一个根时,根最后执行
           二条路最后都指向同一个父类,先左边走到了倒数第二个就走第二条路,最后执行根

  6.应用:新做的功能想使用开源的方法就可以使用继承

开放的:
                class father:
                    def 篮球(self):
                       pass
           新的功能:
                class son(father):
                     def 其它(self):
                       pass

 

2.3 面向对象三大特性之三:多态

# Java:必须要指定变量类型
            string v = 'alex'
        
            def func(string arg):---这个变量必须指定类型
                print(arg)
            
            func('alex')
            func(123)
    
        # Python :是多态的不用指定
            v = 'alex'
        
            def func(arg):
                print(arg)
                
            
            func(1)
            func('alex')

 

三、面向对象的高级用法


 

3.1 类成员分类

类成员:
        # 字段
            - 普通字段,保存在对象中,执行只能通过对象访问
            - 静态字段,保存在类中,执行可以通过对象访问 也可以通过类访问,在方法外类内定义的字段

        # 方法
            - 普通方法,保存在类中,由对象来调用,self=》对象
               例:
                  class Foo:
                     def bar(self):
                      print('bar')
                  obj = Foo()---先找到self
                  obj.bar()----对象调用
                  
                  Foo.bar(obj)----对象当参数调用,这些都是普通方法                
            - 静态方法,保存在类中,由类直接调用
               例:
                  class Foo:
                     def bar(self):----普通方法
                      print('bar')
                     @staticmethod---它下面的方法就是静态方法了,就不用传对象了,可以直接类调用就行
                     def str():
                       print('123')
                Foo.str()--就用调用str方法了,就是类自己的所有固定值了而不在是对象的特有的值了,所有对象都有共同的值   
               
            -   类方法,保存在类中,由类直接调用,cls=》当前类,类直接调的是类,函数的参数是类
              例:
              class Foo:
                    def bar(self):----普通方法
                      print('bar')
                    @classmethod
                    def classmd(cls):---cls是类名不是对象
                        print('classmd')
              Foo.classmd()---加载的是类

         应用场景:
            普通方法:如果对象中需要保存一些值,执行某功能时,需要使用对象中的值
            静态方法:不需要任何对象中的值

 

3.2 方法的其它属性装饰器使用

@property   //一个装饰器,叫属性,作用是把普通方法的调用变为字段的调用             
            用于执行obj.per,如下
            class Foo:
              @property
              def per(self):
                  return 1
              obj=Foo()
              r=obj.per----这个对象调用不带(),相当于字段的调用,想赋值怎么办
        @per.setter  //这些方法要自己实现
            用于执行obj.per=123赋值,用于赋值
            class Foo:
              def per(self,val):
                  print(val)
               obj=Foo()
               obj.per=123
        @per.deleter
            用于删除:---函数做什么自己写不是真的用于删除什么的               
               def per(self):
                   print(666)
                obj=Foo()
                del obj.per
            所以不同的调用格式就执行不同的对应的函数

  以上功能的作用:

比如分页:
           li = []---空列表
           for i in range(100):
               li.append(i)---列表加100个值
           while True:
               p = input('请输入要查看的页码:')
               p = int(p)--字符变为数字
               start = (p-1)*10
               end = p*10
               print(li[start:end])
        升级把start end做为一个组件
             class Pergination:
                 def _init_(self,current_page)
                     try:--异常处理
                        p=int(current_page)
                     except Exception as e:
                       p=1
                     
                     self.page=int(current_page)
                @property
                def start(self):
                    val = (self.page-1)*10
                    return val
                @property
                def end(self):
                  val=self.page*10
                  return val
            这样再调用时print(li[obj.start:obj.end])

 

四、 类的其它知识点


4.1 类成员修饰符

  字段的权限:  

    1.公有成员

    2.私有成员:外部不能直接访问

普通字段私有化
            class Foo:
                def __init__(self,name,age):
                    self.name = name
                    self.age = age
                    self.__age = age加上二个下划线,这个就访问不了了,但能在内部访问
                def show(self):
                    return self.__age----这时就可以访问的到了

            obj = Foo()
            obj.name
            #obj.age
            obj.__age---这个就访问不了,这个就是私有成员
        静态字段私有化
            class Foo:
                __v='123'
                def __init__(self):
                    pass
                def show(self):
                    return Foo.__v
            ret=Foo().show()
        普通方法的公私有
            class Foo:
                def __f1(self):
                    return 123

                def f2(self):    
                    r = self.__f1()
                    return r

            obj = Foo()
            ret = obj.f2()
            print(ret)

 

4.2 特殊成员认识

  1. __call__:对象后加()是执行特殊成员__call__的=Foo()()

 

class Foo:
            def __init__(self):
                print('init')

            def __call__(self, *args, **kwargs):
                print('call')

        # obj = Foo()
        # obj()-----对象后加()是执行特殊成员__call__的=Foo()()

 

  2.数据类型相互转化

    __int__    __str__

  

# s = "123"-----转为字符型相当于 s = str('123')

        # i = int(s)----字符转为数值
        # print(i,type(i))
        
        那函数等能转化吗?
           class Foo:

                def __init__(self)://构造方法
                    pass

                def __int__(self):
                    return 1111

                def __str__(self):
                    return 'alex'

          obj = Foo()
          print(obj, type(obj))

         # int,对象,自动执行对象的 __int__方法,并将返回值赋值给int对象
            r = int(obj)---int里加的对象会自动运行_int_方法,对象进行类型转化,这时对象的值不为地址了而是str方法定义的值
            print(r)
            i = str(obj)---str里加的对象会自动运行_str_方法,对象进行类型转化,这时对象的值不为地址了而是str方法定义的值
            print(i)-----直接返回的是类对象的值不是地址了

  3. 对象相加要执行的特殊方法

class Foo:

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

            def __add__(self, other):
                # self = obj1      封装了(alex,19)
                # other = obj2     封装了(eric,66)
                # return self.age + other.age----定义r返回的结果
                #return Foo('tt',99)
                return Foo(obj1.name, other.age)

            def __del__(self):
                print('析构方法') # 对象被销毁()时,自动执行

        obj1 = Foo('alex', 19)
        obj2 = Foo('eirc', 66)

        r = obj1 + obj2
        # 两个对象相加时,自动执行第一个对象的的 __add__方法,并且将第二个对象当作参数传递进入
        print(r,type(r))

  4.析构方法 ---对象被销毁()时,自动执行

    def __del__(self):---析构方法

  5. *对象转为字典的形式

__dict__:# 将对象中封装的所有内容通过字典的形式返回,而非一个地址
                class Foo:
                    def __init__(self, name,age):
                        self.name = name
                        self.age = age
                        self.n = 123

                # obj = Foo('alex', 18)

                # d = obj.__dict__----对象的成员通过字典显示出来
                # print(d)
                 ['name':alex,'age':18]

                # ret = Foo.__dict__----类成员通过字典显示出来
                # print(ret)

 

  6. *索引 __getitem__ __setitem__   __delitem__

# li = [11,22,33,44]
        # li = list([11,22,33,44])

        # li[3]----调用展示的方法

        # li[3] = 666---调用修改的方法

        # del li[2]----删除方法
        class Foo:

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

            def __getitem__(self, item):
                # return item+10
                # 如果item是基本类型:int,str,索引获取
                # slice对象的话,切片
                if type(item) == slice:
                    print('调用这希望内部做切片处理')
                else:
                    print(item.start)
                    print(item.stop)
                    print(item.step)
                    print('调用这希望内部做索引处理')

            def __setitem__(self, key, value):
                print(key,value)

            def __delitem__(self, key):
                print(key)
        li = Foo('alex', 18)
        r= li[8] # 自动执行li对象的类中的 __getitem__方法,8当作参数传递给item,后加个[]
        print(r)
        r=li[1:2:3]---切片,也用的是__getitem__方法,所以要在方法里判断,在python3里是这样的
        li[100] = "asdf"--自动执行li对象的类中的 __setitem__方法,key是100,asdf是value

        del li[999]---自动执行li对象的类中的 __delitem__方法

  7. __iter__:变为可迭代对象

class Foo:

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

            def __iter__(self):
                return iter([11,22,33])
        li = Foo('alex', 18)//这虽然是个地址,但它里面包含的有数据,只是返回的值地址,类方法可定义对象返回什么样的值
        for i in li:------这个调用就会执行__iter__的方法,对象循环,是可迭代对象,这个是类里有这个方法时才能用的
            print(i)
            
            # 如果类中有 __iter__ 方法,对象=》可迭代对象
            # 对象.__iter__() 的返回值: 迭代器
            # for 循环,若后是迭代器,直接执行next,一个一个的拿
            # for 循环,若后是可迭代对象,先调用对象.__iter__()变为迭代器后再进行next
            # 1、执行li对象的类F类中的 __iter__方法,并获取其返回值
            # 2、循环上一步中返回的对象

 

4.3 metaclass:类的祖宗

  1.Python中一切事物都是对象

  2. 类也是对象

class Foo:
            pass


        obj = Foo()
        # obj是对象,Foo类
        # Foo类也是一个对象,它是type的对象


       例:
          class Foo:
              def func(self):
                  print(12)
          以上这个代码解释器拿到后会进行如下处理:
          def funtion(self):
              print(12)
          Foo=type('Foo',(object,),{'func':funtion})----object:超级类所有的类都继承这个类。声明了一个类

  3. 类都是type类的对象   type(..)

    “对象”都是以类的对象 类()

问题:type创建类这个对象时执行的是__init__,这个是由C实现的,所以看不了,如果类不想用type创建如何做?
            class MyType(type):----MyType做为type的子类,所以先用MyType的__init__
                def __init__(self,*args, **kwargs):
                    # self=Foo,这时还没有obj所以它是Foo
                    print(123)
                    pass

                def __call__(self, *args, **kwargs):
                    # self=Foo
                    obj = self.__new__()
                    self.__init__----Foo.__init__

        python2:class Foo(object):
                __metaclass__ =MyType
   
        python3:class Foo(object,metaclass=MyType):------定义Foo用MyType这个类来创建不再用type了
                def __init__(self):
                    pass

                def __new__(cls, *args, **kwargs):
                    return '对象'----这个对象就给r了,它是obj

                def func(self):
                    print('hello wupeiqi')
            1.结果是123
            2.obj = Foo()----执行MyType的__call__方法,这里会调用__new__--在这里创建生成的obj

4.4 异常处理

  1. 捕捉异常:try...except Exception as e:

try: 
             #代码块,逻辑
             inp=input("请输入序号:")----输入数字就走这个输入是字母就走下一个,都不会报错
             i=int("inp")----出现错误后try下面的代码不会执行直接跳到下面的选择里
             
          except Exception as e:
             #如果try里面出错就会创建一个Exception对象,对象中封装错误信息
             #上述代码块如果出错,自动执行当前块的内容,这样代码就不会报错了
             print(e)---打应错误信息
             i = 1
             print(i)
          注:Exception是所有错误信息都能扑捉封装
              IndexError:扑捉的是索引错误
              ValueError:扑捉的是值的错误
              IOError:IO错误
         else:
             print(12)---不出错就执行
         finally:
            print(34)---出不出错都执行
            try:
                li=[11,22]
                li[999]----这个报错IndexError能扑到
                int(se)----这个报错就扑不到程序就会报错
            except IndexError as e:
                print(e)

  2.主动抛出异常

try:
            # int('asdf')
            # 主动出发异常
            # raise Exception('不过了...')--这是关键字段,这步自己报错,下面来捕捉,Exception报的是报错内容,这是个函数,可以自己定义函数
        except Exception as e:
            print(e)
            
        例:
            def db():
                # return True
                return False

            def index():
                try:
                    r = input(">>")
                    int(r)
                    result = db()
                    if not result:
                        r = open('log','a')
                        r.write('数据库处理错误')
                        # 打开文件,记录日志
                        #raise Exception('数据库处理错误')
                    except Exception as e:
                        str_error = str(e)
                        print(str_error)
                        r = open('log', 'a')
                        r.write(str_error)
                        # 打开文件,记录日志
            index()

  3.自定义异常类,可自调用使用

class OldBoyError(Exception):

            def __init__(self, msg):
                self.message = msg

            def __str__(self)://类生成对象返回的结果 
                return self.message

          # obj = OldBoyError('xxx')
          # print(obj)
          try:
              raise OldBoyError('我错了...')---自定义函数报错
          except OldBoyError as e:
          
          print(e)# e对象的__str__()方法,获取返回

  4.断言

    # assert 条件,断言,用于强制用户服从,不服从就报错,可扑捉,一般不扑捉

    # assert 1==2------必须成立程序才能执行,否则一条都不会执行

    # print(456)

 

4.5 反射

  1.通过字符串的形式操作对象中的成员(用特定方法拿类或函数里的成员不用调用)

class Foo:
            def __init__(self, name,age):
            self.name = name
            self.age = age

            def show(self):
                return  "%s-%s " %(self.name,self.age)
            def __int__(self):
                return 123
            def __str__(self):
                return 'uuu'
        obj = Foo('alex', 18)---对象

        r = int(obj) # 这个对象的数值是r = 123
        u = str(obj)
        b = 'name'
        obj.__dict__['name']=obj.__dict__[b]-----拿出alex,这种方法比较麻烦为减化封闭一个方法如下
        
        更好的写法:去什么东西里面获取什么内容?
             getatter(obj,'name')

        总写法:反射,通过字符串的形式操作对象中的成员
        # getattr---去什么东西里面获取什么内容,从对象里获取某字段,从某模块里获取某函数等
        # hasattr---检测对象里是否有某成员
        # setattr---修改对象里某成员的值
        # delattr---删除对象里的某值
        # 通过字符串的形式操作对象中的成员

        # func = getattr(obj, 'show')
        # print(func)
        # r = func()
        # print(r)

        # print(hasattr(obj, 'name'))
        # obj.k1
        # setattr(obj, 'k1', 'v1')
        # print(obj.k1)
        # obj.name
        # delattr(obj, 'name')---删除对象某值
        # obj.name

        # 去什么东西里面获取什么内容
        # inp = input('>>>')----只能输入name  age
        # v = getattr(obj, inp)
        # print(v)----输入name得到的是alex的结果

  2.实例

class Foo:

            stat = '123'

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

        # 通过字符串的形式操作对象中的成员
        r = getattr(Foo, 'stat')
        print(r)
        
        操作模块也可以
            import s2


            # r1 = s2.NAME
            # print(r1)
            # r2 = s2.func()
            # print(r2)

            r1 = getattr(s2, 'NAME')----NAME是变量
            print(r1)

            r2 = getattr(s2, 'func')---拿到func的地址,func是函数
            result = r2()
            print(result)

            cls = getattr(s2, 'Foo')--Foo是类
            print(cls)
            obj = cls()
            print(obj)
            print(obj.name)
            
        一个网页,不同的按钮进入不同的网页,URL也不同,若第一个按钮对应模块s2.py 第二个对应s3.py    
            在s1中写入
            import s2
            import s3
            inp = input('请输入要查看的URL:')
            if hasattr(s2, inp):---是否有这个值
                func = getattr(s2, inp)得到s2的inp的值
                result = func()
                print(result)
            else:
                print('404')

 

4.6 单例模式:用于使用同一份实例

class Foo:
        #    def __init__(self, name,age):
        #        self.name = name
        #        self.age = age

        # obj = Foo() # obj对象,obj也成为Foo类的 实例,(实例化)
        # obj1 = Foo()
        # obj2 = Foo()
        # obj3 = Foo()
        这个例子跟单例有什么关系,这上面是四个实例,用到四个内存
        
        单例,用于使用同一份实例(对象)
            class Foo:
                def __init__(self, name,age):
                    self.name = name
                    self.age = age

                def show(self):
                    print(self.name,self.age)

            v = None

            while True:
                if v:
                    v.show()----只用这一个单例,只用一个实例
                else:
                    v = Foo('alex', 123)
                    v.show()
        改良
            class Foo:

                __v = None

                @classmethod
                def get_instance(cls):---这个函数里的类
                    if cls.__v:----类时的私有静态字段
                        return cls.__v
                    else:
                        cls.__v = Foo()
                        return cls.__v

            # 不要在使用 类()来创建一个对象
            obj1 = Foo.get_instance()----创建对象,用类方法创建对象
            print(obj1)
            obj2 = Foo.get_instance()
            print(obj2)
            obj3 = Foo.get_instance()
            print(obj3)