反射

⼀. 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是否是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(), Girl(), Girl(), Boy(), Boy(), Girl())
print(ret)

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

 

def add(a, b):
 if (type(a) == int or type(a) == 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
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

    ⽤types中的FunctionType和MethodType可以区分, 当前内容是⽅法还是函数, 接下来.
看⼀个⼩题, 并分析答案

from types import MethodType, FunctionType
class Foo:
 @classmethod
 def func1(self):
 pass
 @staticmethod
 def func2(self):
 pass
 def func3(self):
 pass
 
 def func4(self):
 pass
 lst = [func1, func2, func3]
 
obj = Foo()
Foo.lst.append(obj.func4)
for item in Foo.lst:
 print(isinstance(item, MethodType))

  练习. 写⼀个函数. 判断传递进来的内容是函数还是⽅法?
三. 反射.
  ⾸先, 我们看这样⼀个需求, 说, 有个⼤⽜, 写了⼀堆特别⽜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中是否包含了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 ⽅法

  总结, 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)
 # 通过对象来访问更加合理
 # 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) # 被添加了⼀个属性信息
setattr(f, "chi", lambda x: x + 1)
print(f.chi(3)) # 4
print(f.chi) # 此时的chi既不是静态⽅法, 也不是实例⽅法, 更不是类⽅法. 就相当于你在类中
写了个self.chi = lambda 是⼀样的
print(f.__dict__) # {'chi': <function <lambda> at 0x107f28e18>}
delattr(f, "chi")
print(hasattr(f, "chi")) # False

 

posted @ 2019-01-14 17:06  不痴  阅读(139)  评论(0)    收藏  举报