python34-面向对象魔法方法-元类-

今日学习

反射实战案例

面向对象魔法

魔法方法笔试题

元类介绍

产生类的两种方式

元类的基本使用

元类进阶

双下new

反射实战练习

  • 反射是实战1

  • 需求: 配置文件加载:获取配置文件中所有大写的配置 小写的直接忽略 组织成字典

例子:准备工作

同级目录创建一个settings 文件

name = 'jason'
INFO = '小八嘎'
desc = '米莱迪偷塔'
AGE = 18

随便新建一个py文件,对settings进行取值。

import settings
new_dict = {}
for i in dir(settings):
    if i.isupper():  # 如果名字是纯大写 那么获取该大写名字对应的值   'AGE'   'INFO'
        v = getattr(settings, i) 
        new_dict[i] = v 
print(new_dict)
------------执行结果-------------
{'AGE': 18, 'INFO': '小八嘎'}
  • 反射实战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)
        
-------------执行结果---------
请输入您的命令>>>:dasd
dasd 不是内部或外部命令,也不是可运行的程序或批处理文件
请输入您的命令>>>:ls
ls获取当前路径下所有的文件名称
请输入您的命令>>>:

面向对方魔法方法、

  • 定义

    所谓的魔法方法就是类中定义的双下方法,之所以会叫魔法方法的原因吗,是达到了某个条件,所以触发了调用。
    
魔法方法 触发条件
双下init 实例化对象的时候自动触发
双下str 对象被执行打印操作的时候会自动触发,该方法必须返回一个字符串,返回什么字符串打印对象后就展示什么字符串
双下call 对象加括号调用,自动触发该方法
双下getattr 当对象获取一个不存在的属性名,自动给触发,该方法返回什么,对象获取不存在的属性名就会得到什么
双下set 对象操作属性值的时候触发,对象.属性名 = 属性值
双下del 对象在被删除(主动,被动)的时候自动触发
双下getattribute 对象获取属性的时候自动触发,无论这个属性存不存在
双下enter 对象被with语法执行的时候自动触发,该方法返回什么,as关键字后面的变量就得到什么
双下exit 对象被with语法执行并运行完with子代码之后,自动触发

魔法方法实战

"""补全以下代码 执行之后不报错"""
class Context:
pass
def do_someting(self):
with Context() as f:

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类产生的
2.产生类的类称之为 '元类'
  • 引入

  • 基础阶段我们使用type来查找数据的数据类型
    但是学了面向对象之后 发现查看的不是数据类型 而是数据所属的类

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'>
我们定义的数据类型 其实本质还是通过各个类产生了对象
class str:
    pass


h = 'hello'
print(type(h),h)
---执行结果--------
<class 'str'> hello

我们也可以理解为type用于查看产生当前对象的类是谁
自定义的类都是由type类产生的
-============================================================
class MyClass:
    pass
obj = MyClass()
print(type(obj))  # 查看产生对象obj的类:<class '__main__.MyClass'>
print(type(MyClass))  # 查看产生对象MyClass的类:<class 'type'>

产生类的两种方式

1.class 关键字

2.type(类名,父类,类的名称空间)

---------------------------例子---------------------------------
1.class关键字
	class MyClass:
        pass
        
2.利用元类type
	type(类名,类的父类,类的名称空间)

---------------------------------------------------------------
  • 结论

    学习元类其实就是掌握了类的产生过程 我们就可以在类的产生过程中高度定制化类的行为
    	eg:
    		类名必须首字母大写
    	上述需求就需要使用元类来控制类的产生过程 在过程中校验
    

元类基本使用

类中的双下__init__用于实例化对象
元类中双下__init__用于实例化类

metclass 产生一个控制行为

  • 实战

  • 需求类名必须首字母大写

    上述需求就需要使用元类来控制类的产生过程 在过程中校验

class MyMetaClass(type):  #"""只有继承了type的类才可以称之为是元类"""

    def __init__(self,what, bases=None, dict=None):
        # print('别晕')
        # print('what', what)  类名
        # print('bases', bases) 类的父类
        # print('dict', dict) 类的名称空间
        if not what.istitle():
            raise Exception('元类定义首字母必须大写 ')
        super().__init__(what, bases, dict)

具备用元类的条件
"""如果想要切换产生类的元类不能使用继承 必须使用关键字metaclass声明"""
class aaa(metaclass=MyMetaClass):      
    pass
---------执行结果-【定义aaa】--------------------
raise Exception('元类定义首字母必须大写')
Exception: 元类定义首字母必须大写

    
-------------【定义Bbb】-------------------------------    
   class Bbb(metaclass=MyMetaClass):
    pass
  --------执行结果-----------------------
Process finished with exit code 0

元类进阶、

元的作用:元类不单单可以控制类的产生,其实也可以控制对象

1.对象加括号执行产生该对象类里面的双下call
2.类加括号执行产生该类的元类里面的双下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)
---------执行结果-------------------

  • 总结

    1.如果我们想高度定制对象的产生过程
    	可以操作元类里面的__call__
    	
    2.如果我们想高度定制类的产生过程
    	可以操作元类里面的__init__
    

拓展:双下new方法

类产生对象的步骤
	1.产生一个空对象
	2.自动触发__init__方法实例化对象
	3.返回实例化好的对象
	
__new__方法专门用于产生空对象				骨架
__init__方法专门用于给对象添加属性		  血肉
posted @ 2022-07-30 22:36  名字长的像一只老山羊  阅读(21)  评论(0编辑  收藏  举报