• Python全栈开发之9、面向对象、元类以及单例


      前面一系列博文讲解的都是面向过程的编程,如今是时候来一波面向对象的讲解了

    一、简介

      面向对象编程是一种编程方式,使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。类就是一个模板,模板里可以包含多个方法(函数),方法里实现各种各样的功能,,对象则是根据模板创建的实例,通过实例,对象可以执行类中的方法,每个对象都拥有相同的方法,但各自的数据可能不同。

    二、类、对象和方法

      在Python中,定义类是通过class关键字,class后面紧接着是类名,类名通常是大写开头的单词,紧接着是('要继承的类名'),表示该类是从哪个类继承下来的,通常如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类,也可以不写。

    class MyClass():             # 创建类
        def func(self):          # 定义方法
            pass
    
    obj1=MyClass()               # 根据MyClass创建对象
    

      类中定义方法的时候,和以前定义函数一样,都是用来实现某种功能的,不过定义方法的时候必须传至少一个参数(self),代表创建的对象,而函数则不需要,下面来看一下self具体指的什么。

    class MyClass():
        def func(self,str):
            print(self,str)
    
        def func1(self,str):
            pass
    obj1=MyClass()          # self 哪个对象调用的,self就是那个对象  obi <====> self
    print(obj1)             # <__main__.MyClass object at 0x000002C2119AD4A8>
    obj1.func('jason')      # <__main__.MyClass object at 0x000002C2119AD4A8>
    

    从上面打印出的数据可以看出,创建出来的obj1对象的地址值和func方法里面self的地址值一样,两者一致,说明self就是指代创建的对象,这里读者一定需要理解self指的是什么。

    三、面向对象三大特性,封装、继承和多态。  

    1、封装

      面向对象有3大特性,首先我们来说第一个特性,封装,封装一般是通过在类中封装数据,而通过对象或者self获取。和其他面向对象的语言类似,也是通过构造函数来进行数据封装。下面来看一下代码。 

    class A:
        def __init__(self,name):        # 构造函数,初始化数据,
            self.name=name              # 封装数据
    
        def f1(self):
            print(self.name)            # 通过self获取封装的数据
    
    a=A('jason')
    a.f1()                              #通过对象获取封装数据
    

    还有一种封装的方式,使用私用的属性来封装数据,看一下具体的用法,

    class A:
        name='Jason'
        __age=18                        # 私有静态字段,
        def __init__(self):
            self.__like='soccer'        # 私有普通字段
            self.hobby='kkkk'
    
        def f1(self):
            print(self.__age)         # 私有静态字段,私有普通字段只能被类中的方法调用
            print(self.__like)
    # A.__age                         # 外部获取不到私有静态字段,数据被封装起来
    a=A()                             # soccer
    a.f1()                            # 18
    print(a.hobby)
    

    2、继承  

      Python里面的继承可以多继承,通过继承,可以获得父类的功能,继承的时候,如果父类中有重复的方法,优先找自己,如果有下面关系,D继承B,E继承C,F继承D,E,则查找顺序,D->B->E->C,如果有下面关系,B继承A,C继承A,D继承B,E继承C,F继承D,E,则查找顺序,D->B->E->C-A,下面来看一下用法

    class A:
        def f(self):
            print('a')
    class B:
        def f(self):
            print('b')
    
        def f1(self):
            print('bbbb')
    
    class C(A,B):
        def f1(self):
            print('c')
    cc=C()
    cc.f()         # a
    cc.f1()        # c
    
    #下面的是难点重点
    class A:
        def bar(self):
            print('bar')
            self.f1()
    
    class B(A):
        def f1(self):
            print('b')
    
    class C():
        def f1(self):
            print('c')
    class D(B):
        def f1(self):
            print('d')
    
    class E(C,D):
        pass
    
    d=D()
    d.bar()             # d.bar().f1() 去哪里找f1?
    

    除了继承方法,还可以继承父类的构造函数  

    # 继承构造方法
    class A:
        def __init__(self):
            self.name='jason'
    
    class B(A):
        def __init__(self):
            self.age='16'
            super(B,self).__init__()
            # A.__init__(self)          #另一种继承构造函数的方法
    
    d=B()
    

    3、多态     

     python本身就是支持多态的,所以在Python面向对象里面讨论多态并没有什么意义,这里也就不多讲了

    四、类成员

      类的成员可以分为三大类:字段、方法和属性,下面从代码来看一下用法

    class A:
    
        country = 'China'               # 静态字段,保存在类中 节省内存
    
        def __init__(self, name):
            self.name = name            # 普通字段,保存在对象中
    
        def show(self):                 # 普通方法,保存在类中,对象通过类对象指针使用
            print('普通方法')
            return self.name
    
        @staticmethod                   # 静态方法,保存在类中
        def f1():                       # 没有 self 参数
            print('静态方法')
    
        @classmethod                    # 类方法 相当于静态方法加了一个类名参数
        def f2(cls):                    # 将当前类名当作参数传进去
            print('类方法')
    
        @property                       # 属性或者叫特性,将普通方法伪装成字段,但是不能传入第二个参数
        def f3(self):
            print('属性')
            return self.name
    
        @f3.setter                      # 设置属性
        def f3(self,value):
            print(value)
            self.name= value
    
    a = A('jason')
    print(hasattr(A, 'name'))  # False
    print(hasattr(a, 'name'))  # True
    print(hasattr(a, 'show'))  # True
    print(hasattr(A, 'country'))  # True
    print(hasattr(a, 'country'))  # True
    
    # 优先,自己先去访问自己的成员除了类中的方法
    
    # 用下面的方式去操作
    #      通过类去访问的有:静态字段,静态方法,类方法(静态方法加一个类名参数)
    #      通过对象访问的有:普通字段,普通方法
    # 有 self,对象调用
    # 无 self,类调用
    
    A.f1()                             # 静态方法
    a.show()                           # 普通方法
    A.f2()                             # 类方法
    a.f3                               # 属性,获取属性值 执行的时候不用加括号,和对象调用普通字段类似
    a.f3='kobe'                        # 设置属性值,
    
    print(A.country)
    print(a.name)
    
    # 私有的只能在类中调用,不能再外部调用
    class A:
        name='Jason'
        __age=18                        # 私有静态字段
        def __init__(self):
            self.__like='soccer'        # 私有普通字段
            self.hobby='kkkk'
    
        def f1(self):
            print(self.__age)         # 私有静态字段,私有普通字段只能被类中的方法调用
            print(self.__like)
    # A.__age                         # 外部获取不到私有静态字段
    a=A()                             # soccer
    a.f1()          
    

    特殊成员,也不多说了,直接上代码了

    class A:
        def __init__(self):
            print('init')
            self.name='jason'
    
        def __str__(self):
            return '........'
    
        def __call__(self, *args, **kwargs):
            print('call')
            return 1
    
        def __getitem__(self, item):
            print(item,type(item))
    
        def __setitem__(self, key, value):
            print(key,value)
    
        def __delitem__(self, key):
            print(key)
    
        def __iter__(self):
            yield 1
            yield 2
    
    
    a=A()             # init             类后面加个括号执行      __init__
    a()               # call             对象后面加个括号执行    __call__
    print(a)          # 执行 __str__方法
    k=A()()           # init call k=1
    
    a['dddd']                   # 执行 __getitem__                <class 'str'>
    a['name']='jason'           # 执行 __setitem__
    del a['name']               # 执行 __delitem__
    
    
    a[1:3]                   # py2__getslice__,py3执行 __getitem__  <class 'slice'>
    a[1:3]=[1,2]             # py2__setslice__,py3执行 __setitem__
    del a[1:3]               # py2__detslice__,py3执行 __delitem__
    
    print(a.__dict__)       # {'name': 'jason'} 对象里面的字段
    print(A.__dict__)       # 类里面的字段
    
    for i in a:             # 执行__iter__
        print(i)
    

    五、元类  

      在Python中一切事物都是对象,类也是对象,那么问题来了,类是如何创建的了,类的定义是运行时动态创建的,而创建class的方法就是使用type()函数,通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。type可以接受一个类的描述作为参数,然后返回一个类可以像这样使用:type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

    # 通过type自定义类
    def f1(self):
        print(self.name)
    
    MyClass=type('MyClass',(),{'name':'jason','f1':f1})
    print(MyClass,type(MyClass))                  # <class '__main__.MyClass'> ,<class 'type'>
    print(MyClass.name)                           # jason
    
    my=MyClass()
    print(my.__class__)                           # <class '__main__.MyClass'>
    print(my)                                     # <__main__.MyClass object at 0x000001F3013751D0>
    my.f1()                                       # jason
    

    说完了type,那么type和元类到底是什么关系了,type实际上是一个元类。type就是Python在背后用来创建所有类的元类,除了使用type()动态创建类以外,要控制类的创建行为就要用到metaclass(元类),我们想创建出类,那就根据metaclass创建出类,所以:先定义metaclass,然后创建类。连接起来就是:先定义metaclass,就可以创建类,最后创建实例。元类的一般作用是拦截类的创建,修改类, 返回修改之后的类。下面看一下代码的使用

    # 用函数实现
    # def upper_attr(future_class_name, future_class_parents, future_class_attrs):
    #     attrs=((name, value) for name,value in future_class_attrs.items() if not name.startswith('__'))
    #     uppercase_attr=dict((name.upper(), value) for name,value in attrs)
    #     return type(future_class_name,future_class_parents,uppercase_attr())
    #
    # class A():
    #     # __metaclass__ = upper_attr
    #     country="China"
    #     def __init__(self):
    #         self.name='jason'
    #
    # print(hasattr(A,'country'))
    # print(hasattr(A,'COUNTRY'))
    
    '''
    __new__()方法接收到的参数依次是:
        当前准备创建的类的对象;
        类的名字;
        类继承的父类集合;
        类的方法集合。
    
    __new__ 是在__init__之前被调用的特殊方法
    __new__是用来创建对象并返回之的方法
    而__init__只是用来将传入的参数初始化给对象
    你很少用到__new__,除非你希望能够控制对象的创建
    这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
    如果你希望的话,你也可以在__init__中做些事情
    还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
    
    '''
    
    class UppMetaclass(type):
        def __new__(cls, name,base,attrs):
            uppattrs = dict((k.upper(),v)for k,v in attrs.items() if not k.startswith('__'))
            # return type.__new__(cls, name, base, uppattrs)
            return super(UppMetaclass, cls).__new__(cls, name, base, uppattrs)
    
    class A(metaclass=UppMetaclass):
        # __metaclass__ = UppMetaclass
        country="China"
        def __init__(self):
            self.name='jason'
    
    print(hasattr(A,'country'))
    print(hasattr(A,'COUNTRY'))
    print(A.COUNTRY)
    

    六、单例模式 

     单例,顾名思义单个实例,单利模式存在的目的是保证当前内存中仅存在单个实例,避免内存浪费,下面直接看一下怎么实现单例

    class Instance:                    # 方法一
    
        __instance=None                               # 私有静态字段
    
        def __init__(self):
            self.name='jason'
            self.passwd='kkkkk'
    
        @staticmethod
        def get_instance():
            if not Instance.__instance:              # 如果私有静态字段为空的话,创建一个实例
                Instance.__instance = Instance()
            return Instance.__instance               # 不为空的话,直接返回一个实例
    
    obj1=Instance.get_instance()             # 执行静态方法,创建一个实例赋值给obj1
    obj2=Instance.get_instance()             # 执行静态方法,将私有静态字段保存的对象赋值给obj2
    print(obj1)                              # <__main__.Instance object at 0x0000021AEDF56048>
    print(obj2)                              # <__main__.Instance object at 0x0000021AEDF56048>
    
    class Singleton():              # 方法二
        def __new__(cls, *args, **kwargs):
            if not hasattr(cls,'_instance'):
                cls._instance=super(Singleton,cls).__new__(cls,*args, **kwargs)
            return cls._instance
    
    class A(Singleton):
        pass
    a=A()
    b=A()
    print(a is b)
    
    class Singleton(type):                # 方法三
        def __call__(cls, *args, **kwargs):
            if not hasattr(cls, '_instance'):
                cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
            return cls._instance
    
    
    class A(metaclass=Singleton):    
        # __metaclass__ = Singleton
        pass
    a = A()
    b = A()
    print(a is b)
    

    七、补充 

    属性的另一种表达方式:

    class A:
        def __init__(self):
            self.mylist = [1, 2, 3, 4]
    
        def getmylist(self):
            print(self.mylist)
    
        def setmylist(self, value):
            self.mylist.append(value)
            print(self.mylist)
        def delmylist(self):
            if len(self.mylist) > 0:
                self.mylist.pop()
            print(self.mylist)
        my_pro_list = property(getmylist, setmylist, delmylist, 'Property')
    
    a = A()
    a.my_pro_list
    a.my_pro_list = 7
    del a.my_pro_list
    
    '''
        [1, 2, 3, 4]
        [1, 2, 3, 4, 7]
        [1, 2, 3, 4]
    '''
    

    单利补充:

    class SingMetaclass(type):
        def __call__(self, *args, **kwargs):
            if not hasattr(self, 'instance'):
                # self.instance = super(SingMetaclass, cls).__new__(cls, name, base, attrs)
                # self.instance = self.__new__(self, *args, **kwargs)   # 下面这种方式也行,但是没有注释掉的好理解
                self.instance = super().__call__(*args, **kwargs)
            return self.instance
    
    class A(metaclass=SingMetaclass):
        pass
    

    py27继承顺序,有一种情况和py3不一样,如下图,其中bar没有继承object(经典类)、深度优先,其他情况一样,都是广度优先

      

    # py27
    
    class bar():
        def f1(self):
            print('bar')
    
    class A(bar):
        def f(self):
            print('a')
    
    class C(A):
        def f(self):
            print('c')
    
    class B(bar):
        def f1(self):
            print('b')
    
    class D(B):
        def f1(self):
            print('d')
    
    class E(C,D):
        def f(self):
            print('e')
    
    e = E()
    e.f1()
    

    抽象类和抽象方法:

    from abc import ABCMeta
    
    from abc import abstractmethod
    
    
    class Absclass(metaclass=ABCMeta):      # 抽象类+抽象方法,类似接口,约束类
        @abstractmethod
        def f1(self): pass
    
        @abstractmethod
        def f2(self): pass
    
    
    class B(Absclass):        # B 需要完全实现抽象类中的抽象方法
        def f1(self): pass
    
        def f2(self): pass          # 如果 B 类中没有完全实现 Absclass 类中的方法话,可以编译过,但是执行的话会报错
    
        def f3(self): pass
    
    
    c = B()
    

      

      

      

  • 相关阅读:
    Spring3+hibernate4+struts2整合的 过程中发生如下错误
    使用HQL语句的按照参数名字查询数据库信息的时候 “=:”和参数之间不能存在空格,否则会报错
    org.hibernate.service.classloading.spi.ClassLoadingException: Specified JDBC Driver com.mysql.jdbc.Driver class not found
    Java多线程编程:
    数据库连接池的工作原理
    Oracle数据库表的备份和数据表的删除操作
    数据库连接池
    Mysql登录异常的一个问题:
    2019年终总结
    设计模式入门-简单工厂模式
  • 原文地址:https://www.cnblogs.com/Wxtrkbc/p/5550673.html
Copyright © 2020-2023  润新知