反射、魔法方法、元类
反射的实战案例
案例1
加载配置文件纯大写的配置(配置文件加载:获取配置文件中所有大写的配置 小写的直接忽略 组织成字典)
import settings
new_dic = {}
for i in dir(settings):
if i.isupper():
new_dic[i] = getattr(settings,i)
print(new_dic)
总结:
1.dir(obj) :获取对象中可以调用的名字,以列表的形式返回
2.熟悉:在python中,一切皆对象(文件,模块...)
案例2
模拟操作系统cmd终端执行用户命令
class Mywindows(object):
def dir(self):
print('dir获取当前目录下所有的文件名称')
def ls(self):
print('ls获取当前路径下所有的文件名称')
def ipconfig(self):
print('ipconfig获取当前计算机的网卡信息')
obj = Mywindows()
while True:
cmd = input("请输入你的指令>>>:").strip()
if hasattr(obj,cmd):
name_cmd = getattr(obj,cmd)
name_cmd()
else:
print(f"{cmd}没有这个指令哦")
面向对象的魔法方法
魔法方法其实就是类中定于的双下方法。被称为魔法方法的原因就是这些方法都是到达某个条件自动触发,不需要调用,例如:双下init方法在给对象设置独有的数据的时候,就会自动触发(实例化)。
1.str(self)
对象在被执行打印操作的时候会自动触发,该方法必须返回一个字符串,打印的时候,返回的是什么就打印什么(前提必须是字符串类型)
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
obj = Student("nana",18)
print(obj) # <__main__.Student object at 0x0000026135F772B0>
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
print("__str__")
# return f'这是对象:{self}' # 报错,进行了递归
return f'这是对象的名字:{self.name}'
obj = Student("nana",18)
print(obj) # __str__ 这是对象的名字:nana
2.call(self, *args, **kwargs)
只要对象加括号调用,就会触发该方法,括号里面可以写位置实参,关键字实参
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
obj = Student("nana",18)
obj() # 报错 TypeError: 'Student' object is not callable
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def __call__(self, *args, **kwargs):
print("__call__")
print(args)
print(kwargs)
obj = Student("nana",18)
obj(1,2,name='xiao') # __call__ (1, 2) {'name': 'xiao'}
3.getattr(self, item)
只要当对象获取一个不存在的属性名,就会自动触发。该方法返回的是:return后面写的什么就返回什么,一般可以提示某个属性名不存在。形参item就是对象想要获取的不存在的属性名
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
obj = Student("nana",18)
print(obj.name) # nana
print(obj.hobby) # 报错 AttributeError: 'Student' object has no attribute 'hobby'
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def __getattr__(self, item):
print("__getattr__")
return '您想要获取的属性名:%s不存在'%item
obj = Student("nana",18)
print(obj.name) # nana
print(obj.hobby) # __getattr__ 您想要获取的属性名:hobby不存在
4. setattr(self, key, value)
当对象操作属性的时候会自动触发 eg:对象.属性名= 属性值
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def __getattr__(self, item):
print("__getattr__")
return '您想要获取的属性名:%s不存在'%item
obj = Student("nana",18)
print(obj.name) # nana
print(obj.age) # 18
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def __getattr__(self, item):
print("__getattr__")
return '您想要获取的属性名:%s不存在'%item
def __setattr__(self, key, value):
print("__setattr__")
print(key)
print(value)
obj = Student("nana",18) # __setattr__ name nana __setattr__ age 18
print(obj.__dict__) # {}
print(obj.name) # __setattr__ name nana __setattr__ age 18 __getattr__ 您想要获取的属性名:name不存在
print(obj.age) # __setattr__ name nana __setattr__ age 18 __getattr__ 您想要获取的属性名:age不存在
obj.name = 'xiao' # __setattr__ name nana __setattr__ age 18 __setattr__ name xiao
ps:出现以上情况原因:
1.obj = Student("nana",18) >>>在__init__中也有对象操作属性的时候(self.name = name),说以会触发__setattr__
2.print(obj.__dict__),返回空字典,是因为Student类中,默认继承object父类,那object父类中本来就有__setattr__方法(对象操作属性),然而我们在重写了_setattr__方法,却没有任何操作,所以返回{}是理所应当的
解决:用super()继承父类
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def __getattr__(self, item):
print("__getattr__")
return '您想要获取的属性名:%s不存在'%item
def __setattr__(self, key, value):
super().__setattr__(key,value)
obj = Student("nana",18)
print(obj.name) # nana
5. del(self)
对象在被删除的时候会自动触发。删除包含主动删除和被动删除,主动删除就是执行删除操作,被动删除就是代码运行完毕,文件执行结束,释放内存,进行被动删除
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def __del__(self):
print("__del__")
obj = Student("nana",18)
del obj # __del__
6.getattribute(self, item)
对象获取属性的时候自动触发,无论这个属性存不存在。当类中既有__getattr__,又有__getattribute__的时候,只会执行__getattribute__
class Student:
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def __getattribute__(self, item):
print("getattribute__")
obj = Student("nana",18)
print(obj.name) # getattribute__ None
print(obj.hobby) # getattribute__ None
class Student(object):
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def __getattribute__(self, item):
print("getattribute__")
return super(Student, self).__getattribute__(item)
obj = Student("nana",18)
print(obj.name) # getattribute__ nana
print(obj.hobby) # getattribute__
7.enter(self) 、exit(self, exc_type, exc_val, exc_tb)
enter(self):对象被with语法执行的时候自动触发,该方法返回什么,as关键字后面的变量名就能得到什么;exit(self, exc_type, exc_val, exc_tb):对象被with语法执行并运行完with子代码之后,自动触发
class Student(object):
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
obj = Student("nana",18)
with obj as f:
pass # 报错 AttributeError: __enter__
class Student(object):
school = '清华'
def __init__(self,name,age):
self.name = name
self.age = age
def __enter__(self):
print('__enter__')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("__exit__")
obj = Student("nana",18)
with obj as f:
print(f) # >>>__enter__ <__main__.Student object at 0x00000152B76C7358> __exit__
魔法方法练习
补全以下代码 执行之后不报错
class Context:
pass
with Context() as f:
f.do_something()
解题
class Context:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def do_something(self):
pass
with Context() as f:
f.do_something()
元类
元类的简介
1.基础阶段我们使用type来查看数据的数据类型,学习了面向对象之后,发现查看的不是数据类型,而是数据类型所属的类
l1 = [1,2,3,4]
print(type(l1)) # <class 'list'>
s1 ={1,2,3,4}
print(type(s1)) # <class 'set'>
2.我们定义的数据类型 其实本质还是通过各个类产生对象
class str:
pass
s1 = 'naan' >>>相当于:s1 = str('naan')
3.type也可以被理解为用于查看产生当前对象的类是谁
class Myclass:
pass
obj = Myclass()
print(type(obj)) # <class '__main__.Myclass'>
print(type(Myclass)) # <class 'type'>
总结:自定义的类都是由type类产生的,我们将产生类的类称之为元类
产生类的两种方式
1.class关键字
class 类名:
类体代码
2.利用元类type
type(name, bases, dict) -> a new type
即:type(类名、类的父类、类的名称空间)
type("Myclass",(),{})
学习元类其实就是掌握了类的产生过程,我们就可以在类的产生过程中高度定制化类的行为
类的基本使用
只有继承了type的类才可以称之为是元类;如果想要切换产生类的元类,不能使用继承,必须使用关键字metaclass声明
class MyMetaClass(type):
pass
class Myclass(metaclass = MyMetaClass):
pass
推论:类中的__init__用于实例化对象,元类中的__init__用于实例化类
class MyMetaClass(type):
def __init__(self,what, bases=None, dict=None):
print("你好")
print('what', what) # 类名 what Myclass
print('bases', bases) # 类的父类 bases ()
print('dict', dict) # 类的名称空间 dict {'__module__': '__main__', '__qualname__': 'Myclass'}
super().__init__(what, bases, dict)
class Myclass(metaclass = MyMetaClass):
pass
需求:类名必须首字母大写
class MyMetaClass(type):
def __init__(self,what, bases=None, dict=None):
if not what.istitle():
raise Exception('首字母大写')
super().__init__(what, bases, dict)
class Myclass(metaclass = MyMetaClass):
pass
元类的进阶
元类不单单可以控制类的产生过程,其实也可以控制对象的
对象加括号执行产生该对象类里面的双下call方法
class Myclass:
def __init__(self,name,age):
self.name = name
self.age = age
def __call__(self, *args, **kwargs):
print("__call__")
obj = Myclass('nana',18)
obj() # __call__
推导出:类加括号执行产生该类的元类里面的双下call方法
class Father_Father(type):
def __call__(self, *args, **kwargs):
print('__call__')
print(args)
print(kwargs)
super().__call__(*args, **kwargs)
class Myclass(metaclass= Father_Father):
def __init__(self,name,age):
self.name = name
self.age = age
print('__init__')
obj = Myclass('nana',18) # __call__ ('nana', 18) {} __init__
总结:如果我们想高度定制对象的产生过程,可以操作元类里面的双下call方法,如果我们想要高度定制类的产生过程,可以操作元类里面的双下init方法
练习
需求:实例化对象 所有的参数都必须采用关键字参数的形式
class Father_Father(type):
def __call__(self, *args, **kwargs):
if args:
raise Exception('不能有位置参数')
super().__call__(*args, **kwargs)
class Myclass(metaclass= Father_Father):
def __init__(self,name,age):
self.name = name
self.age = age
print('__init__')
obj = Myclass(name = 'nana',age = 18) # __init__
print(obj.name)
双下new方法
1.类产生的步骤
1.1 产生一个空对象 >>>self.__new__(*args.**kwargs)
1.2 自动触发__init__方法实例化对象 >>> super().__call__(*args, **kwargs)
1.3 返回实例化好的对象 >>>给对象添加属性
2.__new__方法专门用于产生空对象
__init__方法专门用于给对象添加属性
作业
自定义字典并且让你字典具备d.key = value 修改键值对;d.key = value 添加键值对
class MyDict(dict):
def __setattr__(self, key, value):
super(MyDict, self).__setattr__(key, value)
self[key] = value # d['name']='nana'
d = MyDict({'name': 'cx', 'age': 18})
d.name = 'nana'
print('d.name:', d.name) # d.name: nana
print('d:',d) # d: {'name': 'nana', 'age': 18}
d.age2=22
print(d) # {'name': 'nana', 'age': 18, 'age2': 22}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!