23--面向对象05:类相关函数、反射、内置方法、元类
0 类相关的内置函数
# https://www.cnblogs.com/liuqingzheng/articles/9949568.html
# 1 issubclass # 判断第一个类是不是第二个类的子类
class Animal:
pass
class Person(Animal):
pass
res = issubclass(Person, Animal)
print(res)
# 2 type 类 查看一个对象是哪个类的对象
res=type(1) # int
res=type(Animal) # Animal这个类是type这个元类的对象
res=type(type) # type这个类是type类的自身对象
print(res)
# 3 isinstance() 判断一个对象是不是一个类的对象(包含父类)
a=Animal()
res=isinstance(a,Animal) # True
res=isinstance(a,Person) # False
p=Person()
res=isinstance(p,Person) # True
res=isinstance(p,Animal) # True
print(res)
1 反射机制
1.1 反射介绍
# 反射:
指的是 在程序运行过程中 可以"动态"获取对象的信息
动态: 通过字符串的形式 获取对象的属性或方法,动态的设置 对象的属性或方法
1.2 反射实现
# 1.先通过多dir:查看出某一个对象下可以.出哪些属性来
print(dir(obj)) # ['__dir__','name'...]
# 2.可以通过字符串反射到真正的属性上,得到属性值
print(obj.__dict__[dir(obj)[-2]]) # 'lqz'
# 四个内置函数的使用: 通过字符串来操作属性值
hasattr(obj, str) # 判断obj中是否包含str属性
getattr(obj, str) # 从obj中获取str属性
setattr(obj, str, value) # 把obj中的str属性设置成value 这⾥的value可以是值,也可以是函数或者⽅法
delattr(obj, str) # 把obj中的str属性删除掉
# 动态判断 属性或方法是否在对象中 不存在,则抛异常
class Person:
def run(self):
print('run')
p=Person()
res=hasattr(p,'name')
res=hasattr(p,'run')
print(res)
# 动态获取
class Person:
def run(self):
print('run')
p = Person()
p.name='lqz'
p.age='19'
p.hobby='篮球'
s=input('请输入你要执行的方法:')
res=getattr(p,s)
res()
# 动态修改
class Person:
def run(self):
print('run')
def speak():
print('说话')
p=Person()
p.run()
setattr(p,'run',speak)
p.run()
# 动态删除
class Person:
def run(self):
print('run')
p=Person()
p.run()
delattr(p,'run')
p.run()
# 应用场景
class Person:
def run(self):
print('run')
p = Person()
if hasattr(p, 'run1'):
run = getattr(p, 'run1')
run()
2 类的内置方法
# 内置方法(魔法方法):
定义在类内部,以'__名字__'的方法 是绑定给对象的方法
特点:会在某种情况下自动触发执行
1 __str__ # 打印对象会触发 返回值(必须是字符串类型)
2 __repr__ # 在命令窗口下,直接输出对象时,触发它的执行,类似于__str__
3 __del__ # del 对象,对象回收的时候触发
4 __item__系列 # '[]'拦截 对象是否可以 '[]' 操作属性
5 __attr__系列 # '.'拦截 对象是否可以 '.' 操作属性
6 __enter__和__exit__ # 上下文管理器
7 __eq__ # 两个对象比较
8 __init__ # 类实例化会触发
9 __new__ # 类实例化会触发,它比__init__早(造出裸体的人,__init__穿衣服)
10 __call__ # 类实例化后的对象()触发
类也是对象 类(),类的实例化过程调用元类的__call__
# 总结:
__init__ 和__new__ :是自身调用时 触发运行
__call__ :是自身调用完后的对象 触发运行
###########具体详解
# __init__: 类加括号(实例化)执行,触发它
class Person:
def __init__(self,name,age):
# 完成初始化功能,往对象中放属性
self.name1=name
self.age=age
p=Person('lqz',18)
print(p.name1)
# __str__:print(对象) 时,触发对象的 __str__
class Person:
pass
def __str__(self):
return self.name
p = Person()
p.name='lqz'
print(p) # 'lqz'
# __repr__ :在命令窗口下,直接输出对象时,触发它的执行,类似于__str__
# __del__ 析构方法,当对象在内存中被释放时,自动触发执行。
class Person:
def __del__(self):
print('我被删除了')
p=Person()
del p
# __attr__系列 对象.属性的时候触发
# __getattr__
# __setattr__
# __delattr__
# __item__系列 对象[]时,触发它执行
# __getitem__
# __setitem__
# __delitem__
class Person:
def __getitem__(self, item):
print(item)
return 'xxx'
def __delitem__(self, key):
print('我被删了')
p=Person()
print(p['name']) # 'name' 'xxx'
del p['name'] # '我被删了'
3 元类
引入:一切都源自于一句话:python中一切皆为对象
3.1 元类介绍
# 什么是元类?
元类就是用来实例化产生类的类
关系:元类(type)---实例化---->类(People)---实例化---->对象(obj)
class People:
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s:%s' %(self.name,self.age))
print(People.__dict__)
# 如何得到对象
obj=调用类()
obj=People('egon',18)
print(type(obj)) # People
# 如果说类也是对象
People=调用元类(...)
print(type(People)) # type
# 查看内置的元类:
1.type是内置的元类
2.我们用class关键字定义的所有类 以及内置的类 都是由元类type实例化产生
print(type(People))
print(type(int))
3.2 class关键字创造类的流程分析
# 类有三大特征:
# 1.类名
class_name="People"
# 2.类的基类(父类)
class_bases=(object,)
# 3.执行类体代码,拿到类的名称空间
class_dic={} # 类的名称空间
class_body="""
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s:%s' %(self.name,self.name))
"""
exec(class_body,{},class_dic)
print(class_dic)
# 4.调用元类 依次传入以上三个参数
People=type(class_name,class_bases,class_dic)
3.3 自定义元类来控制类的创建
# 如何自定义元类:
通过继承type类
# 如何给类 指定元类:
使用metaclass关键字参数为一个类指定元类
# 注意:
1.一个类没有声明自己的元类,默认它的元类就是type
2.只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
# eg:
class Mymeta(type):
# 四个参数:空对象,"People",(),{...}
def __init__(self,x,y,z):
print(self)
print(x)
print(y) # ()
print(z)
# if not x.istitle():
# raise NameError('类名的首字母必须大写啊!!!')
# if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:
# raise TypeError('类中必须有文档注释,并且文档注释不能为空')
# 当前所在的类,调用类时所传入的参数
def __new__(cls, *args, **kwargs):
# 造Mymeta的对象
print('run1111111111.....')
# print(cls,args,kwargs)
# return super().__new__(cls,*args, **kwargs)
return type.__new__(cls,*args, **kwargs)
class People(metaclass=Mymeta):
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s:%s' %(self.name,self.name))
# 类的产生过程 (class机制 四件事) 就是元类的调用过程
People=Mymeta("People",(object,),{...}) # 类名、基类、名称空间
# 调用Mymeta发生三件事,调用Mymeta就是触发type类.__call__()
1.先造一个空对象=>People, 调用Mymeta类内的__new__()方法
2.调用Mymeta这个类内的__init__方法,完成初始化对象的操作
3.返回初始化好的对象
# 强调:
只要是调用类,那么会一次调用
1.类内的__new__ # 产生空对象
2.类内的__init__ # 初始化对象
3.4 __call__方法
# __call__() 方法
class Foo:
def __init__(self,x,y):
self.x=x
self.y=y
# obj,1,2,3,a=4,b=5,c=6
def __call__(self,*args,**kwargs):
print('===>',args,kwargs)
return 123
obj=Foo(111,222)
print(obj) # obj.__str__
res=obj(1,2,3,a=4,b=5,c=6) # res=obj.__call__()
print(res) # 123
应用:如果想让一个对象可以加括号调用,需要在该对象的类中添加一个方法__call__
# 总结:
对象() -> 类内的__call__
类() -> 自定义元类内的__call__
自定义元类() -> 内置元类__call__
3.5 自定义元类控制类的调用
class Mymeta(type):
# 空对象,"People",(),{...}
def __init__(self, x, y, z):
print('元类调用Mymeta的__init__')
def __new__(cls, *args, **kwargs):
print('元类调用Mymeta的__new__')
return super().__new__(cls, *args, **kwargs)
class People(metaclass=Mymeta):
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print('%s:%s' % (self.name, self.name))
# 类的产生
# People=Mymeta()=》type.__call__=>干了3件事
1.type.__call__函数内会先调用Mymeta内的__new__
2.type.__call__函数内会调用Mymeta内的__init__
3.type.__call__函数内会返回一个初始化好的对象
class Mymeta(type): # 只有继承了type类的类才是元类
def __call__(self, *args, **kwargs):
# 1.Mymeta.__call__函数内会先调用People内的__new__
people_obj=self.__new__(self)
# 2.Mymeta.__call__函数内会调用People内的__init__
self.__init__(people_obj,*args, **kwargs)
# print('people对象的属性:',people_obj.__dict__)
# eg:自定义元类Mymeta控制 People类对象的所有属性 都变成私有的
people_obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()}
# 3.Mymeta.__call__函数内会返回一个初始化好的对象
return people_obj
class People(metaclass=Mymeta):
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print('%s:%s' %(self.name,self.name))
def __new__(cls, *args, **kwargs):
# 产生真正的对象
return object.__new__(cls)
# 类的调用
# obj=People('egon',18) =》Mymeta.__call__=》干了3件事
1.Mymeta.__call__函数内会先调用People内的__new__
2.Mymeta.__call__函数内会调用People内的__init__
3.Mymeta.__call__函数内会返回一个初始化好的对象
obj1=People('egon',18)
obj2=People('egon',18)
# print(obj)
print(obj1.__dict__)
print(obj2.__dict__)
3.6 元类下的属性查找
# 属性查找的原则:对象-》类-》父类...-》自定义元类-》元类 切记:父类 不是 元类
# 属性查找应该分成两层: 先继承,再元类
一层是 对象层 (基于c3算法的MRO)的查找
一层是 类层 (即元类层)的查找
# eg: 分析元类Mymeta中__call__里的self.__new__的查找
在StanfordTeacher、Foo、Bar里都没有找到__new__的情况下
会去找object里的__new__,而object下默认就有一个__new__
class Mymeta(type):
n=444
# self=<class '__main__.StanfordTeacher'>
def __call__(self, *args, **kwargs):
obj=self.__new__(self) # StanfordTeacher.__new__
# obj=object.__new__(self)
# print(self.__new__ is object.__new__) # True
self.__init__(obj,*args,**kwargs)
return obj
class Bar(object):
# n=333
# def __new__(cls, *args, **kwargs):
# print('Bar.__new__')
pass
class Foo(Bar):
# n=222
# def __new__(cls, *args, **kwargs):
# print('Foo.__new__')
pass
class StanfordTeacher(Foo,metaclass=Mymeta):
# n=111
def __init__(self,name,age):
self.name=name
self.age=age
obj=StanfordTeacher('lili',18)
print(obj.__dict__)
# print(obj.n)
# print(StanfordTeacher.n)
3.7 元类案例
3.7.1 元类中控制 类的对象 的属性全部为隐藏属性
class Mymeta(type):
def __call__(self, *args, **kwargs):
people_obj = self.__new__(self)
self.__init__(people_obj, *args, **kwargs)
my_dic = {'_%s__%s' % (self.__name__,k): v for k, v in people_obj.__dict__.items()}
people_obj.__dict__ = my_dic
return people_obj
class People(metaclass=Mymeta):
def __init__(self, name, age):
self.name = name
self.age = age
obj1 = People(name='egon', age=18)
print(obj1.__dict__)
print(obj1.age)
3.7.2 元类中控制 类无需__init__方法
# 思路:
元类帮其完成创建对象,以及初始化操作;
# 要求
1.实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument
2.key作为用户自定义类产生对象的属性,且所有属性变成大写
3.类的数据属性都变成大写
class Mymetaclass(type):
def __new__(cls,name,bases,attrs):
update_attrs={}
for k,v in attrs.items(): # 类的属性
if not callable(v) and not k.startswith('__'):
update_attrs[k.upper()]=v
else:
update_attrs[k]=v
return type.__new__(cls,name,bases,update_attrs)
def __call__(self, *args, **kwargs):
if args:
raise TypeError('must use keyword argument for key function')
obj = self.__new__(self) # 创建对象,self为类Chinese
# obj = object.__new__(self)
for k,v in kwargs.items(): # 对象的属性
obj.__dict__[k.upper()]=v
return obj
class Chinese(metaclass=Mymetaclass):
country='China'
tag='Legend of the Dragon' #龙的传人
def walk(self):
print('%s is walking' %self.name)
p=Chinese(name='lili',age=18)
print(p.__dict__) # {'NAME':'lili', 'AGE':18}
补充
exec的用法
# exec:三个参数
参数一:包含一系列python代码的字符串
参数二:全局作用域(字典形式),如果不指定,默认为globals()
参数三:局部作用域(字典形式),如果不指定,默认为locals()
# 可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
g={
'x':1,
'y':2
}
l={}
exec('''
global x,z
x=100
z=200
m=300
''',g,l)
print(g) # {'x': 100, 'y': 2,'z':200,......}
print(l) # {'m': 300}