面向对象之反射 元类

一:反射

【1】基础概念:

(1)定义:反射指的是一个对象 应该具备 修改 检测 增加属性的能力

    本质:属性的增删改查

 

(2)使用场景:

    (1)当我框架搭建出来 需要向框架内部添加细节的时候 但是不知道该细节中内对象有什么属性 类支持什么功能 

    (2)此时可以通过反射询问对象含有什么属性 类支持什么功能

 

(3)涉及的四个函数

  (1)hasattr

    作用:查看某对象是否含有某些属性

例如:

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

p = Person('SR',18)

print(hasattr(p,'name'))  # True

  

  (2)getattr

      作用:通过该函数可以获取对象的属性

例如:

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

p = Person('SR',18)

if hasattr(p,'name'):
    print(getattr(p,'name',None))  # SR
    print(getattr(p, 'names', None)) # None

PS:

(1)如果对象属性不存在 会报错

(2)也可以设置默认值参数 属性不存在返回None

 

  (3)setattr

    作用:设置对象的属性

例如:

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

p = Person('SR',18)

setattr(p,'id_cart','123')
print(p.id_cart)  # 123

 

  (4)delattr

    作用:删除对象的属性

例如:

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

p = Person('SR',18)

delattr(p,'id_cart')
print(p.id_cart)  # 报错 上述属性已经被删除

PS:上述四个方法都是属于普通函数 没有双下划线 非类内值的方法

反射的案例:

例如:

def run(self):
    while True:
        cmd = input('请输入你要执行的命令>>:')

        # 如果输入exit退出
        if cmd == 'exit':
            break

        # 判断类是否有该方法
        if hasattr(self,cmd):

            # 如果有该方法 获取
            func= getattr(self,cmd)
            # 通过获取的方法调用
            func()

        else:
            print('命令不支持!')



win = Wincmd()
linux = Linuxcmd()

# 将对象以参数形式传入 调用类中的方法以及属性
run(win)
run(linux)
反射案例

 

(2)动态导入:

  作用:

   (1)当我框架设计好的时候 开始使用对象以及类

     (2)但是作为框架的设计者 并不能知道对方的类叫什么名字 对象叫什么名字

   (3)因此我们为框架的使用者创建一个配置文件 框架使用者将类的信息传入配置文件中

   (4)框架设计者在该配置文件中调用所需的类

例如:

def run(self):
    while True:
        cmd = input('请输入你要执行的命令>>:').strip()

        # 如果输入exit退出
        if cmd == 'q':
            break

        # 判断类是否有该方法
        elif hasattr(self,cmd):

            # 如果有该方法 获取
            func= getattr(self,cmd)
            # 通过获取的方法调用
            func()

        else:
            print('命令不支持!')

import importlib
import settings

# 在框架设计者 通过配置文件获取 使用者的类路径 以及类名称
PATH = settings.CLASS_PATH
# print(PATH)  # lib.plugins.Wincmd

# 从模块中分别取出路径 以及类名称

module_path,class_name = PATH.rsplit(".",1)
# print(module_path) # lib.plugins
# print(class_name) # Wincmd

# 通过导入路径名称 拿到模块
path = importlib.import_module(module_path)

# 通过模块调取内部的类
cls = path.Wincmd

# 进行类的实例化 获取对象
obj = cls()


run(obj)  # 正在执行cd命令!
动态导入

PS:

  (1)框架使用者可以自己定义自己的名称 只需将类名称以及路径设置配置文件即可

  (2)框架设计者无需管框架使用者类中的名称以及属性 其只要从配置文件调用即可

 

二:元类

【1】基础概念

(1)定义:是用来创建类的类

    解释:既然万物皆对象 类也属于对象 类对象就是通过元类产生的

例如:

class Person:
    pass

p = Person()

print(type(p))

print(type(Person)) # <class 'type'>

PS:类都是有type类产生的

 

(2)自定义类:

  (1)基本组成:

    (1)类名

    (2)基类

    (3)名称空间

例如:

cls = type('Name',(),{})
print(cls)  # <class '__main__.Name'>

 

 (3)作用:高度的自定义自己需要的类

例如:

