Java教程

元类

本文主要是介绍元类,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录
  • 反射实例案例
  • 面向对象的双下方法
  • 笔试题讲解
  • 元类简介
  • 产生类的两种表现形式(本质是一种)
  • 元类的基本使用
  • 元类进阶操作
  • 双下new方法

反射实例案例

对于面向对象之反射,我的理解是你通过输入的方式给出一个指令,我不需要知道你给出的是哪个对象,其中的指令有哪些,我只要管自己输入要执行的指令,有我就执行,没有我就返回指令不存在的提示。

class WinCmd(object):
    def ls(self):
        print('windows系统正在执行ls命令')

    def dir(self):
        print('windows系统正在执行dir命令')

    def cd(self):
        print('windows系统正在执行cd命令')


class LinuxCmd(object):
    def ls(self):
        print('Linux系统正在执行ls命令')

    def dir(self):
        print('Linux系统正在执行dir命令')

    def cd(self):
        print('Linux系统正在执行cd命令')


obj = WinCmd()
obj1 = LinuxCmd()
'''反射提供了一种不需要代码的前提下,操作数据和功能'''


def run(obj):
    while True:
        cmd = input('请输入您的指令>>>:')
        if hasattr(obj,cmd):
            func_name = getattr(obj, cmd)
            func_name()
        else:
            print('cmd command not found')


run(obj1)
run(obj)

面向对象的双下方法

面向对象的双下方法被一些人称为魔法方法,因为一些面向对象双下方法在达到某个条件的时候会自动触发,不需要手动调用

# __str__
class person(object):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        print('__str__执行')
        return '打印时执行__str__'


obj1 = person('king')
print(obj1)
print(person)

'''
在对象被执行打印(print、前端展示)操作的时候自动触发
该方法必须返回字符串类型的数据,不然会报错
主要用于精准的描述对象
'''

# __del__
class person(object):
    def __init__(self, name):
        self.name = name

    def __del__(self):
        print(self.name)

obj1 = person('king')

# 执行完成代码为被动
# print(obj1)
# print('哈哈')

# 主动
print(obj1)
del obj1  
print('哈哈')

'''
对象被执行(被动、主动)删除操作之后自动执行
被动删除:python解释器会在程序执行完成之后,自动删除内容
主动删除:使用del等删除的关键字手动删除
'''

# __getattr__
class person(object):
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        print(f'{item}对象查找不存在')


obj1 = person('king')
print(obj1.age)  # None,__getattr__的执行在这个结果出现之前
'''
对象查找(使用)不存在的名字的时候自动触发
'''

# __setattr__
# 主要用于给对象添加属性用的,当对象执行添加属性操作的时候自动触发  >>> obj.变量名=变量值

class person(object):
    def __init__(self, name):
        self.name = name

    def __setattr__(self, key, value):
        print('执行了__setattr__')
        super().__setattr__(key, value)
'''
当你对__setattr__方法进行操作添加的时候要记得继续再继承一下父类的方法__setattr__不然你的属性添加不到对象的名称空间中
'''

obj1 = person('king')
obj1.age = 18
# print(obj1.age)
print(obj1.__dict__)

# __call__

class person(object):
    def __init__(self, name):
        self.name = name

    def __call__(self, *args, *kwargs):
        print('hahaha', args, kwargs)
        return 'sdaadssd'


obj1 = person('king')
obj1(1)

'''
用于对象加括号调用的时候自动触发,如果想接受实参,加上形参即可
'''

# __enter__

class person(object):
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('执行__enter__')

    def __exit__(self, exc_type, exc_val, exc_tb):  # 一定要加上后面三个参数,在看不到的地方自动传入了四个参数
        print('执行__exit__', exc_type, exc_val, exc_tb)  # 执行__exit__ None None None


obj1 = person('king')
with obj1 as f:  # with程序开始,自动执行__enter__
    print('123')  # with程序结束,自动执行__exit__
print('321')

'''
__enter__和__exit__必需配合使用
'''

# __getattribute__

class person(object):
    def __init__(self, name):
        self.name = name

    def __getattribute__(self, item):
        print(f'{item}',item)


obj1 = person('king')
print(obj1.__dict__)  # None   只要一看到对象查找名字(调用数据),就运行__getattribute__
print(obj1.age)  # None
print(obj1.name)  # None

'''
只要对象查找名字无论名字是否存在都会执行该方法
如果类中有__getattribute__方法 那么就不会去执行__getattr__方法
用了这个方法,就算有这个属性,print打印的结果也是None
'''

笔试题讲解

# 让字典具备据点符查找值的功能
# 1.定义一个类继承字典
class MyDict(dict):
    # 做到可以句点符取值
    def __getattr__(self, item):
        return self.get(item)
    
    # 做到句点符赋值
    def __setattr__(self, key, value):
        self[key] = value  # 对象名称空间中是正常的属性=属性值,而字典中是键值队的形式,因此赋值方式要更改一下
    
    
