面向对象编程 --- 反射

Posted krisyzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象编程 --- 反射相关的知识,希望对你有一定的参考价值。

反射 (reflect)

什么是反射,其实是反省,自省的意思

反射指的是一个对象应该具备,可以检测,修改,增加自身属性的能力

反射就是通过字符串操作属性

设计四个函数,这四个函数就是普通的内置函数,没有下划线,与print等没区别.

hasattr   getattr    setattr    delattr

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

stu = Student(uu,12)
if hasattr(stu,name):  # 判断对象是否存在某个属性
    print(getattr(stu,name‘,None))  # 从对象中取出属性,第三个为默认值  当属性不存在时 返回默认值
# 为对象添加新的属性
setattr(stu,gender,male)
print(stu.gender)
# 从对象中删除属性 delattr(stu,
name) print(stu.name)

使用场景:

  反射其实就是对属性的增删改查,但是如果直接使用内置的__dict__来操作,语法繁琐,不好理解

框架设计方式:
框架代码:

反射被称为框架的基石,为什么?因为框架的设计者,不可能提前知道你的对象到底是怎么设计的,所以你提供给框架的对象,必须通过判断验证之后才能正常使用,判断验证就是反射要做的事情,当然通过__dict__也是可以实现的,其实这些方法也就是对__dict__的操作进行了封装

"""需求:要实现一个用于处理用户的终端指令的小框架"""
# settings

# 该文件作为框架的配置文件
# 作为框架的使用者  在配置文件中指定你配合框架的类是哪个
Class_path = libs.plugins.WinCmd
#libs.plugins


# class WinCmd:
#
#     def cd(self):
#         print(‘wincmd切换目录‘)
#
#     def delete(self):
#         print(‘wincmd删除‘)
#
#     def dir(self):
#         print(‘列出目录‘)
#
# class LinuxCmd:
#
#     def cd(self):
#         print(‘Linux切换目录‘)
#
#     def delete(self):
#         print(‘Linux删除‘)
#
#     def ls(self):
#         print(‘列出目录‘)
import settings
import importlib
# 框架已经实现的部分
def run(obj):
    while True:
        cmd = input(请输入指令:)
        if cmd == exit:
            break
     # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
     # 判断对象是否具备处理这个指令的方法
if hasattr(obj,cmd):
      # 取出对应的方法 res
= getattr(obj,cmd) res() # 执行方法处理指令 else: print(nono) print(结束) # 框架 得根据配置文件拿到需要的类
# 从配置中单独拿出来 模块路径和类名称 modles_path,cls_name
= settings.Class_path.rsplit(.,1)
# 拿到模块 mk
= importlib.import_module(modles_path)
# 拿到类 cls
= getattr(mk,cls_name)
# 实例化对象 obj
= cls()
# 调用框架 run(obj)

如此一来,框架就与实现代码彻底解耦了,只剩下配置文件

元类(metaclass)

元类是什么,用于创建类的类

万物皆对象,类当然也是对象

对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是有另一个类实例化产生的

默认情况下所有类的元类都是type

验证:

class Person:
    pass

p = Person()
print(type(Person))  # <class ‘type‘>

学习元类的目的:

高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来书写

类也是对象,也有自己的类

"""需求:创建类对象做一些限制"""

# 定义了一个元类
class MyType(type):
    def __init__(self,cls_name,bases,dict):
        super().__init__(cls_name,bases,dict)
        print(cls_name,bases,dict)
        if not cls_name.istitle:
            raise Exception(请遵守规则!)

为Pig类指定了元类为MyType
class Pig(metaclass=MyType)
    pass
#  Pig () ‘__module__‘: ‘__main__‘, ‘__qualname__‘: ‘Pig‘

class pig(metaclass=MyType):
    pass
# 报错  Exception: 请遵守规则!

元类中call方法:

__call__函数的执行时机: 该方法会在调用对象时自动触发执行(对象加括号)

class Foo:
    def __call__(self,*args,**kwargs):
        print(run)

f = Foo()  # 调用Foo得到f对象
f()  # 调用对象时  触发__call__的执行

通常调用一个普通对象是没有意义的,那__call__在什么时候用呢?

我们说类也是一个对象,那么Foo()是不是也执行了Foo的类中的__call__函数呢?

Foo的类是谁呢? 默认是元类type,通过mateclass来指定为自定义的元类来测试

# 测试
class A(type):
    def __call__(self, *args, **kwargs):
        print(run)
        pass
    pass

class B(metaclass=A):
    pass

print(B())
# 输出   run
# 输出 None

覆盖__call__函数时的注意事项

  第一行输出表明了,调用类B时,的确自动执行了__call__函数,

  第二行输出一个空,这是为什么呢?

  必须明确创建对象的过程: 先创建空对象,执行初始化将属性存储到对象的名称空间中!
  所以在__call__函数中必须完成这两步操作,同时将初始化完成的对象返回给调用者

  一旦覆盖了__call__函数,就必须自己来完成上述的几个步骤

class MyMeta(type):
    def __init__(self,name,bases,dict):
        super().__init__(name,bases,dict)
        print(run)

    def __call__(self, *args, **kwargs):
        print(元类 call)
        print(self)
        print(args)
        print(kwargs)

        return super().__call__(*args,**kwargs)

class Dog(metaclass=MyMeta):

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

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

d = Dog(dahuang)
print(d.name)

# 输出结果
#run
# 元类 call
# <class ‘__main__.Dog‘>
# (‘dahuang‘,)
# 
# dahuang
# 必须以关键字的形式传参

class Metaclass(type):
    def __call__(self, *args, **kwargs):
    
        if args:
            raise Exception(必须以关键字的形式传参)
        return super().__call__(*args,**kwargs)


class A(metaclass=Metaclass):
    def __init__(self,name):
        self.name = name

a = A(name = uu)
print(a.name)

元类的new方法

当你要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__Init__来对这个类进行初始化操作

注意:如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是对应的类对象

class Meta(type):

    def __new__(cls, *args, **kwargs):
        print(cls) # 元类自己
        print(args) # 创建类需要的几个参数  (类名,基类,名称空间)
        print(kwargs) # 空的
        print(new run)
        obj = type.__new__(cls,*args,**kwargs)
        return obj

    def __init__(self,a,b,c):
        super().__init__(a,b,c)
        print(run)

class A(metaclass=Meta):
    pass

# a = A()
print(A)

总结new方法和init方法都可以实现控制类的创建过程,init更简单

使用场景:

  当你想要控制对象的创建过程时,就覆盖call方法

  当你想要控制类的创建过程时,就覆盖init方法

单例设计模式

设计模式用于解决某种固定问题的套路

MVC     MTV

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

为什么要用单例:

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

# 单例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

# 只会创建一个对象
Person()
Person()

 

以上是关于面向对象编程 --- 反射的主要内容,如果未能解决你的问题,请参考以下文章

深入理解面向对象与反射

Python基础-week06 面向对象编程进阶

python开发学习-day07(面向对象之多态类的方法反射新式类and旧式类socket编程)

python面向对象编程

面向对象编程-总复习

Python面向对象进阶和socket网络编程-day08