• 反射


    本章内容:

      1.isinstance , type ,issubciass

      2.区分函数 和 方法

      3.反射

    一 isinstance  type  issubciass

      首先,我们先看 issubciass() 这个内置函数可以帮我们判断 xx 类是否是 yy类的子类

    class Base:
        pass
    class Foo(Base):
        pass
    class Bar(Foo):
        pass
    
    print(issubclass(Bar,Foo))  # True
    print(issubclass(Foo,Bar))  # False
    print(issubclass(Bar,Base)) #  True

     然后我们来看 type, type 在前面的学习期间已经使用过了,type(obj) 表示查看 obj 是由哪个类创建的

    class Foo:
        pass
    
    obj = Foo()
    print(obj,type(obj))   # 查看 obj 的类
    
    
    
    # <__main__.Foo object at 0x0197E090> <class '__main__.Foo'>

      那这个鬼东西有什么用呢?可以帮我们判断 xxx 是否是 xxx数据类型的

    class Boy:
        pass
    
    class Girl:
        pass
    # 统计传进来的男生和女生分别有多少
    def func(*args):
        b = 0
        g = 0
        for obj in args:
            if type(obj) == Boy:
                b += 1
            elif type(obj) == Girl:
                g += 1
        return b,g
    ret = func(Boy(),Girl(),Boy(),Girl(),Boy(),Girl(),Boy(),Girl())
    print(ret)   # (4,4)

      或者,你在进行计算的时候,先判断好要计算的数据类型必须是int 或者 float,这样的计算才有意义

    def add(a,b):
        if(type(a) == int or type(b) == float) and (type(b) == int or type(b) == float):
            return a + b
        else:
            print("我要报错")

      isinstance 也可以判断 xxx 是 yyy 类型的数据,但是 isinstance 没有 type 那么精准

    class Base:
        pass
    class Foo(Base):
        pass
    class Bar(Foo):
        pass
    
    print(isinstance(Foo(),Foo))   # True
    print(isinstance(Foo(),Base))  # True
    print(isinstance(Foo(),Bar))   # False

      isinstance 可以判断该对象是否是 xxx家族体系中的(只能往上判断)

    二 区分函数 和 方法

      我们之前讲过函数和方法,这俩东西如何进行区分呢?其实很简单,我们只需要打印一下就能看到区别

    def func():
        pass
    
    class Foo:
        def chi(self):
            print("我是吃")
    f = Foo()
    print(f.chi)
    # 方法 <bound method Foo.chi of <__main__.Foo object at 0x0198E110>>
    
    print(func) # 函数 <function func at 0x018FBFA8>

      函数在打印的时候,很明显显示的是 function ,而方法在打印的时候很明显是 method 

      那在这里,我要声明一下,其实并不一定是这样的,看下面的 代码:

    class Foo:
        def chi(self):
            print("我是吃")
    
        @staticmethod
        def static_method():
            pass
        @classmethod
        def class_method(cls):
            pass
    f = Foo()
    print(f.chi)    
    # <bound method Foo.chi of <__main__.Foo object at 0x019DF190>>
    print(Foo.chi)  
    # <function Foo.chi at 0x019DE588>
    print(Foo.static_method) 
    # <function Foo.static_method at 0x019DE5D0>
    print(Foo.class_method) 
    # <bound method Foo.class_method of <class '__main__.Foo'>>
    print(f.static_method)  
    # <function Foo.static_method at 0x019DE5D0>
    print(f.class_method)      
    # <bound method Foo.class_method of <class '__main__.Foo'>>

       仔细观察:我们得到以下的结论:

      1.类方法,不论任何情况,都是方法

      2.静态方法,不论什么情况,都是函数

      3.实例方法,如果是实例访问,就是方法,如果是类名访问就是函数

       那如何用程序 帮我们分辨,到底是函数还是方法? 首先借助于 types 模块

    # 所有的方法都是MethodType的实例
    # 所有的函数都是FunctionType的实例
    from types import MethodType,FunctionType
    
    def func():
        pass
    
    print(isinstance(func,FunctionType ))   # True
    print(isinstance(func,MemoryError ))    # False
    
    class Foo:
        def chi(self):
            print("我是吃")
        @staticmethod
        def static_method():
            pass
        @classmethod
        def class_method(cls):
            pass
    obj = Foo()
    print(type(obj.chi))   # <class 'method'>
    print(type(Foo.chi))   # <class 'function'>
    
    print(isinstance(obj.chi,MethodType))    # True
    print(isinstance(Foo.chi,FunctionType))  # True
    
    print(isinstance(Foo.static_method,FunctionType))  # True
    print(isinstance(Foo.static_method,MethodType))    # False
    
    print(isinstance(Foo.class_method,FunctionType))     # False
    print(isinstance(Foo.class_method,MethodType))        # True

      用 types 中的 FunctionType 和 MethodType 可以区分,当前内容是方法 还是函数,接下来,看一个小题

    分析分析答案 :

     1 from types import MethodType,FunctionType
     2 
     3 class Foo:
     4     @classmethod
     5     def func1(self):
     6         pass
     7     @staticmethod
     8     def func2(self):
     9         pass
    10     def func3(self):
    11         pass
    12     def func4(self):
    13         pass
    14     lst = [func1,func2,func3]
    15 obj = Foo()
    16 
    17 Foo.lst.append(obj.func4)
    18 for item in Foo.lst:
    19     print(isinstance(item,MethodType))
    20 
    21 # False False False True 

    三 反射

      首先,我们看这样一个需求,说,大牛写了 一堆特别牛逼的代码,然后放在了一个 py 文件中(模块).这时,你想调用这个东西,但是呢,首先你得知道大牛写的这些代码都是干什么的,那就需要你把大牛写的 每一个函数跑一下,摘一摘自己想要的内容,模拟一下:

    #  大牛.py
    
    def chi():
        print("大牛一顿吃100个包子")
    def he():
        print("大牛一次喝100瓶矿泉水")
    def shui():
        print("大牛一次睡一年")
    View Code

      接下来你要去一个一个的调用,但是呢,在调用之前,大牛告诉你了,他写了哪些哪些方法,那仙子阿就可以这么办了..

    import master
    
    while 1:
        print("""作为大牛, 我帮你写了了:
            chi
            he
            shui
    等功能. 自己看看吧""")
        gn = input("请输⼊入你要测试的功能:")
        if gn == 'chi':
            master.chi()
        elif gn == "he": 
            master.he()
        elif gn == "shui":
            master.shui()
        else:
            print("大牛就这几个功能. 别搞事情")                   
                 

        写是写完了了. 但是.....如果大牛现在写了了100个功能呢? 你的判断要判断100次么? 太累
      了吧. 现有的知识解决不了这个问题. 那怎么办呢? 注意看. 我们可以使用反射来完成这样的
      功能. 非常的简单. 想想. 这里我们是不是让用户输入要执行的功能了. 那这个功能就是对应
      模块里的功能. 那也就是说. 如果能通过字符串来动态访问模块中的功能就能解决这个问题.
      好了. 我要告诉你. 反射解决的就是这个问题. 为什么叫反射? 反着来啊. 正常是我们先引入
      模块, 然后用模块去访问模块里的内容. 现在反了. 我们手动输入要运行的功能. 反着去模块
      里找. 这个就叫反射

    import master
    while 1:
        print("""作为大牛, 我帮你写了:
            chi
            he
            shui
    等功能. 自己看看吧""")
        gn = input("请输入你要测试的功能:")
        # niuB版
        func = getattr(master, gn)
        func()    

      getattr(对象, 字符串): 从对象中获取到xxx功能. 此时xxx是一个字符串. get表示找, attr
    表示属性(功能). 但是这里有个问题. 用户如果手一抖, 输入错了. 在大牛的代码里没有你要找
    的内容. 那这个时候就会报错. 所以. 我们在获取attr之前. 要先判断一下. 有没有这个attr.
    完整代码:

    import master
    from types import FunctionType
    while 1:
        print("""作为大牛, 我帮你写了了:
            chi
            he
            shui
    等功能. 自己看看吧""")
        gn = input("请输入你要测试的功能:")
        # niuB版
        if hasattr(master, gn): # 如果master里面有你要的功能
        # 获取这个功能, 并执行
            attr = getattr(master, gn)
            # 判断是否是函数. 只有函数才可以被调用
        if isinstance(attr, FunctionType):
            attr()
        else:
            # 如果不不是函数, 就打印
            print(attr)         

      好了, 这里我们讲到了两个函数. 一个是getattr(). 一个是hasattr(). 其中getattr()用来获
    取信息. hasattr()用来判断xxx中是否包含了了xxx功能, 那么我们可以在模块中这样来使用反射.
    在面向对象中一样可以这样进行操作. 这个就比较牛B了. 后期相关框架内部核心源
    码几乎都是这些东西. 首先, 我们先看一些简单的.

    class Person:
        country = "大清"
        def chi(self):
            pass
    # 类中的内容可以这样动态的进行获取
    print(getattr(Person,"country"))
    print(getattr(Person,"chi"))      # 相当于 Foo.func 函数
    
    # 对象也一样可以
    obj = Person()
    print(getattr(obj,"country"))
    print(getattr(obj,"chi"))       # 相当于 obj,func 方法
    
    # 大清
    # <function Person.chi at 0x0194E588>
    # 大清
    # <bound method Person.chi of <__main__.Person object at 0x0194F150>>

      总结:getattr 可以从模块中获取内容,也可以从类中获取内容,也可以 从对象中获取内容,在 python 中

    一切皆为对象,那可以这样认为,getattr 从对象中动态的获取成员

    来看一个示例:

    class Person:
        def chi(self):
            print("")
        def he(self):
            print("")
        def shui(self):
            print("")
        def run(self):
            lst = ["chi","he","shui"]
            num = int(input("""本系统有以下功能
                1.吃
                2.喝
                3.睡
            请输入你要执行的功能:"""))
    
            # 通过类名也可以使用
            # func = getattr(Person,lst[num - 1])
            # func(self)
            # 通过对象来访问更加合理
            method = getattr(self,lst[num - 1])
            method()
    p = Person()
    p.run()

      补充:
        关于反射, 其实一共有4个函数:
          1. hasattr(obj, str) 判断obj中是否包含str成员
          2. getattr(obj,str) 从obj中获取str成员
          3. setattr(obj, str, value) 把obj中的str成员设置成value. 注意. 这里的value可以是
                      值, 也可以是函数或者方法
          4. delattr(obj, str) 把obj中的str成员删除掉
    注意, 以上操作都是在内存中进行的. 并不会影响你的源代码

    class Foo:
        pass
    
    f = Foo()
    
    print(hasattr(f,"chi"))             # False
    setattr(f,"chi",'123')
    print(f.chi)                        #  123   被添加了一个舒心信息
    
    setattr(f,"chi",lambda x:x + 1)
    print(f.chi(3))                     # 4
    
    print(f.chi)                       # <function <lambda> at 0x0171BFA8>
    #  此时的 chi 既不是静态方法,也不是实例方法,更不是类方法,
    #  就相当于你在 类中写了个 self.chi = lambda 是一样的
    print(f.__dict__)
    # {'chi': <function <lambda> at 0x013FBFA8>}
    
    delattr(f,"chi")
    print(hasattr(f,'chi'))  # False
  • 相关阅读:
    P3688 [ZJOI2017] 树状数组 【二维线段树】
    CF516D Drazil and Morning Exercise【并查集,结论】
    Luogu5540 最小乘积生成树
    CDW数学小笔记
    ZROI 金华集训 线性代数选讲
    多项式乘法(代码)
    我的vimrc
    P4562 [JXOI2018]游戏
    [POJ3585]Accumulation Degree
    [CH5302]金字塔
  • 原文地址:https://www.cnblogs.com/wenqi2121/p/10321648.html
Copyright © 2020-2023  润新知