面向对象四

今日内容概要

  • 反射实战案例
  • 面向对象的魔法方法(双下方法)
  • 魔法方法实战演练
  • 元类简介
  • 创建类的两种方式
  • 元类的实际应用
  • 元类之双下new方法

今日内容详细

反射实战案例

#配置文件加载:获取配置文件中所有大写的配置小写直接忽略
new_dict = {}  # 定义空字典
import settings
for i in dir(settings):  # 循环获取文件中可以调用的名字
    if i.isupper():  # 如果名字是大写的
        v = getattr(settings,i)  # 获取该大写名字对应的值
        new_dict[i] = v  # 将对应的值添加到字典中 i是键 v是值
print(new_dict)
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):  # 判断对象obj中是否存在cmd属性
        cmd_name = getattr(obj,cmd)  # 如果存在则拿到该属性的名字
        cmd_name()  # 函数地址加括号调用
    else:
        print(f'{cmd}不是内部命令不是可执行的程序')

面向对象魔法方法

魔法方法其实就是类中定义的双下方法
	之所以会叫魔法方法原因是这些方法都是到达某个条件自动触发 无需调用
 		eg: __init__方法在给对象设置独有数据的时候自动触发(实例化)
 
下列讲解的魔法方法都必须明确的知道的触发的条件
class MyClass(object):
    def __init__(self, name):
        """实例化对象的时候自动触发"""
        # print('__init__方法')
        # pass
        self.name = name
    def __str__(self):
        """
        对象被执行打印操作的时候会自动触发
            该方法必须返回一个字符串
            返回什么字符串打印对象之后就展示什么字符串
        """
        # print('__str__方法')
        # print('这是类:%s 产生的一个对象')
        # return '对象:%s'%self
        return '对象:%s'%self.name
    def __call__(self, *args, **kwargs):
        """对象加括号调用 自动触发该方法"""
        print('__call__方法')
        print(args)
        print(kwargs)
    def __getattr__(self, item):
        """当对象获取一个不存在的属性名 自动触发
            该方法返回什么 对象获取不存在的属性名就会得到什么
            形参item就是对象想要获取的不存在的属性名
        """
        print('__getattr__', item)
        return '您想要获取的属性名:%s不存在'%item
    def __setattr__(self, key, value):
        """对象操作属性值的时候自动触发>>>:  对象.属性名=属性值"""
        # print("__setattr__")
        # print(key)
        # print(value)
        super().__setattr__(key, value)
    def __del__(self):
        """对象在被删除(主动 被动)的时候自动触发"""
        # print('__del__')
        pass
    def __getattribute__(self, item):
        """对象获取属性的时候自动触发 无论这个属性存不存在
            当类中既有__getattr__又有__getattribute__的时候 只会走后者
        """
        # print('__getattribute__')
        # return super(MyClass, self).__getattribute__(item)  复杂写法
        return super().__getattribute__(item)  # 简便写法
    def __enter__(self):
        """对象被with语法执行的时候自动触发 该方法返回什么 as关键字后面的变量名就能得到什么"""
        print('__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        """对象被with语法执行并运行完with子代码之后 自动触发"""
        print('__exit__')
class Myclass(object):
    def __init__(self,name):
        self.name = name

    #     print('__init__方法')
    def __str__(self):
        print('__str__')
        return '%s'%self.name
    def __call__(self, *args, **kwargs):
        print(args)
        print(kwargs)

    def __getattr__(self, item):
        print('__getattr__',item)  # 形参item就是想要获取的属性名
    def __setattr__(self, key, value):
        print('__setattr__')
        super().__setattr__(key,value)
    def __del__(self):
        print('__del__')
    def __getattribute__(self, item):
        print(' __getattribute__')
obj = Myclass('jason')
#__init__实例化对象的时候执行
#__str__打印操作的时候会自动执行 该方法必须返回字符串
#__call__对象加括号的时候就会执行
#__getattr__获取不存在的属性名时就会执行
#__setattr__ 对象添加属性的时候就会执行
#__del__对象执行删除的时候执行
#__getattribute__ 获取对象属性名机会执行
print(obj.age)

魔法方法笔试题

"""补全以下代码 执行之后不报错"""
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()

元类简介

# 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'>
"""
基础阶段我们使用type来查找数据的数据类型
但是学了面向对象之后 发现查看的不是数据类型 而是数据所属的类

我们定义的数据类型 其实本质还是通过各个类产生了对象
    class str:
        pass
    h = 'hello'  str('hello')

我们也可以理解为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:
		类名必须首字母大写
	上述需求就需要使用元类来控制类的产生过程 在过程中校验
"""

元类基本使用

class MyMetaClass(type):
    pass
"""只有继承了type的类才可以称之为是元类"""
class MyClass(metaclass=MyMetaClass):
    pass
"""如果想要切换产生类的元类不能使用继承 必须使用关键字metaclass声明"""

'''
思考
	类中的__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)
"""只有继承了type的类才可以称之为是元类"""


# class Myclass(metaclass=MyMetaClass):
#     pass


"""如果想要切换产生类的元类不能使用继承 必须使用关键字metaclass声明"""
class aaa(metaclass=MyMetaClass):
    pass

元类进阶

"""元类不单单可以控制类的产生过程 其实也可以控制对象的!!!"""
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)


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

双下new方法

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