【7.29】python中的魔法方法和元类的应用

学习内容概要

  • 反射实战案例

  • 面向对象的魔法方法(双下方法)

  • 魔法方法实战演练

  • 元类简介

  • 创建类的两种方式

  • 元类的实际应用

  • 元类之双下new方法

    今日内容详细

反射实战案例

1.加载配置文件纯大写的配置
需求:配置文件加载:获取配置文件中所有大写的配置 小写的直接忽略  组织成字典settings里有以下内容
NAME = 'make'
AGE = 18
name = 'jason'
age = 28
================================================================
import settings
new_dict = {}
# print(dir(settings))  # dir获取括号中对象可以调用的名字
# ['NAME', 'AGE', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'name', 'age']
for i in dir(settings):
    if i.isupper():  # 如果名字是纯大写 那么获取该大写名字对应的值   'NAME'   'AGE'
        v = getattr(settings, i)
        new_dict[i] = v
        print(new_dict)  # {'AGE': 18, 'NAME': 'make'}
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)

面向对象魔法方法

  • 魔法方法其实就是类中定义的双下方法
  • 之所以会叫魔法方法原因是这些方法都是到达某个条件自动触发 无需调用
    • __init__方法在给对象设置独有数据的时候自动触发(实例化)
魔法方法 作用
def init(self): 实例化对象的时候自动触发
def str(self): 对象被执行打印操作的时候会自动触发
该方法必须返回一个字符串
返回什么字符串打印对象之后就展示什么字符串
def call(self, *args, **kwargs): 对象加括号调用 自动触发该方法
def getattr(self, item): 当对象获取一个不存在的属性名 自动触发
该方法返回什么 对象获取不存在的属性名就会得到什么
形参item就是对象想要获取的不存在的属性名
def setattr(self, key, value): 对象操作属性值的时候自动触发>>>: 对象.属性名=属性值
def del(self): 对象在被删除(主动 被动)的时候自动触发
def getattribute(self, item): 对象获取属性的时候自动触发 无论这个属性存不存在
当类中既有__getattr__又有__getattribute__的时候 只会走后者
def enter(self): 对象被with语法执行的时候自动触发 该方法返回什么 as关键字后面的变量名就能得到什么
def exit(self, exc_type, exc_val, exc_tb): 对象被with语法执行并运行完with子代码之后 自动触发

魔法方法笔试题

"""补全以下代码 执行之后不报错"""
class Context:
    def __enter__(self):
        pass
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

with Context() as f:
    f.do_something() 
=================================
# 解决方式:

class Context:
    def __enter__(self):
        return self  # 返回什么f就拿到什么
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass
    def do_something(self):
        pass  # 这一步定义一个方法 给下述做准备
with Context() as f:
    f.do_something()  # f是对象 (Context)类产生的对象  对象.do_something  这个方法  就不会报错 

元类简介

推导:

s1 = '今天很热'
l1  = [1,232,3,43,345,46,456,5]
d1 = {'name':'make','age':20}
print(type(s1))  # <class 'str'>
print(type(l1))  # <class 'list'>
print(type(d1))  # <class 'dict'>

# 发现:
基础阶段我们使用type来查找数据的数据类型
学了面向对象之后 发现查看的不是数据类型 而是数据所属的类
我们定义的数据类型 其实本质还是通过各个类产生了对象

class str:
    s1 = '今天很热'
print(str.s1)
# 我们也可以理解为type用于查看产生当前对象的类是谁
class MyClass:
    pass
obj = MyClass()
print(type(obj))  # 查看产生对象obj的类:<class '__main__.MyClass'>
print(type(MyClass))  # 查看产生对象MyClass的类:<class 'type'>

  • 通过上述推导 得出结论 自定义的类都是由type类产生的
    我们将产生类的类称之为 '元类'

产生类的两种方式

1.class关键字
	class MyClass:
        pass

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

元类基本使用

  • 只有继承了type的才能称之为是元类

  • 如想切换产生类的元类 需要使用关键字metaclass 声明

  • 推导:

    • 类中的__init__用于实例化对象
    • 元类中__init__用于实例化类
"""
模拟做到底层代码 类的首字母必须要大写
"""
class MyMetaClass(type):
    def __init__(self, what, bases=None, dict=None):
        print('what', what)  # 类名
        print('bases', bases)  # 类的父类
        print('dict', dict)  # 类的名称空间
        if not what.istitle():
            raise Exception('首字母必须大写 你会不会写python 面向对象学过吗 lowB')
        super().__init__(what, bases, dict)
=========================================
#  如果想要切换产生类的元类  必须要使用metaclass 申明
class Myclass(metaclass=MyMetaClass):
    pass
=========================================
class aaa(metaclass=MyMetaClass):
    pass
# 类名首字母没有大写 报错

元类进阶

  • 元类可以控制类的产生过程,也可以控制对象!!!

    • 1.对象加括号执行产生该对象类里面的双下call
    • 2.类加括号执行产生该类的元类里面的双下call
class MyClass:
    def __call__(self, *args, **kwargs):
        pass

obj = MyClass()
obj()  # 对象加括号执行产生该对象类里面的双下call 
========================================
class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        pass
class MyClass(metaclass=MyMetaClass):
    pass

obj = MyClass()  # 类加括号执行产生该类的元类里面的双下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)  # 报错 必须用关键字参数传参')【原因:控制添加了__call__的判断条件】

__call__  
obj = MyClass(name='jason', age=18)  # __call__  __init__【先走双下call 在双下init】

  • 总结:

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

双下new方法

  """
类产生对象的步骤
	1.产生一个空对象
	2.自动触发__init__方法实例化对象
	3.返回实例化好的对象
"""
__new__方法专门用于产生空对象				骨架
__init__方法专门用于给对象添加属性		  血肉
posted @ 2022-07-31 15:30  W日常  阅读(71)  评论(0)    收藏  举报