面向对象之元类

1.isinstance方法和issubclass方法

  isinstance():判断某个对象是不是某个类的实例

class Person:
    pass
class Student(Person):
    pass

stu = Student()

print(isinstance(stu,Student))
print(isinstance(stu,Person))

>>>:
            True
            True


print(isinstance('11',str))

>>>:
            True

  issubclass():判断一个类是不是另一个类的子类

class A:
    pass
class Person(A):
    pass
class Student(Person):
    pass

stu = Student()

# 判断一个类是不是 另一个类子类 (所有类都是object的子类或子子类)
print(issubclass(Student,Person))
print(issubclass(Student,A))
print(issubclass(Student,object))

>>>:
        True
        True
        True
    

2.反射

  2.1反射的定义:

    在Python中反射其实是反省的意思,即对象要具备一种修正错误的能力。

    反射总共分为4类:1.hasattr    是否存在某个属性/方法

              2.getattr  获取某个属性的值/方法的内存地址

              3.setattr     设置某个属性的值

              4.delattr     删除某个属性

     这4类方法都有一个特点:都是通过字符串的形式来操作对象的属性值/方法。根据这个特点我们也可以理解为通过字符串来操作对象属性/方法(属性/方法的增删改查)就是反省即反射。

  2.2反射4类方法的具体用法

class Student:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def study(self):
        print("学生正在学习...")

stu=Student("杨过",22,'')
print(hasattr(stu,'name')) #True
print(hasattr(stu,'study')) #True
print(hasattr(stu,'score'))  #False
hasattr
class Student:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def study(self):
        print("学生正在学习...")

stu=Student("杨过",22,'')
#getattr方法的第三个参数表示没有找到属性/方法时,返回第三个参数中的内容
print(getattr(stu,'name','对象中没有这个属性')) # 杨过
print(getattr(stu,'study','对象中没有这个方法')) # <bound method Student.study of <__main__.Student object at 0x000001ED41793048>>
print(getattr(stu,'score','对象中没有这个属性'))  # 对象中没有这个属性
getattr
class Student:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def study(self):
        print("学生正在学习...")

stu=Student("杨过",22,'')
setattr(stu,'name','郭靖')
print(stu.__dict__)  # {'name': '郭靖', 'age': 22, 'sex': '男'}
setattr(stu,'score',0)
print(stu.__dict__)  # {'name': '郭靖', 'age': 22, 'sex': '男', 'score': 0}
#setattr 对存在的属性进行修改,对不存在的属性进行添加
setattr
class Student:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def study(self):
        print("学生正在学习...")

stu=Student("杨过",22,'')
delattr(stu,'name')
print(stu.__dict__)  # {'age': 22, 'sex': '男'}
delattr

3.__str__方法

  在学习__str__方法前,我们都需要了解一个规则:在类中前后带__的函数都是特殊的内置函数,会在某些时机自动执行,一般情况下我们不应该直接调用他们。

  __str__方法的作用:当我们输出对象时,该对象的类自动执行__str__方法,以自定义的数据格式代替原来的对象内存地址。

class Student:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def __str__(self):
        return "<%s  %s  %s>"%(self.name,self.age,self.sex) #此处的输出格式可以自定义


stu1=Student("杨过",18,"")
print(stu1)

>>>:
<杨过  18  男>
__str__方法的用法

4.__del__方法

  __del__方法也被称为析构函数:分析构造,并拆除这个对象

  4.1__del__方法的运行时间:1.当对象从内存中被删除时即程序即将结束运行时,会运行自动执行__del__方法。

                    2.当程序员手动删除了一个对象时,会自动执行__del__方法。

               当然,以上两个执行过程的前提是,对象的类中有__del__这个方法。

  4.2在什么需求下会使用__del__方法?

    当我们程序结束时,需要做一些清理,对一些已经没有用处但是还是运行的资源进行回收,那么可以用到__del__这个方法。

import time
class Student:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def __del__(self):
        print("__del__执行了")

stu=Student("杨过",18,"")
time.sleep(3)  
#程序睡眠3秒后输出__del__执行了即程序即将结束时,自动执行__del__。


import time
class Student:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def __del__(self):
        print("__del__执行了")

