• (十八)反射


    . isinstance, type, issubclass

     ⾸先, 我们先看issubclass() 这个内置函数可以帮我们判断xxx类是否是yyy类型的⼦类.

    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的类
    

      

    那这个⿁东⻄有什么⽤呢? 可以帮我们判断xxx是否是yyy数据类型的

    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(), Girl(), Girl(), Boy(), Boy(), Girl())
    print(ret)
    

      

    isinstance也可以判断xxxyyy类型的数据. 但是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
    print(func) # <function func at 0x10646ee18>
    class Foo:
     	def chi(self):
     		print("我是吃")
    f = Foo()
    print(f.chi) # <bound method Foo.chi of <__main__.Foo object at 0x10f688550>>
    

      

    函数在打印的时候. 很明显显⽰的是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 0x10f688550>>
    print(Foo.chi) # <function Foo.chi at 0x10e24a488>
    print(Foo.static_method) # <function Foo.static_method at 0x10b5fe620>
    print(Foo.class_method) # bound method Foo.class_method of <class '__main__.Foo'>>
    print(f.static_method) # <function Foo.static_method at 0x10e1c0620>
    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, MethodType)) # False
    class Foo:
       def chi(self):
         print("我是吃")
       @staticmethod
       def static_method():
         pass
       @classmethod
       def class_method(cls):
         pass
    obj = Foo()
    print(type(obj.chi)) # method
    print(type(Foo.chi)) # 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
    

      

    . 反射.

     ⾸先, 我们看这样⼀个需求, , 有个⼤⽜, 写了⼀堆特别⽜B的代码. 然后放在了⼀个py

    ⽂件⾥(模块), 这时, 你想⽤这个⼤⽜写的东⻄. 但是呢. 你⾸先得知道⼤⽜写的这些代码都是⼲什么⽤的. 那就需要你把⼤⽜写的每⼀个函数跑⼀下. 摘⼀摘⾃⼰想⽤的内容. 来咱们模拟这样的需求, ⾸先, ⼤⽜给出⼀个模块.

    ⼤⽜.py 

    def chi():
     	print("⼤⽜⼀顿吃100个螃蟹")
    def he():
     	print("⼤⽜⼀顿喝100瓶可乐")
    def la():
     	print("⼤⽜不⽤拉")
    def shui():
     	print("⼤⽜⼀次睡⼀年")

     接下来, 到你了. 你要去⼀个⼀个的调⽤. 但是呢. 在调⽤之前. ⼤⽜告诉你了. 他写了哪些⽅法. 那现在就可以这么来办了 

    import master
    while 1:
     print("""作为⼤⽜, 我帮你写了:
     chi
     he
     la
     shui
    等功能. ⾃⼰看看吧""")
     gn = input("请输⼊你要测试的功能:")
     if gn == 'chi':
     	master.chi()
     elif gn == "he":
         master.he()
     elif gn == "la":
     	master.la()
     elif gn == "shui":
     	master.shui()
     else:
     	print("⼤⽜就这⼏个功能. 别搞事情")
    

      

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

    import master
    while 1:
     print("""作为⼤⽜, 我帮你写了:
     chi
     he
     la
     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
     la
     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中是否包含了yyy功能, 那么我们可以在模块中这样来使⽤反射.在⾯向对象中⼀样可以这样进⾏操作. 这个就比较⽜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 ⽅法
    

      

    总结, getattr可以从模块中获取内容, 也可以从类中获取内容, 也可以从对象中获取内. python中⼀切皆为对象. 那可以这样认为. getattr从对象中动态的获取成员

     来看⼀个⽰例

    class Person:
     	def chi(self):
     		print("吃")
     	def he(self):
     		print("喝")
     	def la(self):
     		print("拉")
     	def sa(self):
     		print("撒")
     	def shui(self):
     		print("睡")
    def run(self):
     lst = ['chi', 'he', 'la', 'sa', 'shui']
     num = int(input("""本系统有以下功能
     1. 吃
     2. 喝
     3. 拉
     4. 撒
     5. 睡
    请选择你要执⾏的功能:"""))
     # 通过类名也可以使⽤
     # func = getattr(Person, lst[num - 1])
     # func(self)
     # 通过对象来访问更加合理
    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) # 被添加了⼀个属性信息
    setattr(f, "chi", lambda x: x + 1)
    print(f.chi(3)) # 4
    

      

  • 相关阅读:
    redis线程模型
    同步容器和并发容器
    200+面试题
    redis pipeline
    redis事务和脚本
    redis事务
    redis优缺点
    redis持久化策略
    Redis为什么要把所有数据放到内存中?
    redis的过期策略以及内存淘汰机制
  • 原文地址:https://www.cnblogs.com/asia-yang/p/10152314.html
Copyright © 2020-2023  润新知