类型判断
issubclass
首先,我们先看issubclass() 这个内置函数可以帮我们判断x类是否是y类型的子类。
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在前面的学习期间已经使用过了。type(obj) 表示查看obj是由哪个类创建的。
class Foo: pass obj = Foo() print(obj, type(obj)) # 查看obj的类
isinstance
isinstance也可以判断x是y类型的数据。
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可以判断该对象是否是家族体系中的(只能往上判断类)。
反射
为什么需要反射?
首先,我们来看这样一个需求。
从前有一个大牛,写了一堆特别牛B的代码。然后放在一个py文件(模块)中。这个时候你想使用一下大牛写的东西。但是呢,你首先得知道大牛写的这些代码都是干什么用的。那就需要你把大牛写的每一个函数跑一下。
大牛.py
def chi(): print("⼤⽜一顿吃100个螃蟹") def he(): print("⼤牛一顿喝100瓶可乐") def la(): print("⼤牛不⽤拉") def shui(): print("⼤牛⼀次睡一年")
接下来,到你了。你要去一个一个调用。但是在你调用之前,大牛告诉你,他写了哪些方法,那现在就可以这么办了:
while 1: print(''' 作为大牛,我帮你写了 chi he la shui 等功能,你自己看着办吧... ''') func = input('请输入你要测试的功能:').strip() if func == 'chi': daniu.chi() elif func == 'he': daniu.he() elif func == 'la': daniu.la() elif func == 'shui': daniu.shui() else: print('大牛就这几个功能,别搞事情!')
这样写是写完了,但是...
如果大牛写了100个功能怎么办,你要写100个if判断吗?太累了吧,现有的知识解决不了这个问题呀,那怎么办?
我们可以使用反射来完成这样的功能,非常简单,我们现在可以获取要执行的功能,只不过我们使用input()函数获取的一个字符串,这个字符串和实际模块中的函数名是一样的。那我们就可以利用这一点,只要通过字符串动态的访问模块中的功能就可以了,反射就是做这个事情的。
什么是反射?
之前我们导入模块都是,先引入模块,然后通过模块去访问否个我们要用的功能,现在呢?我们手动输入要运行的功能,然后拿着这个功能去模块里查找,这就叫反射。
通俗点说就是通过字符串的形式操作对象相关的属性。Python中的一切事物都是对象(都可以使用反射)
我们首先来看一下,在Python中使用反射如何解决上面的问题吧。
import daniu while 1: print(''' 作为大牛,我帮你写了 chi he la shui 等功能,你自己看着办吧... ''') func_str = input('请输入你要测试的功能:').strip() if hasattr(daniu, func_str): func = getattr(daniu, func_str) func() else: print('大牛就这几个功能,别搞事情!')
上面的代码中用到了如下两个方法:
hasattr(对象, 字符串)是用来判断对象是否有字符串名称对应的这个属性(功能)。
getattr(对象,字符串)是用来获取对象中字符串名称对应的属性(功能)。
因为Python中一切皆对象,所以反射这个特性非常的有用,很多框架中都会用到此特性。
反射应用
接下来,我们先看个简单的例子:
class Person: country = "China" def eat(self): pass # 类中的内容可以这样动态的进⾏获取 print(getattr(Person, "country")) print(getattr(Person, "eat")) # 相当于Foo.func 函数 # 对象⼀样可以 obj = Person() print(getattr(obj, "country")) print(getattr(obj, "eat")) # 相当于obj.func ⽅法
getattr可以从模块中获取内容,也可以从类中获取内容,也可以从对象中获取内容。又因为在Python中⼀切皆为对象,所以把反射理解为从对象中动态的获取成员。
反射的四个函数
关于反射, 其实⼀共有4个函数:
- hasattr(obj, str) 判断obj中是否包含str成员
- getattr(obj,str) 从obj中获取str成员。
- setattr(obj, str, value) 把obj中的str成员设置成value。这⾥的value可以是值,也可以是函数或者⽅法。
- delattr(obj, str) 把obj中的str成员删除掉。
class Foo: pass f = Foo() print(hasattr(f, 'eat')) # False setattr(f, 'eat', "123") print(f.eat) # 被添加了⼀个属性信息 setattr(f, "eat", lambda x: x + 1) print(f.eat(3)) # 4 print(f.eat) # 此时的eat既不是静态⽅法, 也不是实例⽅法, 更不是类⽅法. 就相当于你在类中写了个self.chi = lambda 是⼀样的 print(f.__dict__) # {'eat': <function <lambda> at 0x1015a2048>} delattr(f, "eat") print(hasattr(f, "eat")) # False
注意:以上操作都是在内存中进⾏的,并不会影响你的源代码。
补充importlib
importlib是一个可以根据字符串的模块名实现动态导入模块的库。
举个例子:
目录结构:
├── aaa.py
├── bbb.py
└── mypackage
├── __init__.py
└── xxx.py
使用importlib动态导入模块:
bbb.py
import importlib func = importlib.import_module('aaa') print(func) func.f1() m = importlib.import_module('mypackage.xxx') print(m.age)
类的其他成员
列举类中的其他常见成员。
__str__
改变对象的字符串显示。可以理解为使用print函数打印一个对象时,会自动调用对象的__str__方法。
class Student: def __init__(self, name, age): self.name = name self.age = age # 定义对象的字符串表示 def __str__(self): return self.name s1 = Student('张三', 24) print(s1) # 会调用s1的__str__方法
__repr__
在python解释器环境下,会默认显示对象的repr表示。
>>> class Student: ... def __init__(self, name, age): ... self.name = name ... self.age = age ... def __repr__(self): ... return self.name ... >>> s1 = Student('张三', 24) >>> s1 张三
总结:
str函数或者print函数调用的是obj.__str__()
repr函数或者交互式解释器调用的是obj.__repr__()
注意:
如果__str__没有被定义,那么就会使用__repr__来代替输出。
__str__和__repr__方法的返回值都必须是字符串。
__format__
class Student: def __init__(self, name, age): self.name = name self.age = age # 定义对象的字符串表示 def __str__(self): return self.name def __repr__(self): return self.name __format_dict = { 'n-a': '{obj.name}-{obj.age}', # 姓名-年龄 'n:a': '{obj.name}:{obj.age}', # 姓名:年龄 'n/a': '{obj.name}/{obj.age}', # 姓名/年龄 } def __format__(self, format_spec): """ :param format_spec: n-a,n:a,n/a :return: """ if not format_spec or format_spec not in self.__format_dict: format_spec = 'n-a' fmt = self.__format_dict[format_spec] return fmt.format(obj=self) s1 = Student('张三', 24) ret = format(s1, 'n/a') print(ret) # 张三/24