今日内容概要
- 反射实战案例
- 面向对象的魔法方法(双下方法)
- 魔法方法实战演练
- 元类简介
- 创建类的两种方式
- 元类的实际应用
- 元类之双下new方法
反射实战案例
1.实战一:加载配置文件纯大写的配置
# 配置文件加载:获取配置文件中所有大写的配置 小写的直接忽略 组织成字典 import settings new_dict = {} # print(dir(settings)) # dir获取括号中对象可以调用的名字 # ['AGE', 'INFO', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'desc', 'name'] for i in dir(settings): if i.isupper(): # 如果名字是纯大写 那么获取该大写名字对应的值 'AGE' 'INFO' v = getattr(settings, i) new_dict[i] = v print(new_dict)
2.实战二:模拟操作系统cmd终端执行用户命令
class WinCmd(object): def dir(self): print('dir获取当前目录下所有的文件名称') def ls(self): print('ls获取当前路径下所有的文件名称') def ipconfig(self): print('ipconfig获取当前计算机的网卡信息') obj = WinCmd() while True: cmd = input('请输入您的命令>>>:') if hasattr(obj, cmd): cmd_name = getattr(obj, cmd) cmd_name() else: print('%s 不是内部或外部命令,也不是可运行的程序或批处理文件' % cmd)
面向对象魔法方法
1.魔法方法概念
魔法方法就是类中定义双下方法,之所以会叫魔法方法原因是这些方法都是到达某个条件自动触发,无需调用。
eg:__init__方法再给对象设置独有数据的时候自动触发(实例化)
2.常见的魔法方法及触发条件
(1)__init__
class MyClass(object): def __init__(self,name): """实例化对象的时候自动触发""" print('__init__方法') obj = MyClass
(2)__str__
class MyClass(object): def __str__(self): """ 对象被执行打印操作的时候会自动触发 该方法必须返回一个字符串 返回什么字符串打印对象之后就展示什么字符串 """ # print('__str__方法') print('这是类:%s 产生的一个对象') # return '对象:%s'%self # return '对象:%s'%self return '' obj = MyClass() print(obj)
(3)__call__
class MyClass(object): def __call__(self, *args, **kwargs): """对象加括号调用 自动触发该方法""" print('__call__方法') print(args) print(kwargs) obj = MyClass() obj(1,2,name='jaosn',age = 18) ''' __call__方法 (1, 2) {'name': 'jaosn', 'age': 18} '''
(4)__getattr__
class MyClass(object): def __getattr__(self, item): """当对象获取一个不存在的属性名 自动触发 该方法返回什么 对象获取不存在的属性名就会得到什么 形参item就是对象想要获取的不存在的属性名 """ print('__getattr__', item) return '您想要获取的属性名:%s不存在' % item obj = MyClass() print(obj.age) ''' __getattr__ age 您想要获取的属性名:age不存在 '''
(5)__setattr__
class MyClass(object): def __init__(self,name): """实例化对象的时候自动触发""" print('__init__方法') self.name = name def __setattr__(self, key, value): """对象操作属性值的时候自动触发>>>: 对象.属性名=属性值""" print("__setattr__") print(key) print(value) # super().__setattr__(key, value) __str__重写之后才需要写这个 obj = MyClass('jason') print(obj.__dict__) # {'name': 'jason'} obj.name = 'kevin' print(obj.__dict__) # {'name': 'kevin'}
(6)__del__
class MyClass(object): def __del__(self): """对象在被删除(主动 被动)的时候自动触发""" print('__del__') obj = MyClass() del obj # __del__
(7)__gatattribute__
class MyClass(object): def __getattribute__(self, item): print('__getattribute__') """对象获取属性的时候自动触发 无论这个属性存不存在 当类中既有__getattr__又有__getattribute__的时候 只会走后者 """ # 补充之回到__getattr__方法: return super(MyClass, self).__getattribute__(item) 复杂写法 return super().__getattribute__(item) # 简便写法 obj = MyClass() obj.name # __getattribute__ print(obj.name) # None
(8)__enter__与__exit__
class MyClass(object): def __enter__(self): """对象被with语法执行的时候自动触发 该方法返回什么 as关键字后面的变量名就能得到什么""" print('__enter__') return 123 def __exit__(self, exc_type, exc_val, exc_tb): """对象被with语法执行并运行完with子代码之后 自动触发""" print('__exit__') obj = MyClass() with obj as f: pass print(f) # 123 ''' __enter__ __exit__ '''
3.表格说明
项目 Value __ init__ 对象实例化的时候自动触发 __ str__ 对象被执行打印(print、前端展示)操作的时候自动触发,该方法必须返回字符串类型的数据,很多时候用来更加精准的描述对象 __ del__ 对象被执行(被动、主动)删除操作之后自动执行 __ getattr__ 对象查找不存在名字的时候自动触发 __ setattr__ 对象在执行添加属性操作的时候自动触发 >>> obj.变量名=变量值 __ call__ 对象被加括号调用的时候自动触发 __ enter__ 对象被执行with上下文管理语法开始自动触发 ,该方法返回什么as后面的变量名就会得到什么 __ exit__ 对象被执行with上下文管理语法结束之后自动触发 __ getattribute__ 只要对象查找名字无论名字是否存在都会执行该方法,如果类中有__getattribute__方法 那么就不会去执行__getattr__方法 __ new__ 在类被实例化前,最先被执行的方法,主要用来去创建一个完全空白的对象
魔法方法笔试题
需求:补全下面代码,执行之后不报错
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.元类的引出
(1)基础阶段我们使用type来查找数据的数据类型;
(2)但是学了面向对象之后,发现查看的不是数据类型,而是数据所属的类;s1 = '哈哈哈 今天下午终于可以敲代码了!!!' l2 = [60, 80, 100, 120, 150, 200] d = {'name': '死给我看', 'age': 18} print(type(s1)) # <class 'str'> print(type(l2)) # <class 'list'> print(type(d)) # <class 'dict'>
(3)而是数据所属的类型,其实本质还是通过各个类产生了对象。
class str: pass h = 'hello' str('hello')
(4)我们可以理解为type用于查看产生当前对象的类是谁
class MyClass: pass obj = MyClass() print(type(obj)) # 查看产生对象obj的类:<class '__main__.MyClass'> print(type(MyClass)) # 查看产生对象MyClass的类:<class 'type'>
(5)通过上述推导,得出最后结论,自定义的类都是由type类产生的,我们将产生类的类称之为“元类!!!”
产生类的两种方式
1.方式一:class关键字
class MyClass: pass
2. 利用元类type
type(类名,类的父类,类的名称空间)
class MyClass1: pass print(MyClass1.__dict__,MyClass1) # {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'MyClass1' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass1' objects>, '__doc__': None} <class '__main__.MyClass1'> res = type('MyClass2',(),{}) print(res.__dict__,res) # {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'MyClass2' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass2' objects>, '__doc__': None} <class '__main__.MyClass2'> obj = res() print(obj) # <__main__.MyClass2 object at 0x00000209EA66D390>
3.类的补充
学习元类其实就是掌握了类的产生过程,我们就可以在类的产生过程中高度定制化类的行为。
eg:
类名必须首字母大写
上述需求就需要使用元类来控制类的产生过程 在过程中校验
元类的基本使用
1.自定义指定元类
class MyMetaClass(type): pass """只有继承了type的类才可以称之为是元类""" class MyClass(metaclass=MyMetaClass): pass """如果想要切换产生类的元类不能使用继承 必须使用关键字metaclass声明"""
2.元类中双下init用于实例化类
类中的__init__用于实例化对象;元类中__init__用于实例化类.
class MyMetaClass(type): def __init__(self, what, bases=None, dict=None): # print('别晕') # print('what', what) 类名 # print('bases', bases) 类的父类 # print('dict', dict) 类的名称空间 if not what.istitle(): # print('首字母必须大写 你会不会写python 面向对象学过吗 lowB') raise Exception('首字母必须大写 你会不会写python 面向对象学过吗 lowB') super().__init__(what, bases, dict) class Aaa(metaclass=MyMetaClass): pass # 没有报错,说明实例化类成功
元类进阶
元类不单单可以控制类的产生过程,其实也可以控制对象的!!!
(1)对象加括号执行产生该对象类里面的双下call
(2)类加括号执行产生该类的元类里面的双下call
回想__call__方法:
对象加括号会自动执行产生该对象的类里面的__call__,并且该方法返回什么对象加括号就会得到什么
推导:类加括号会执行元类的里面的__call__该方法返回什么其实类加括号就会得到什么class MyMetaClass(type): def __call__(self, *args, **kwargs): print('__call__') if args: raise Exception('必须用关键字参数传参') super().__call__(*args, **kwargs) class MyClass(metaclass=MyMetaClass): def __init__(self, name, age): self.name = name self.age = age print('__init__') # 需求:实例化对象 所有的参数都必须采用关键字参数的形式 obj = MyClass('jason', 18) # obj = MyClass(name='jason', age=18)
总结:
如果我们想高度定制对象的产生过程:可以操作元类里面的__call__。
如果我们想高度定制类的产生过程:可以操作元类里面的__init__。
魔法方法之双下new方法
1.类产生对象的步骤
(1)产生一个空对象
(2)自动触发__init__方法实例化对象
(3)返回实例化好的对象
2. __new__和__init__
__new__方法专门用于产生空对象 骨架
__init__方法专门用于给对象添加属性 血肉
作业展示
1.自定义字典并且让字典具备
d.key = value 修改键值对
d.key = value 添加键值对
class MyClass(dict):
def __setattr__(self, key, value):
self[key] = value
super().__setattr__(key, value)
obj = MyClass()
obj.name = 'k'
obj.age = 18
obj.gender = 'male'
print(obj)
'''
{'name': 'k', 'age': 18, 'gender': 'male'}
'''