面向对象之反射,元类

一.反射

1.什么是反射

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问,检测和修改它本身状态或行为的一种能能力(自省)。

2.python中的反射

反射是所有面向对象编程语言都具备的功能

python中通过一下四个函数来实现反射

1 hasattr(object,name)  #判断对象是否拥有某个属性
2 setattr(object,name,value) #为对象增加新的属性
3 getattr(object,name,default) #从对象中获取某个属性
4 delattr(object,name) #从对象中删除某个属性

3.为什么需要反射

一个类在定义的时候,可能一些属性的设计并不是很完美,而后期需要做出修改,或是增加新属性时,使用反射的方式可以不需要修改源代码

  反射的另一个优势(重点):可插拔设计 

 

    不仅限于修改已有的属性,通过反省能够发现已存在的属性,只要你给我一个对象我就能检查其拥有的属性,从而使用这些属性,而不需要提前了解这些对象,这大大提高了程序的扩展性

案例1:

 1 import plugins
 2 def run(plugin):
 3     while True:
 4         cmd = input('请输入指令:').strip()
 5         if cmd == 'exit':
 6             break
 7         if hasattr(plugin,cmd):
 8             fun = getattr(plugin,cmd)
 9             fun()
10         else:
11             print('该指令不受支持')
12       print(‘已退出’)
1 #plugins
2 
3 class Wincmd:
4         def dir(self):
5             print('wincmd列出所有文件。。。')
6         def cd(self):
7             print('wincmd 切换目录。。。')
8         def delete(self):
9             print('wincmd 要不要删除目录。。。 ')

对案例1进行改进,使用动态导入模块

案例2:

  1.首先配置settings

class_path = 'lib.plugins.Wincmd'

  2.主体框架:

 1 import  importlib
 2 from 面向对象 import settings
 3 def run(plugin):
 4     while True:
 5         cmd = input('请输入指令:')
 6         if cmd == 'exit':
 7             break
 8         if hasattr(plugin,cmd):
 9             func = getattr(plugin,cmd)
10             func()
11         else:
12             print('该指令不受支持')
13     print('see you la la')
14 
15 path,class_name = settings.class_path.rsplit('.',1)  
16 mk = importlib.import_module(path)     #找到模块(文件)
17 cls = getattr(mk,class_name)  #通过反射,模块与类名,得到类
18 obj = cls()  #实例化对象
19 run(obj)  #调用函数  传入对象

二.元类

1.什么是元类?

在Python中,一切皆对象,类也是对象。

验证一下:

1 class Person:
2     pass
3 print(type(Person))
4 #<class 'type'>

由此看来Person类属于type类,是由type类产生的对象。

因此可以推导出:

用于实例化产生类的类称之为元类,就是此时的type类

2.创建类的流程分析:

 一个类有三个基本组成部分:

  1.类的名字(字符类型)

  2.类的父类们(是一个元组或列表)

  3.类的名称空间(字典类型)

用另一种方式出创建类

cls_obj = type('dog',(),{})
#同样可以创建类

3.自定义元类,通过自定义原来创建类

创建类是由type完成的type中必然包含了创建了的具体代码,现在需要对这些代码进行修改,两种方式:

  1.修改type源代码不可取

  2.创建新的元类,使用自己的元类来创建类从而实现定制类

一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过该继承type

来自定义元类,然后使用metaclass关键字参数为一个类指定元类

1 class MyType(type):
2     def __init__(self,clss_name,bases,dict):
3         super().__init__(clss_name,bases,dict)
4 
5 class Person(metaclass = MyType):
6     pass

4.在自定义元类中通过覆盖type类中的__init__来控制定义类

下面有个需求,我们高度自定义一个类,需要控制类的名字必须以大驼峰的方式来书写

这是需要在自定义元类中覆盖type类中的__init__方法。

class MyType(type):
    def __init__(self,cls_name,bases,dict):
        super().__init__(cls_name,bases,dict)
        if not cls_name.istitle():
            raise Exception('类名要大写的驼峰')

class Pig(metaclass = MyType):
    pass

5.元类中call方法(当你想要控制对象的创建过程时)

当你调用类对象时会自动珍惜元类中的__call__方法,并将这个类本身作为第一个参数传入,以及右面的一堆参数,覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建,并返回其返回值。

案例:实现将对象的所有属性名成转为大写

class MyType(type):
    def __call__(self,*args,**kwargs):
        for k,v in kwargs.items():
            kwargs[k] = v.upper()
        return super().__call__(*args,**kwargs)

class Person(metaclass = MyType):
        def __init__(self,name,gender):
            self.name = name
            self.gender = gender
p = Person(name = 'jack',gender = 'man')
print(p.name)
print(p.gender)

 

注意:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象

6.单例设计模式

单例:指的是一个类产生一个对象

为什么要使用单例:单例是为了节省资源,当一个类的所有对象属性全部相同时,则没有必要创建多个对象

需求:只生成一个对象

 1 class Single(type):
 2     def __call__(self, *args, **kwargs):
 3         if hasattr(self,"obj"): #判断是否存在已经有的对象
 4             return getattr(self,"obj") # 有就返回
 5         obj = super().__call__(*args,**kwargs) # 没有则创建
 6         self.obj = obj # 并存入类中
 7         return obj
 8 
 9 class Person(metaclass=Single):
10     pass
11 
12 # 只会创建一个对象
13 Person()
14 Person()
15 #创建再多个对象都是同一个

 

posted @ 2019-07-30 16:52  s686编程传  阅读(150)  评论(0编辑  收藏  举报