其他分享
首页 > 其他分享> > 面向对象魔法方法与元类

面向对象魔法方法与元类

作者:互联网

反射实战案例

1.加载配置文件中所有纯大写的配置
import src  # AA = '是大写', aa = '是小写'

new_dict = {}
print(dir(src))  # dir用于获取括号中对象可以调用的名字
# ['AA', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'aa']
for i in dir(src):
    if i.isupper():  # 判断名字是不是纯大写
        v = getattr(src, i)
        new_dict[i] = v  # 将纯大写的名字加入字典
print(new_dict)
# {'AA': '是大写'}

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(f'{cmd}不是内部或外部命令,也不是可运行的程序或批处理文件')

面向对象魔法方法

'''
魔法方法其实就是类中定义的双下方法,之所以叫魔法方法是因为这些方法都是达到某个条件就会自动触发,也就是对象实例化
eg:__init__
'''
class MyClass(object):
    def __init__(self):
        '实例化对象的时候触发'
        print('触发__init__')
        # self.name = name
        pass

    def __str__(self):
        '''对象被执行打印操作的时候会触发
        该方法必须返回一个字符串
        返回什么字符串打印对象之后就展示什么字符串'''
        print('触发__str__')
        return ''

    def __call__(self, *args, **kwargs):
        '对象加括号调用时自动触发'
        print('触发__call__')
        print(args)
        print(kwargs)

    def __getattr__(self, item):
        '''对象获得一个不存在的属性名时自动触发
        对象获取的不存在的属性名会获取该方法的返回值
        形参item就是对象获取的不存在的属性名'''
        print('触发__getattr__')
        return f'属性名:{item}不存在'

    def __setattr__(self, key, value):
        '对象操作属性值时自动触发(对象.属性名=属性值)'
        print('触发__setattr__')

    def __del__(self):
        '对象在被删除的时候自动触发触发'
        print('触发__del__')

    def __getattribute__(self, item):
        '''对象获取属性的时候自动触发
        无论这个属性存不存在
        当类中既有__getattr__又有__getattribute__时只会走后者'''
        print('触发__getattribute__')

    def __enter__(self):
        '对象被with语法执行时自动触发,该方法返回什么,as关键字后面的变量名就能得到什么'
        print('触发__enter__')
        return '对象被with语法执行'

    def __exit__(self, exc_type, exc_val, exc_tb):
        '对象被with语法执行并运行子代码之后自动触发'
        print('触发__exit__')

        
res = MyClass()  # 触发__init__
print(res)  # 触发__str__
res(1, 2, name='barry', age=20)  # 触发__call__
# (1, 2)
# {'name': 'barry', 'age': 20}
print(res.name)  # 触发__getattr__
# 属性名:name不存在
res.name = 'barry'  # 触发__setattr__
del res  # 触发__del__
print(res.name)  # 触发__getattribute__
# None
with res as f:
    print(f)
# 触发__enter__
# 对象被with语法执行
# 触发__exit__
img

魔法方法笔试题

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

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

    def do_something(self):
        pass


with Context() as f:
    f.do_something()

元类简介

s1 = '字符串'
l1 = ['列表']
d1 = {'这是': '字典'}
print(type(s1))  # <class 'str'>
print(type(l1))  # <class 'list'>
print(type(d1))  # <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类产生的,我们将产生类的类称为'元类'"
img

产生类的两种方式

1.class 关键字
class 类名:
    pass
    
2.利用元类type
type(类名,类的父类,类的名称空间)

'利用元类type来产生类虽然更繁琐,但可以帮我们高度定制产生类的过程'

元类的使用

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)  # 类名
        # MyClass
        print(bases)  # 类的父类
        # ()
        print(dict)  # 类的名称空间
        # {'__module__': '__main__', '__qualname__': 'MyClass'}
        if not what.istitle():  # 判断类名首字母是否大写
            raise Exception('首字母必须大写')  # 首字母不是大写则报错
        super().__init__(what, bases, dict)  # 首字母大写则产生类
    '只有继承了type的类才可以称之为是元类'


class Myclass(metaclass=MyMetaClass):
    pass


class abc(metaclass=MyMetaClass):
    pass
# 报错:Exception: 首字母必须大写
img

元类进阶

'元类不止可以控制类的产生,还可以控制对象的产生'
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('barry', 20)
# 报错:Exception: 必须使用关键字传参
obj = Myclass(name='barry', age=20)
# 触发__call__
# 触发__init__
'''
我们可以操作元类里面的__call__来定制对象的产生过程
我们可以操作元类里面的__init__来定制类的产生过程
'''

双下new方法

'''
类产生对象的步骤
1.产生一个空对象
2.自动触发__init__方法实例化对象
3.返回实例化好的对象
'''
__new__方法专门用于产生空对象
__init__方法专门用于给对象添加属性

标签:__,触发,self,魔法,元类,面向对象,print,type,def
来源: https://www.cnblogs.com/riuqi/p/16535429.html