'''对于这个题目要做到区分是名称空间的名字还是数据k:v键值对'''
obj = MyDict({'name': 'jason', 'age': 18})
# 1.具备句点符取v
# print(obj.name)
obj.pwd = 123  # 相当于 正常字典的obj['gender'] = 'male'
'''给字典名称空间添加名字,不是数据k:v'''
print(obj)

2.补全下列代码 使其运行不报错
# 补全下列代码 使其运行不报错
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 ctx:
    ctx.do_something()
'''
对于with 对象 as 变量名: 来说对象所属的类中一定要有双下enter和双下exit
对于对象内容所变成的do_something来说,对象内要有do_something方法
'''

元类简介

# 元类
即产生类的类

class MyClass(object):
    pass


obj = MyClass()
print(type(obj))  # <class '__main__.MyClass'> type查看的其实是当前对象所属的类名称
print(type(MyClass))  # <class 'type'>


class Student:
    pass


print(type(Student))  # <class 'type'>


class Teacher(MyClass):
    pass


print(type(Teacher))  # <class 'type'>
'''type就是所有类默认的元类'''

产生类的两种表现形式(本质是一种)

1.class关键字
	class C1(object):
        pass
    print(C1)  # <class '__main__.C1'>
    
2.type元类
	type(类名,父类,类的名称空间)
    res = type('C1', (), {})
    	print(res)  # <class '__main__.C1'>

'''
学习元类的目的
	元类能够控制类的创建 也就意味着我们可以高度定制类的行为
	eg:掌握了物品的生产过程 就可以在过程中做任何的额外操作
	
	比如:要求类的名字必须首字母大写
		思考在哪里编写定制化代码
			类的产生过程目前还比较懵 	  元类里面的__init__方法
			对象的产生过程呢 			     类里面的__init__方法
		方法:由已知推未知
'''

元类的基本使用

'''元类是不能通过继承的方式直接指定的'''
通过关键字参数metaclass修改该类的元类,C1的创建受自己元类的影响
class C1(metaclass=MyTypeClass):
    pass


class MyTypeClass(type):  # 元类用来定制类的行为(样式)
    def __init__(cls, cls_name, cls_bases, cls_dict):  # 必须写明4个形参,因为默认传入4个实参
        # print(cls, cls_name, cls_bases, cls_dict)
        if not cls_name.istitle():
            raise Exception("类名的首字母必须大写 你个sd")
        super().__init__(cls_name)


class C1(metaclass=MyTypeClass):
    school = "清华大学"


class a(metaclass=MyTypeClass):
    school = '清华大学'

元类进阶操作

1.回想__call__方法
	对象加括号会自动执行产生该对象的类里面的__call__,并且该方法返回什么对象加括号就会得到什么
  推导:类加括号会执行元类的里面的__call__该方法返回什么其实类加括号就会得到什么
"""类里面的__init__方法和元类里面的__call__方法执行的先后顺序"""
class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        print('__call__ run')
        print(args, kwargs)
        super(MyTypeClass, self).__call__(*args, **kwargs)


class MyClass(metaclass=MyTypeClass):
    def __init__(self, name):
        print('__init__')
        self.name = name
        
        
obj = MyClass('jason')
# call在init前运行

# 定制对象的产生过程
class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        if args:  # 若args有值表示输入的不是关键字参数
            raise Exception("必须全部采用关键字参数")
        super().__call__(*args, **kwargs)


class MyClass(metaclass=MyTypeClass):
    def __init__(self, name):
        self.name = name

'''只要为了展示,传入的值目前拿不出来'''
'''强制规定:类在实例化产生对象的时候 对象的独有数据必须采用关键字参数'''
obj2 = MyClass(name='jason')  # 关键字参数是放在kwargs里的
print(type(obj2))
'''
如果你想高度定制类的产生过程
	那么编写元类里面的__init__方法
如果你想高度定制对象的产生过程
	那么编写元类里面的__call__方法
'''

双下new方法

__new__用于产生空对象(类)			骨架
__init__用于实例化对象(类)		血肉
class MyTypeClass(type):
    # def __new__(cls, *args, **kwargs):
    #     print('__new__ run')
    #     return super().__new__(cls, *args, **kwargs)
    #
    # def __init__(cls,cls_name, cls_bases, cls_dict):
    #     print('__init__ run')
    #     super().__init__(cls_name, cls_bases, cls_dict)

    def __call__(cls, *args, **kwargs):
        # 1.产生一个空对象
        obj = object.__new__(cls, *args, **kwargs)
        # 2.调用init方法实例化
        return obj


class MyClass(metaclass=MyTypeClass):
    def __init__(self, name):
        self.name = name

obj = MyClass('jason')
print(obj)
"""
注意:并不是所有的地方都可以直接调用__new__ 该方法过于底层
	如果是在元类的__new__里面 可以直接调用
class Meta(type):
      def __new__(cls, *args, **kwargs):
          obj = type.__new__(cls,*args,**kwargs)
          return obj
	如果是在元类的__call__里面 需要间接调用
class Mate(type):
    def __call__(self, *args, **kwargs):
    	obj = object.__new__(self) # 创建一个空对象
    	self.__init__(obj,*args,**kwargs) # 让对象去初始化
         return obj
"""
这篇关于元类的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!