python-反射-元类-单例模式

一、isinstance和issubclass

# 判断某个对象是不是某个类的实例
# isinstance()
class Person:
    pass
class Student(Person):
    pass

stu = Student()
print(isinstance(stu,Student)) #True

#判断是不是子类
#issubclass()
print(issubclass(Student,Person)) #True

二、反射

  1. 什么是反射:其实说的是反省,简单的说就是对象要具备一种修正错误的能力

    hasattr 是否存在某个属性

    getattr 获取某个属性的值

    setattr 设置某个属性的值

    delattr 删除某个属性

  2. 什么时候用反射:

    如果在编写代码期间,就能明确知道我要访问的属性,没有必要使用反射

    如果在编写代码期间,无法明确知道我要访问的属性,这时就应该使用反射

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

    def study(self):
        print("学生正在学习...")

stu = Student("矮根","woman",38)
print(stu.name) #矮根

#当你获取到一个对象,但是并不清楚对象的内部细节时,就需要使用反射了
def test(obj):
    if hasattr(obj,"name"):
        print(getattr(obj,"name","没有name属性"))
test(stu)

#设置属性的值
setattr(stu,"school","beijing")
print(getattr(stu,"school","没有学校属性")) #beijing

#删除属性的值
delattr(stu,"school")
print(getattr(stu,"school","没有学校属性")) #没有学校属性

#删除属性的值
delattr(stu,"age")
print(stu.age) 

练习:

扫描二维码关注公众号,回复: 4940474 查看本文章
class Student:
    def study(self):
        print("学习中....")

stu = Student()
res = getattr(stu,"study",None)
print(res)

def eat(self):
    print("正在吃饭...")
#可以通过反射的方式为对象增加一个方法,但是注意这样增加的方法就是一个普通函数,不会自动传值
setattr(stu,"eat",eat) #<function eat at 0x0000021994A82E18>
eat(stu) #正在吃饭...
===================================================
# 需要编写一个CMD工具,这个工具可以支持两个命令 dir,tasklist
class CMD:
    def dir(self):
        print("列出当前文件夹目录....")

    def tasklist(self):
        print("查看任务列表.....")

cmd = CMD()
res = input("请输入指令:").strip()

if hasattr(cmd,res):
    func = getattr(cmd,res)
    print(func)
    func()
else:
    print("输入的指令不正确....")

三、__str__

  1. 前后带双下滑线的都是特殊的内置函数,会在某些时机自动执行,一般情况我们不应该直接调用他们。当我们需要自定义打印显示内容时,就需要实现__str__方法,该方法必须返回一个字符串,返回的是什么,打印出来就是什么
class Test:
    def __init__(self,name):
        self.name = name
    def __str__(self):
        print("str run....")
        return self.name
t = Test("张三")
#在将一个对象转换字符串时,本质就是在调用这个对象 __str__ 方法
print(str(t))
#str run....
#张三

四、__del__

  1. 当对象从内存中删除时会自动执行,另外一种情况时,程序员手动删除了这个对象,也会自动执行

  2. 什么时候用:在python中有自动内存管理机制,所以 python自己创建的数据,会自动清理,不需要我们做任何操作。但是有一种情况,我们使用python打开了一个不属于python管理的数据,比如:打开了了一个文件,这个文件是操作系统在打开,会占有系统内存,而python解释器无法操作系统内存的,所以当你的python解释器运行结束后,文件依然处于打开状态,这时候就需要使用__del__来关闭系统资源

    简单的说,当程序运行结束时,需要做一些清理操作,就使用__del__

    __del__也称之为析构函数,分析构造,并拆除这个对象

class Student:

    def __del__(self):
        print("对象被删除了....")

