栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Day34——面向对象的魔法方法,元类

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Day34——面向对象的魔法方法,元类

文章目录
  • 反射实战案列
  • 面向对象魔法方法
  • 魔法方法笔试题
  • 元类简介
  • 产生类的两种方式
  • 元类的基本使用
  • 元类进阶
  • 双下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('请输入你的指令:').strip()
    if hasattr(obj, cmd):
        cmd_name = getattr(obj, cmd)
        cmd_name()
    else:
        print('%s 不是内部或外部命令,也不是可运行的程序或批处理文件' % cmd)

'''
请输入你的指令:xxx
xxx 不是内部或外部命令,也不是可运行的程序或批处理文件
请输入你的指令:ls
ls获取当前路径下所有的文件名称
请输入你的指令:dir
dir获取当前目录下所有的文件名称
请输入你的指令:ipconfig
ipconfig获取当前计算机的网卡信息
'''
面向对象魔法方法
  • 理解

    魔法方法其实就是类中定义的双下方法,之所以会叫魔法方法原因是这些方法都是到达某个条件自动触发 无需调用
    	eg:__init__方法在给对象设置独有的数据值的时候会自动触发(实例化)
    
    方法解释
    __ init__给对象设置独有的数据的时候会自动触发
    __ str__对象被执行打印(print、前端展示)操作的时候自动触发,该方法必须返回字符串类型的数据,很多时候用来更加精准的描述对象
    __ del__对象被执行(被动、主动)删除操作之后自动执行
    __ getattr__对象查找不存在的名字
    __ setattr__对象在执行添加属性操作的时候自动触发 >>> obj.变量名=变量值
    __ call__对象被加括号调用的时候自动触发
    __ enter__开始进行with操作的时候
    __ exit__with操作结束的时候
    __ getattribute__查找存不存在的时名字候都会触发,如果类中有__getattribute__方法 那么就不会去执行__getattr__方法
    __ new__在类被实例化前,最先被执行的方法,主要用来去创建一个完全空白的对象;先走new,再走init
  • 举例说明(部分)

__call__方法

class MyClass(object):
    def __init__(self, name):
        """实例化对象的时候自动触发"""
        # print('__init__方法')
        # pass
        self.name = name
        
    def __call__(self, *args, **kwargs):
        """对象加括号调用 自动触发该方法"""
        print('__call__方法')
        print(args)
        print(kwargs)
       
'''
__call__方法
(1, 2)
{'a': 123}
'''

__getattr__方法

class MyClass(object):
    def __init__(self, name):
        """实例化对象的时候自动触发"""
        # print('__init__方法')
        # pass
        self.name = name
        
    def __getattr__(self, item):
        """当对象获取一个不存在的属性名 自动触发
            该方法返回什么 对象获取不存在的属性名就会得到什么
            形参item就是对象想要获取的不存在的属性名
        """
        print('__getattr__', item)
        return '您想要获取的属性名:%s不存在' % item


obj = MyClass('jason')
print(obj.age)  # age是不存在的

'''
__getattr__ age
您想要获取的属性名:age不存在
'''
class MyClass(object):
    def __init__(self, name):
        """实例化对象的时候自动触发"""
        # print('__init__方法')
        # pass
        self.name = name
        
    def __del__(self):
        """对象在被删除(主动 被动)的时候自动触发"""
        print('__del__')  # 代码运行结束后会清空,所以会自动执行

obj = MyClass('jason')

'''
__del__
'''
class MyClass(object):
    def __init__(self, name):
        """实例化对象的时候自动触发"""
        # print('__init__方法')
        # pass
        self.name = name
        
    def __setattr__(self, key, value):
        """对象操作属性值的时候自动触发>>>:  对象.属性名=属性值"""
        print("__setattr__")
        print(key)
        print(value)

obj = MyClass('jason')  # 执行一次
obj.name = 666  # 执行第二次

'''
__setattr__
name
jason
__setattr__
name
666
'''
魔法方法笔试题

要求:补全以下代码 执行之后不报错

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()
元类简介
  • 简介

元类即产生类的类

  • 引入

基础阶段我们使用type来查找数据的数据类型
但是学了面向对象之后 发现查看的不是数据类型 而是数据所属的类
我们也可以理解为type用于查看产生当前对象的类是谁

#type查看的其实是当前对象所属的类名称
print(type(123))
print(type([12, 33, 44]))
print(type({'name':'jason','pwd':123}))

'''
# 执行结果



'''
class MyClass:
    pass
obj = MyClass()
print(type(obj))  # 查看产生对象obj的类:
print(type(MyClass))  # 查看产生对象MyClass的类:

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

type就是所有类默认的元类!!!

产生类的两种方式

方法一

1.class关键字

class MyClass:

pass

class C1(object):
    pass
	print(C1)  # 

方法二

2.利用元类type

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

type(类名,父类,类的名称空间)
res = type('C1', (), {})
print(res)  # 
元类的基本使用

元类是不能通过继承的方式直接指定的,需要通过关键字参数的形式修改
如果想要切换产生类的元类不能使用继承 必须使用关键字metaclass声明

举例说明(限制类的创建必须首字母大写)

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 Myclass(metaclass=MyMetaClass):
    pass
元类进阶

元类不单单可以控制类的产生过程 其实也可以控制对象的!!!

我们回想双下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__
Traceback (most recent call last):
  File "D:/pythonProject/day33/01 反射的实战演练.py", line 115, in 
    obj = MyClass('jason', 18)
  File "D:/pythonProject/day33/01 反射的实战演练.py", line 103, in __call__
    raise Exception('必须用关键字参数传参')
Exception: 必须用关键字参数传参
'''

如果你想高度定制类的产生过程
那么编写元类里面的__init__方法
如果你想高度定制对象的产生过程
那么编写元类里面的__call__方法

双下new方法

类产生对象的步骤

1.产生一个空对象
2.自动触发__init__方法实例化对象
3.返回实例化好的对象

先走new方法产生空对象,再走init方法实例化

__new__方法专门用于产生空对象 骨架
__init__方法专门用于给对象添加属性 血肉

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1031412.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号