Loading

面向对象之元类

【一】常用的魔法方法

【1】初始化对象的属性__init__

【二】元类

【1】什么是元类

  • 一切源于一句话:Python中一切皆对象
  • 八大基本数据类型是对象
  • 类实例化得到的对象也是对象
  • 其实类本身也是一种对象
class Human:
    def __init__(self, name, age):
        self.name = name
        self.age = age


obj = Human('hqq', 21)

print(type(obj))  # <class '__main__.Human'>

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

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

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

  • 通过查看每一种数据的数据类型,我们会发现都共同拥有一个类
  • 这个类就是 type 我们也称之为元类

【2】产生类的两种方式

(1)直接关键字创建

  • 语法
关键字(class) 类名(Student)继承的父类(默认是object):
    # 类体代码
  • 示例
class Human:
    def __init__(self, name, age):
        self.name = name
        self.age = age

(2)通过 type 创建

def read():
    ...


Human = type('Human', (object,), {'name': 'hqq', 'age': 21, 'read': read})
print(type(Human)) # <class 'type'>
print(Human.__dict__)

【3】为什么要使用元类

  • 元类可以控制类的创建,也就意味着我们可以高度定制类的具体行为
    • 比如,当我们掌控了食品的生产过程,我们就可以在里面随便动手脚

【4】元类的基本使用

(1)需求

  • 要求所有类的名字不能有下划线_

(2)思考

  • 我们应该在哪里定制这些代码
    • 类的产生过程也许我们还不熟悉
    • 但是对象的产生过程,是通过类内的 __init__ 方法实现的
    • 那我们猜,在元类里面也有一个 __init__方法

(3)基本使用

class MyType(type):
    def __init__(cls, class_name, class_bases, class_dict):
        print("MyType的__init__被调用,class_name是:", class_name)
        print("MyType的__init__被调用,class_bases是:", class_bases)
        print("MyType的__init__被调用,class_dict是:", class_dict)
        super().__init__(class_name, class_bases, class_dict)


# 元类的使用区别于我们继承中使用父类的那种形式
# 元类的使用采用  metaclass 关键字声明
# 在前面学习 abc 模块的时候,我们也使用过类似的语法

# 【1】创建一个类
class MyClass(object, metaclass=MyType):
    ...


# 【2】初始化类
MyClass()

# MyType的__init__被调用,class_name是: MyClass
# MyType的__init__被调用,class_bases是: (<class 'object'>,)
# MyType的__init__被调用,class_dict是: {'__module__': '__main__', '__qualname__': 'MyClass'}

(4)进阶使用

  • 限制类名不能有下划线
class MyType(type):
    def __init__(cls, class_name, class_bases, class_dict):
        print("MyType的__init__被调用,class_name是:", class_name)
        print("MyType的__init__被调用,class_bases是:", class_bases)
        print("MyType的__init__被调用,class_dict是:", class_dict)
        # super().__init__(class_name, class_bases, class_dict)
        if '_' in class_name:
            raise NameError('类名不能有下划线')


class H_uman(metaclass=MyType):
    def __init__(self, name, age):
        self.name = name
        self.age = age


obj = H_uman('hqq', 21)

'''
Traceback (most recent call last):
  File "D:\python28\面向对象\导读.py", line 899, in <module>
    class H_uman(metaclass=MyType):
  File "D:\python28\面向对象\导读.py", line 896, in __init__
    raise NameError('类名不能有下划线')
NameError: 类名不能有下划线
'''
  • 类名有下划线有会抛出错误

【5】元类的进阶使用

(1)引入

  • 当对象用括号调用时,必定会调用对象所属类的里面的__call__方法,并且得到对应的返回值
  • 对象() ---->类里面的__call__方法
  • 类()---->自定元类里面的__call__方法
  • 自定元类()---->内置元类里面的__call__方法
class MyType(type):
    def __init__(cls, class_name, class_bases, class_dict):
        print("MyType的__init__被调用,class_name是:", class_name)
        print("MyType的__init__被调用,class_bases是:", class_bases)
        print("MyType的__init__被调用,class_dict是:", class_dict)
        if '_' in class_name:
            raise NameError('类名不能有下划线')
        super().__init__(class_name, class_bases, class_dict)

    def __call__(self, *args, **kwargs):
        print('我被MyType里面的call方法触发了')
        print(args, kwargs)
        obj = super().__call__(*args, **kwargs)
        return obj


class Human(metaclass=MyType):
    def __init__(self, name):
        self.name = name



obj = Human('hqq')
'''
MyType的__init__被调用,class_name是: Human
MyType的__init__被调用,class_bases是: ()
MyType的__init__被调用,class_dict是: {'__module__': '__main__', '__qualname__': 'Human', '__init__': <function Human.__init__ at 0x0000028A154340E0>}
我被MyType里面的call方法触发了
('hqq',) {}

'''

(2)定制对象产生过程

class MyType(type):
    def __init__(cls, class_name, class_bases, class_dict):
        print("MyType的__init__被调用,class_name是:", class_name)
        if '_' in class_name:
            raise NameError('类名不能有下划线')
        super().__init__(class_name, class_bases, class_dict)

    def __call__(self, *args, **kwargs):
        print('我被MyType里面的call方法触发了')
        print(args, kwargs)
        if kwargs:
            raise TypeError('必须用位置传参')
        obj = super().__call__(*args, **kwargs)
        return obj


class Human(metaclass=MyType):
    def __init__(self, name):
        self.name = name



obj = Human(name='hqq')

'''
Traceback (most recent call last):
  File "D:\python28\面向对象\导读.py", line 912, in <module>
    obj = Human(name='hqq')
          ^^^^^^^^^^^^^^^^^
  File "D:\python28\面向对象\导读.py", line 901, in __call__
    raise TypeError('必须用位置传参')
TypeError: 必须用位置传参

'''

【6】__new__方法

  • __new__用于产生空对象(类),相当于人的骨架
  • __init__用于实例化对象(类),相当于人的血肉
class MyType(type):
    def __init__(cls, class_name, class_bases, class_dict):
        print("MyType的__init__被调用,class_name是:", class_name)
        if '_' in class_name:
            raise NameError('类名不能有下划线')
        super().__init__(class_name, class_bases, class_dict)

    def __call__(self, *args, **kwargs):
        print('我被MyType里面的call方法触发了')
        print(args, kwargs)
        if kwargs:
            raise TypeError('必须用位置传参')
        obj = super().__call__(*args, **kwargs)
        return obj

    def __new__(cls, *args, **kwargs):
        print('我在MyType中被__new__执行了')
        return super().__new__(cls,*args, **kwargs)


class Human(metaclass=MyType):
    def __init__(self, name):
        self.name = name

posted @ 2024-01-13 23:22  HuangQiaoqi  阅读(3)  评论(0编辑  收藏  举报