stu = Student()
#手动删除,立即执行__del__
del stu
class TextFile:

    def __init__(self,filepath,mode="rt",encoding="utf-8"):
        self.file = open(filepath,mode=mode,encoding=encoding)

    def read(self):
        return self.file.read()

    def write(self,text):
        self.file.write(text)

    # 该方法其实就是一个通知性质 仅仅是告诉程序员 对象即将被删除
    def __del__(self):
        # 在这里关闭系统的文件 妥妥的
        self.file.close()

tf = TextFile("a.txt")
print(tf.read())
tf.file.close() #不需要手动关闭了,在对象删除时会自动关闭
tf.read() #等所有的程序执行完,在执行关闭,只执行一次

五、exec方法

  1. exec:execute的缩写,表示执行的意思
  2. exec作用:其作用是帮你解析执行python代码,并且将得到的名称存储到指定的名称空间,解释器内部也是调用它来执行代码的
  • 参数一:需要一个字符串对象,表示需要被执行的python语句
  • 参数二:是一个字典,表示全局名称空间
  • 参数三:也是一个字典,表示局部空间
globalsdic = {}
localsdic = {}

exec("""
aaa = 1
bbbb = 2
def func1():
    print("我是func1")
""",globalsdic,localsdic)

#如果同时制定了全局和局部,则会将字符串中包含名称解析后存到局部中
print(localsdic) #'aaa': 1, 'bbbb': 2, 'func1': <function func1 at 0x000001DF2FBD2E18>}
localsdic["func1"]() #我是func1


# 如果只传了一个传参数,则将字符串中包含名称解析后存到全局中
exec("""
aaaaaaaaaaaaaaaaaaaa = 1
bbbbbbbbbbbbbbbbbbbb = 2
""",localsdic)

六、元类

  1. 什么是元类:一切皆对象,元类是指,用于产生类的类,type就是元类,所有的自定义类都是通过type实例化得来
  • 类是由type实例化产生的
  • 我们可以使用type来产生一个类
  • 一个类是由类名字,类的父类元祖, 类的名称空间三个部分组成
#class 也是一个对象
class Student(object):

    school = "北京大学!"

    def study(self):
        print("学习中...")

#使用type可以发现,类其实是type类型的实例(对象)
print(type(Student)) #<class 'type'>
#我们可以自己调用type实例化产生一个类
code = """
name = "张三"
age = 18
def hello(self):
    print("hello %s" % self.name)
"""
code = """
name = "张三"
age = 18
def hello(self):
    print("hello %s" % self.name)
"""

#类的名字
class_name = "MyClass"
#类的的父类们
base_classes = (object,)
#类的名称空间
namespace = {}

exec(code,{},namespace)
res = type(class_name,base_classes,namespace)

print(res.name) #张三
print(res.age) #18
res.hello(res) #hello 张三

#一个类是由类名字,类的父类元祖, 类的名称空间三个部分组成
class Test(object): #Test = type("Test",(object,),{})
    pass

元类补充:

class MyMeta(type):

    def __new__(cls, *args, **kwargs):
        print("new run")

        res = type.__new__(cls,*args)
        return res

    def __init__(self,class_name,bases,namespace):
        print("init run")
        print(self)

class Student(metaclass=MyMeta):
    pass

print(Student)
#new run
#init run
#<class '__main__.Student'>
#<class '__main__.Student'>
__new__与__init__的区别
__new__ 比 __init__ 先执行,其作用是创建一个空的类对象
作为一个类对象,必须具备是三个组成部分,所以调用type中的__new__来完成组
得到这个类对象后需要将其返回,以供__init__来使用

七、__call__方法控制对象的实例化

  1. __call__:调用的意思,在对象被调用时执行
  2. 自定义元类的目的:
    1. 可以通过__call__来控制对象的创建过程
    2. 可用控制类的创建过程
class MyMeta(type):

    # self表示要创建对象的那个类(Person)  *args是调用Person类时传入的参数
    def __call__(self, *args, **kwargs):

        print("MyMte中的 call run")
        print(self,*args,**kwargs)

        # 下面的三步是固定写法,是一个模板,只要你需要控制对象的创建过程,就应该先把模板写出来
        # 1.创建空对象
        obj = object.__new__(self)
        # 2.调用初始化方法
        self.__init__(obj,*args,**kwargs)
        # 3.得到一个完整的对象
        return obj