# 自定义一个元类
class My_class(type):
    # 调用元类初始化方法
    def __init__(self,class_name,bases,dict):
        super().__init__(class_name,bases,dict)

        # 判断类名是否已大写开头
        if not class_name.istitle():
            raise Exception ('类名不会写吗!')

# 指定其所对应的元类
class Person(metaclass=My_class):
    pass

class person(metaclass=My_class): # Exception: 类名不会写吗!
    pass

PS:通过上述案例 自己定义一个类 要求类名必须为大写字母开头

基本思路:

  (1)首先类也是对象 我们想在创建类的时候做限制

  (2)既然想在创建的类的时候做限制 可以使用初始化方法 本身我们不能随便修改源代码 但是我们可以调用元类中的初始化方法

  (3)子类通过覆盖元类中的初始化方法实现需求

 

(4)call方法的应用

    作用:在对象被调用对象的时候 会执行该方法

基础案例:

class My_class(type):
    def __init__(self,name,bases,dict):
        super().__init__(name,bases,dict)


    def __call__(self, *args,**kwargs):
        print(self)  # <class '__main__.Dog'>
        print(*args) # 大黄
        print(**kwargs) #{}
        # 返回对象
        return super().__call__(*args,**kwargs)

class Dog(metaclass=My_class):

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

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


# 执行该方法的时候 会调用元类中的call方法
d = Dog('大黄')
# 在元类中没有返回对象的时候
print(d)  # None 而不是一个对象

# 元类中返回对象
print(d) # <__main__.Dog object at 0x00000000027F8B70>
call基础案例

PS:

(1)当你调用类对象的时候 会自动调用元类中的call方法 并且将这个类本身作为第一个参数传给元类 以及后面的一堆参数

(2)但是在自定义元类中因为调用了type这个元类 会覆盖原有元类call方法 这样原来的call方法就不能实例化对象 必须调用super().__call__来完成对象的创建

 

call进阶版:

# 将对象姓名变为大写

class My_class(type):
    # 调用call方法 可以操作对象
    def __call__(self, *args, **kwargs):
        new_args = []
        # 循环打印 创建新的
        for i in args:
            new_args.append(i.upper())
            # 将新的对象返回
        return super().__call__(*new_args,**kwargs)





class Person(metaclass=My_class):
    def __init__(self,name):
        self.name = name

p = Person('jack')
print(p.name) # JACK
call案例

PS:

(1)如果操作类调用init方法 因为类的创建是初始化过程 init用来初始化

(2)操作对象可以调用call方法 对象的产生就是调用call方法 可以通过call方法 影响对象

 

(5)__new__方法

例如:

class Meta(type):

    def __new__(cls, *args, **kwargs):
        print(cls) # 元类自己
        print(args) # 创建类需要的几个参数  类名,基类,名称空间
        print(kwargs) #空的 
        print("new run")
        # return super().__new__(cls,*args,**kwargs)
        obj = type.__new__(cls,*args,**kwargs)
        return obj
    def __init__(self,a,b,c):
        super().__init__(a,b,c)
        print("init run")
class A(metaclass=Meta):
    pass
print(A)

PS:

(1)首先会执行元类中的new方法 拿到一个空对象 然后自动调用init来对这个类进行初始化操作

(2)如果你覆盖了该方法 必须保证new方法有返回值 且为该类对应的 对象

 

(6)元类的单例设计模式:

  (1)设计模式:指的是解决某种问题的固定模式
  (2)单例:一个类只产生一个对象

  作用:为了节省空间 一个类全部对象属性都相同时候 可以使用单例

例如:

  1. # 单例n元类
    class Single(type):
        def __call__(self, *args, **kwargs):
            if hasattr(self,"obj"): #判断是否存在已经有的对象
                return getattr(self,"obj") # 有就返回
    
            obj = super().__call__(*args,**kwargs) # 没有则创建
            print("new 了")
            self.obj = obj # 并存入类中
            return obj
    
    
    class Student(metaclass=Single):
        def __init__(self,name):
            self.name = name
    
    
    class Person(metaclass=Single):
        pass

     

posted @ 2019-07-30 17:07  SR丶  阅读(158)  评论(0编辑  收藏  举报