stu=Student("杨过",18,"")
del stu
time.sleep(3)
#一旦运行到del stu时便输出__del__执行了 即删除对象时,自动执行__del__方法
__del__方法的执行时间
import time
class Student:
    def __init__(self,file_path,mode='rt+',encoding='utf-8'):
        self.file=open(file_path,mode=mode,encoding=encoding)
    def read(self):
        return self.file.read()
    def write(self,text):
        return self.file.write(text)
    def __del__(self):
        # 在这里关闭系统的文件
        self.file.close()

stu=Student("1.txt")
print(stu.read())
使用__del__回收资源

5.exec方法

  5.1 定义

      execute的缩写,是执行的意思。

  5.2 exec的作用

      解析字符串形式的Python代码,并且将代码中的名称存储到特定的名称空间中,Python解释器的内部也是使用这种方式来执行代码的。

  5.3 exec的参数

      参数一:字符串对象,字符串中时Python代码

      参数二:一个字典,表示全局的名称空间

      参数三:一个字典,表示局部的名称空间

glocal_dic={}
local_dic={}
exec("""
a=1
b=2
def func():
    print("func run")
""",glocal_dic)
print(glocal_dic)

#如果只传了一个传参数 则 将字符串中包含名称 解析后存到全局中
两个参数
glocal_dic={}
local_dic={}
exec("""
a=1
b=2
def func():
    print("func run")
""",glocal_dic,local_dic)
print(glocal_dic)
print(local_dic)
#如果同时制定了 全局和局部 则 会字符串中包含名称 解析后存到局部中
三个参数

6.元类

  6.1定义

    元类是指用于产生类的类。type就是指元类。所有的自定义类都是通过元类实例化得来的

  6.2元类的作用

    使用type(元类)来产生一个自定义类

#首先我们需要知道,一个类由类名,类的父类元组,类的名称空间三部分组成。

#需求:我们需要使用exce和type,自定义一个MyClass的类
#============================================
#类名
class_name='MyClass'
#类的父类
class_bases=(object,)
#MyClass类中的代码
code="""
a=1
b=2
def func(self):
    print("func run ")
"""
#名称空间,此时为空字典
namespaces={}
#使用exec
exec(code,{},namespaces) #此时namespaces就是自定义类的名称空间
#使用type实例化类
res=type(class_name,class_bases,namespaces)
print(res)
print(res.__dict__)

#===========================================
>>>:
<class '__main__.MyClass'>
{'a': 1, 'b': 2, 'func': <function func at 0x0000020838081A60>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}
使用type来实例化产生一个自定义类

  6.3 自定义元类的目的

     1.可以通过__call__来控制对象的创建过程即类实例化对象的过程。

     2.可以控制类的创建过程。

  6.4 元类中的__new__

  __new__和__init__的区别:__new__比__init__先执行。

  __new__的作用是创建一个空的类对象。作为一个类对象,必须具备的三个组成部分(类名、父类的元组、名称空间),所以调用type中的__new__来完成组装,得到这个类对象后需要将其返回,以供__init__来使用。

2.

7.自定义元类控制类的对象的实例化

  在介绍自定义元类控制类的对象的实例化的过程之前,我们介绍一个内置方法__call__。

__call__,调用的意思,在调用对象时,会执行该对象所属类中的__call__方法,调用类时,会执行该类所属元类中的__call__方法。一般这个方法只会在元类中使用,可以控制类被调用时即类实例化对象过程的一些逻辑判断。

  类实例化对象的过程:

          1.创建一个空对象

          2.调用类的初始化函数

          3.得到一个完整的对象

class MyMeta(type): #但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
    def __call__(self, *args, **kwargs): #self是Student这个类,*args和**kwargs是指类实例化对象时传入的参数
        print("MyMeta is running")
        #创建一个空对象
        obj=object.__new__(self)
        #调用初始化函数
        self.__init__(obj,*args,**kwargs)
        #返回对象
        return obj
class Student(object,metaclass=MyMeta):  #Student=Mymeta('Student',(object,),{名称空间})
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def study(self):
        print("%s正在学习。。。"%self.name)
stu=Student("egon",18,"male")  # 会触发Student的类(即元类)中的__call__函数
print(stu.name)
print(stu.__dict__)

#===============================================
>>>:
MyMeta is running
egon
{'name': 'egon', 'age': 18, 'sex': 'male'}
自定义元类控制类的对象的实例化过程