# 修改Person类的元类为MyMeta
class Person(metaclass=MyMeta):

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

    def __call__(self, *args, **kwargs):
        print("call run...")
#调用Person这个对象时,执行的是Person的类(type)中__call__ 方法
p = Person("张三疯",80)
print(p.name) #张三疯
print(p.age) #80

八、通过元类控制类的创建过程

  1. 要控制类的创建过程,只要找到类所属的类中的__init__即可
class MyMeta(type):

    # self 刚建出来的类
    # 第二个 类的名字
    # 第三个 类的父类们(元组)
    # 第四个 这个类传进来的名称空间
    def __init__(self,class_name,bases,namespace):
        print("============================")
        #print(self.__dict__)
        # 我要控制类的名字,必须是大写开头
        if not class_name.istitle():
            print("类名 必须大写开头...... ")
            # 该代码是主动抛出异常
            raise TypeError("类名 必须大写开头...... ")
        #要空类的创建,必须包含__doc__这个属性
        if not self.__doc__:
            raise TypeError("类中必须有文档注释.....")

class student(metaclass=MyMeta):   # Student = MyMeta("Student",(object,),{})
    """
        这是文档注释  可以通过__doc__来获取
        这是一个学生类
    """
    def __init__(self,name):
        print("-----------------------")
        print(self.__dict__)
        self.name = name


print(student.__doc__)

元类的使用总结:

  • 元类是用于创建类的类
  • 元类是为了能控制类的创建过程以及类实例化对象的过程

一、控制类的创建过程:

  1. 创建一个元类(需要继承type)
  2. 覆盖__init__方法,该方法会将新建的类对象、类名,父类们、名称空间都传进来,可以利用这些信息在做处理
  3. 对于需要被控制的类,需要指定metaclass 为上面的元类

二、控制类实例化对象的过程:

  1. 创建一个元类(需要继承type)
  2. 覆盖__call__方法,会将正在实例化对象的类,调用类传入的参数都传进来
  3. __call__方法中必须要先编写模板代码
    1. 创建空对象
    2. 调用类的__init__方法来初始化这个空对象
    3. 返回该对象
  4. 加入你需要控制的逻辑

类的三个组成部分:

类名、父类们、名称空间

元类===》实例化产生===》类===》实例化产生===》对象

九、单例模式

  1. 什么是单例模式:单个实例,一种设计模式
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):
    """
    这是一个单例类,请不要直接实例化,使用get方法来获取实例
    """

    obj = None
    def __init__(self,name,brand,type):
        self.name = name
        self.brand = brand
        self.type = type

    def printing(self,text):
        print("正在打印 %s" %text)

    # 通过该方法来获取对象可以保证只有一个对象
    # 但是这还不够,因为还是可以通过调用类产生新对象
    # 就应该使用元类来控制实例化的过程 __call__
    # 在__call__ 中编写代码,保证每次调用call都返回同一个实例即可

    @classmethod
    def get_printer(cls):
        if not cls.obj:
            obj = cls("ES005","爱普生","彩色打印机")
            cls.obj = obj
            print("创建了新的对象")

        return cls.obj
p = Printer.get_printer()
print(p) #<__main__.Printer object at 0x0000023FCEAA1208>

p = Printer.get_printer()
print(p) #<__main__.Printer object at 0x0000023FCEAA1208>

p1 = Printer("ES005","爱普生","彩色打印机")
p2 = Printer("ES005","爱普生","彩色打印机")

print(p1) #<__main__.Printer object at 0x0000023FCEAA1208>
print(p2) #<__main__.Printer object at 0x0000023FCEAA1208>

猜你喜欢

转载自blog.csdn.net/zdc45625/article/details/85108984