Python面向对象之魔法方法、元类

反射实战案例

  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('请输入您的命令>>>:')
    if hasattr(obj, cmd):
        cmd_name = getattr(obj, cmd)
        cmd_name()
    else:
        print('%s 不是内部或外部命令,也不是可运行的程序或批处理文件' % cmd)

面向对象魔法方法

概念

魔法方法其实就是类中定义的双下方法

这些双下方法不需要刻意调用,到达某个条件会自动触发

eg: __init__方法在给对象设置独有数据的时候自动触发(实例化)

常用的魔法方法

方法 触发条件 描述
__init__ 实例化对象之后立即触发 初始化对象的成员
__new__ 实例化对象时自动触发(在__init__之前触发) 实例化对象
__del__ 当该类对象被销毁时,自动触发 关闭或释放对象创建时资源
__call__ 将对象当作函数调用时触发,方式: 对象() 可以将复杂的步骤进行合并操作,减少调用的步骤,方便使用
__str__ 使用print(对象)或者str(对象)的时候触发 print(对象时)进行操作,得到字符串,通常用于快捷操作

与属性操作相关的魔法方法

方法 触发条件 描述
__getattr__ 获取不存在的对象成员时触发 为访问不存在的属性设置值
__setattr__ 设置对象成员值的时候触发 接管设置操作,可以在设置前之前进行判断验证等行为
__getattribute__ 使用对象成员时触发,无论成员是否存在 在具有封装操作(私有化时),为程序开部分访问权限使用

上下文管理相关魔法方法(with)

方法 触发条件 描述
__enter__ 对象被with 语法执行上下文操作的时候自动触发 enter 的返回值被 with 语句的目标或者 as 后的名字绑定
__exit__ 对象被with语法执行并运行完with子代码后 自动触发 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作

魔法方法笔试题

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

元类

即产生类的类

print(type(123))  # <class 'int'>
print(type([12, 33, 44]))  # <class 'list'>
print(type({'name':'jason','pwd':123}))  # <class 'dict'>

'''type查看的其实是当前对象所属的类名称'''
class MyClass(object):
    pass
obj = MyClass()
print(type(obj))
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 MyClass:
    pass

2. 利用元类type

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

学习元类其实就是掌握了类的产生过程 我们就可以在类的产生过程中高度定制化类的行为

eg:
		类名必须首字母大写
        
'''上述需求就需要使用元类来控制类的产生过程 在过程中校验'''

元类基本使用

元类是不能通过继承的方式直接指定的,需要通过关键字参数的形式修改

class MyClass(metaclass=MyMetaClass):
    pass

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)
        
"""如果想要切换产生类的元类不能使用继承 必须使用关键字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__')
  • 若要高度定制对象的产生过程,可以操作元类里面的__call__
  • 若要想高度定制类的产生过程,可以操作元类里面的__init__

双下new方法

__new__用于产生空对象(类),相当于骨架

__init__用于实例化对象(类),相当于血肉

类产生对象的步骤

  1. 产生一个空对象
    2. 自动触发__init__方法实例化对象
    3. 返回实例化好的对象
posted @ 2022-08-01 00:10  梦想有双休  阅读(54)  评论(0)    收藏  举报