自定义元类控制类的对象的实例化的过程步骤:

    1.创建一个自定义元类,继承type

    2.覆盖type中的__call__方法,传入的参数为 正在实例化对象的类、类实例化对象时的参数

    3.在__call__方法中,,编写类实例化对象过程的模板

      3.1 创建一个空对象

      3.2调用类中的初始化函数

      3.3返回此时的对象

    4.加入控制逻辑

8.自定义元类控制类的创建过程

  要控制类的创建过程,只需要找到类所属元类中的__init__方法即可。

class MyMeta(type): #但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
    def __init__(self,class_name,class_bases,namespaces): #第一个参数为创建的类,其余参数,是类名,类的父类元组,类的名称空间
        #Python会自动创建属性赋值,不需要我们在重新创建
        #书写逻辑
        if not class_name.istitle(): #书写类名大写打开的逻辑
            raise TypeError("类的名字必须以大写字母开头") #主动抛出异常
        if not self.__doc__ or len((self.__doc__).strip())==0:
            raise TypeError("类必须有文档注释且内容不能为空")

class Student(object,metaclass=MyMeta):  #Student=Mymeta('Student',(object,),{名称空间})
    """
    xxx
    """
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def study(self):
        print("%s正在学习。。。"%self.name)
stu=Student("egon",18,"male")  # 会触发Student的类(即元类)中的__call__函数
print(stu.name)
print(stu.__dict__)
#=============================================
当不写文档注释或者文档注释为中没有字符时
>>>:
TypeError: 类必须有文档注释且内容不能为空
自定义元类控制类的创建过程

自定义元类控制类的创建过程的步骤:

    1.创建一个自定义元类,继承type

    2.覆盖type方法中的__init__方法,传入的参数为:正在创建的类、类名,类的父类元组,类的名称空间

    3.加入控制逻辑

    4.对于需要被控制的类,指定metaclass=自定义的元类

9.单例模式

  单例模式指的是一种设计模式。

  单个实例:一个类如果只有一个实例对象,那么称这个类为单例。

#需求:定义一个打印机类,这个类有name,brand,type三个属性,和一个printing方法,根据打印机的实际情况,
# 该类只有一个实例对象即可,不需要有很多对象。
class Printer:
    obj=None
    def __init__(self,name,brand,type):
        self.name=name
        self.brand=brand
        self.type=type
    def printing(self):
        print("正在打印。。。")
    @classmethod
    def get_printer(cls):
        if not cls.obj:  #不存在对象时
            obj=cls("ES005","爱普生","彩色打印机")  #进行实例化
            cls.obj=obj
            print("创建了新的打印机对象")
        return cls.obj

p1=Printer.get_printer()
p2=Printer.get_printer()
p3=Printer.get_printer()
print(p1)
print(p2)
print(p3)

>>>:
创建了新的打印机对象
<__main__.Printer object at 0x000001B2185230B8>
<__main__.Printer object at 0x000001B2185230B8>
<__main__.Printer object at 0x000001B2185230B8>
定义在类中的单例模式
上述定义在类中的单例模式的确解决了一个实例的问题,但是上述解决问题的方案不严谨,如果有些程序员不严谨,不严格依照要求,通过p= Pinker("xx","xx","xxx")的方式还是能定义出其他的类,那么这种方式就不合适了。我们需要的是只能这么用,不管实例化几个对象出来,都是同一个,那么我们可以使用自定义元类来控制类实例化对象的过程这一思想来完成需求。
class MyMeta(type):
    obj=None
    def __call__(self, *args, **kwargs):
        if not MyMeta.obj:
            #创建空对象
            obj=object.__new__(self)
            #调用初始化函数
            self.__init__(obj,*args,**kwargs)
            MyMeta.obj=obj
        return MyMeta.obj
class Printer(metaclass=MyMeta):
    obj=None
    def __init__(self,name,brand,type):
        self.name=name
        self.brand=brand
        self.type=type
    def printing(self):
        print("正在打印。。。")
p1=Printer("ES005","爱普生","彩色打印机")
p2=Printer("ES005","爱普生","彩色打印机")
p3=Printer("ES005","爱普生","彩色打印机")
print(p1)
print(p2)
print(p3)

#============================================
>>>:
<__main__.Printer object at 0x000002740AE4AF60>
<__main__.Printer object at 0x000002740AE4AF60>
<__main__.Printer object at 0x000002740AE4AF60>
使用自定义元类的方式实现单例模式 
原文地址:https://www.cnblogs.com/846617819qq/p/10143111.html