面向对象内置方法、isinstance和issubclass、元类

【一】__init__(),__del__(),__new__(),__call__

  • Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类
  • 这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发
  • 这个方法是一个类的构造函数,与之对应的__del__()是析构函数,通过此方法我们可以定义一个对象的初始操作。
  • 但实际上,新式类的__new__()才是真正的初始化函数。
class S:
    def __init__(self):
        print(f'__init__')

    def __new__(cls, *args, **kwargs):
        print(f"__new__")
        # cls 表示一个类,一个当前要被实例化的类,参数由py解释器自动提供
        return super().__new__(cls, *args, **kwargs)

    def __del__(self):
        print(f"__del__")


s = S()
print('something')
# 输出结果:
# __new__
# __init__
# something
# __del__

# 实际上,
# __new__()负责创建一个对象,
# __init__负责定制化该对象,即是在对象创建好之后初始化变量
  • 实现实例(__call__
class Student:
    def __init__(self):
        print(f"__init__被触发")

    def __call__(self, *args, **kwargs):
        print(f"__call__被触发")


s = Student() # __init__被触发

# __call__ 可以放在两个位置
# 一个位置在当前对象的父类中 直接对象() 会触发当前类的 __call__
# 另一个位置在当前对象的父类的元类中会触发

# callable : 校验当前对象是够可被调用 ()
# 类本身是可以被任意调用的
print(callable(Student))  # True
# 对象本身是不具备直接被调用的功能的
# 想让对象也能 () 被调用就必须重写 __call__
print(callable(s)) # True

【二】isinstance() 和 issubclass()

class Person(object):
    ...


class Student:
    ...


stu = Student()
person = Person()
# 【一】isinstance(): 判断当前对象是否是某一个类的类型
print(isinstance('chosen', str))  # True
print(isinstance(1, str))  # False


# 【二】issubclass(): 校验一个类是否是属于另一个类的子类
class Animal(object):
    ...


class Dog(Animal):
    ...


class Cat(object):
    ...


print(issubclass(Dog, Animal))  # True
print(issubclass(Cat, Animal))  # False
print(issubclass(Cat, object))  # True

【三】元类

【1】什么是元类

  • 元类是所有类的基类,包括 object
class People(object):
    def __init__(self, name):
        self.name = name


# 【1】实例化类得到类的对象
student = People(name='chosen')
# 【2】查看这个对象的数据类型
print(f'student的数据类型:{type(student)}')
# student的数据类型:<class '__main__.People'>

# 【3】查看产生这个类的数据类型
print(f'People的数据类型:{type(People)}')
# People的数据类型:<class 'type'>

# 【4】查看产生字典的数据类型
print(f'dict 的数据类型: {type(dict)}')
# dict 的数据类型: <class 'type'>

【2】元类的创建方式

(1)直接用关键字创建

  • 语法
关键字(class) 类名(Student)继承的父类(默认是object):
    # 类体代码
  • 示例
class Student(object):
    school = '清华大学'

    def __init__(self, name):
        self.name = name

    def read(self):
        print(f'{self.name}is reading!')


# 【一】查看创建当前类的类
print(type(Student))
# <class 'type'>

# 【二】查看当前类的名称空间
print(Student.__dict__)
# {'__module__': '__main__', 'school': '清华大学', '__init__': <function Student.__init__ at 0x000001879E6F3A30>, 'read': <function Student.read at 0x000001879E9C57E0>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}

(2)使用type生成

  • 语法
类名 = type('类名',(父类1,父类2),名称空间字典)
  • 实例
Student = type('Student', (object,), {'name': 'chosen'})
print(Student) # <class '__main__.Student'>
print(type(Student)) # <class 'type'>

# 如果是类的名称空间字典,一定会看到除了自己定义的属性以外的很多属性,自带的属性
# 如果是对象的民称空间字典,一定会看到只有__init__ 初始化后的属性

print(Student.__dict__) # {'name': 'chosen', '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}

【3】元类的使用

(1)为什么要学元类?

  • 元类可以控制类的创建,也就意味着我们可以高度定制类的具体行为
    • 比如当我们掌控了食品的生产过程,我们就可以在里面随意改动
  • 我们知道了元类能创建类
    • 就可以在元类创建类的过程中进行修改和增加

(2)元类的基本使用

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'}

(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)
        if not class_name.istitle():
            raise TypeError(f'类名 {class_name} 必须首字母大写')
        super().__init__(class_name, class_bases, class_dict)


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

# 【1】创建一个符合规范的类
# (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'}

# 【二】创建一个不符合规范的类
# (1)创建类
class myclass(object, metaclass=MyType):
    ...


# (2)初识化类
myclass()

# 不符合规范会直接报错
#     raise TypeError(f'类名 {class_name} 必须首字母大写')
# TypeError: 类名 myclass 必须首字母大写

【4】元类的进阶使用(__call__方法)

  • 在类内部定义一个 __call__方法,对象加括号会自动执行产生该对象的类里面的 __call__方法,并且可以得到对应的返回值
  • 类加括号调用,同理类加括号也会触发元类中的__call__方法,从而获得返回值,而这个返回值正是我们实例化类所得到的对象
class MyType(type):
    def __init__(cls, class_name, class_bases, class_dict):
        print(f'MyType 中的 init 方法 我被触发了')
        super().__init__(class_name, class_bases, class_dict)

    def __call__(self, *args, **kwargs):
        print(f'MyType 中的 call 方法 我被触发了')
        print(f'args: {args}, kwargs: {kwargs}')
        # 创建对象并初始化参数
        obj = super().__call__(*args, **kwargs)
        # print(obj)
        # 将产生的对象返回出去
        return obj


class MyClass(metaclass=MyType):
    def __init__(self, name):
        print(f' MyClass 中的 init 方法 我被触发了')
        self.name = name

    def __call__(self, *args, **kwargs):
        print(f' MyClass 中的 call 方法 我被触发了')
        return 'call 方法返回的值'


obj = MyClass(name='chosen')
print(obj)
print(obj())

输出结果:
# Mytype 中的 init方法 被触发了
# Mytype 中的 call方法 被触发了
# args:(), kwargs:{'name': 'chosen'}
# MyClass 中的 init方法 被触发了
# <__main__.MyClass object at 0x0000028DE7A47D60>
# MyClass 中的 call方法 被触发了
# call 方法的返回值
  • 具体步骤示例
class MyType(type):
    def __init__(cls, class_name, class_bases, class_name_space):
        print(f"MyType 的 __init__ 被触发")
        super().__init__(class_name, class_bases, class_name_space)

    # 【三】触发元类中的 __call__ 方法
    def __call__(self, *args, **kwargs):
        # 【四】打印三个参数
        # 【1】当前类本身
        print(f"self :>>>> {self}")  # self :>>>> <class '__main__.MyClass'>
        # 【2】类实例化时候传进来的位置参数
        print(f"args :>>>> {args}")  # args :>>>> ('dream',)
        # 【3】类实例化时候传进来的关键字参数
        print(f"kwargs :>>>> {kwargs}")  # kwargs :>>>> {'age': 18}
        print(f'类名() 触发 MyType 中的 __call__')
        # 【五】如果不写返回值,返回的值默认是就是None
        # 一定要返回一个对象 ---> 自己不会写
        # 自己不知道怎么写但是,以前我不写都能拿到对象 ---> 有人帮你写好了并且是 type 帮你写好了
        # 直接把 type 的拿过来用  super().__call__()
        # 正常返回当前类的对象
        # super() 其实是 type
        obj = super().__call__(*args, **kwargs)
        print(f"obj :>>>> {obj}")  # obj :>>>> <__main__.MyClass object at 0x000002A89A56C4C0>
        # 【六】直接将当前返回的对象返回
        return obj


# 【一】触发 MyType 的 __init__ 方法
class MyClass(metaclass=MyType):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __call__(self, *args, **kwargs):
        print(f'当前 MyClass 的 对象() 触发了 __call__')


# 【二】对象(参数) ---> 触发 MyType 的 __call__ 方法
# 【七】me 接收到的就是 MyType 的 __call__ 方法 返回的 obj 对象
me = MyClass('chosen', age=20)
print(me())
print(me.name)

输出结果:
# MyType 的 __init__ 被触发
# self :>>>>><class '__main__.MyClass'>
# args :>>>>>('chosen',)
# kwargs :>>>>>{'age': 20}
# 类名() 触发 Mytype 中的 __call__
# obj :>>>>><__main__.MyClass object at 0x000001BA7D3A7D00>
# 当前 MyClass 的对象() 触发了 __call__
# None
# chosen

【5】总结

  • 元类属于面向对象中比较高阶的用法,无特殊需求和特殊情况外不建议使用
  • 如果想高度定制类的产生过程
    • 那么编写元类里面的__init__方法
  • 如果想高度定制对象的产生过程
    • 那么编写元类里面的__call__方法

【四】元类结合__new__

  • __new__用于产生空对象(类),相当于人的骨架
  • __init__用于实例化对象(类),相当于人的血肉

【1】类中的__new__

class MyClass(object):
    def __init__(self, name, age):
        print(f"给当前 MyClass 类的对象初始化属性的时候会触发 __init__")
        self.name = name
        self.age = age

    def __call__(self, *args, **kwargs):
        print(f"当前 MyClass 类的对象被调用的时候会触发 __call__")
        return f'当前 MyClass 类 的 __call__ 的返回值 :>>>> {self.name}'

    # 【三】花骨架 有了骨架才能上色
    def __new__(cls, *args, **kwargs):
        print(f"给当前 MyClass 类的对象创建骨架的时候会触发 __new__")

        # 【1】当前类本身
        print(f" MyClass 类 的 __call__ 的 cls :>>>> {cls}")
        # MyClass 类 的 __call__ 的 cls :>>>> <class '__main__.MyClass'>

        # 【2】当前类初始化传的位置参数
        print(f" MyClass 类 的 __call__ 的 args :>>>> {args}")
        # MyClass 类 的 __call__ 的 args :>>>> ('dream',)

        # 【3】当前类初始化传的关键字参数
        print(f" MyClass 类 的 __call__ 的 kwargs :>>>> {kwargs}")
        # MyClass 类 的 __call__ 的 kwargs :>>>> {'age': 18}

        # 【四】调用父类 的 object 的 __new__ 帮我搭建好骨架
        obj = object.__new__(cls)
        # 【1】查看当前返回值发现是一个对象类型
        print(f'obj :>>>> {obj}')
        # obj :>>>> <__main__.MyClass object at 0x000001984B032340>
        # 【2】发现当前对象的民称空间是空的
        print(f'obj.__dict__ :>>>> {obj.__dict__}')
        # obj.__dict__ :>>>> {}
        # 【五】调用自己的 __init__ 方法 初始化属性
        obj.__init__(*args, **kwargs)
        # 【】给自己的名称空间初始化属性
        print(f'obj.__dict__ :>>>> {obj.__dict__}')
        # obj.__dict__ :>>>> {'name': 'dream', 'age': 18}
        return obj


# MyClass 相当于给你一张纸
# 【一】类() 调用 ---> 一定会触发 __init__ 初始化对象的属性 #  __init__  给你人体骨架上个色
# 【二】在调用  __init__  之前要调用 __new__  # __new__ 相当于将你人体的骨架搭建好
m = MyClass('chosen', age=18)
# 【六】完成对象属性的初始化
print(m.name)

输出结果:
# 给当前 MyClass 类的对象创建骨架的时候会触发 __new__
# MyClass 类 的 __call__ 的 cls :>>>> <class '__main__.MyClass'>
# MyClass 类 的 __call__ 的 args :>>>> ('chosen',)
# MyClass 类 的 __call__ 的 kwargs :>>>> {'age': 18}
# obj :>>>> <__main__.MyClass object at 0x00000192DD9D7EE0>
# obj.__dict__ :>>>> {}
# 给当前 MyClass 类的对象初始化属性的时候会触发 __init__
# obj.__dict__ :>>>> {'name': 'chosen', 'age': 18}
# 给当前 MyClass 类的对象初始化属性的时候会触发 __init__
# chosen

【2】元类中的__new__

class MyType(type):
    def __init__(cls, class_name, class_bases, class_name_space):
        print(f"给当前 MyType 类的对象初始化属性的时候会触发 __init__")
        super().__init__(class_name, class_bases, class_name_space)

    def __call__(self, *args, **kwargs):
        # 得到一个空的对象
        obj = super().__call__(*args, **kwargs)
        return obj

    # 【三】花骨架 有了骨架才能上色
    def __new__(cls, *args, **kwargs):
        print(f"给当前 MyType 类的对象创建骨架的时候会触发 __new__")

        # 【1】当前类本身
        print(f" MyType 类 的 __call__ 的 cls :>>>> {cls}")
        #  MyType 类 的 __call__ 的 cls :>>>> <class '__main__.MyType'>

        # 【2】当前类初始化传的位置参数
        print(f" MyType 类 的 __call__ 的 args :>>>> {args}")
        #  MyType 类 的 __call__ 的 args :>>>> ('MyClass', (), {'__module__': '__main__', '__qualname__': 'MyClass', '__init__': <function MyClass.__init__ at 0x0000016DE31ACAF0>, '__call__': <function MyClass.__call__ at 0x0000016DE31ACB80>})

        # 【3】当前类初始化传的关键字参数
        print(f" MyType 类 的 __call__ 的 kwargs :>>>> {kwargs}")
        # MyType 类 的 __call__ 的 kwargs :>>>> {}

        # 【四】让你的父类帮你大骨架
        obj = type.__new__(cls, *args, **kwargs)
        print(f'obj :>>>> {obj}')
        # obj :>>>> <class '__main__.MyClass'>

        print(f'obj.__dict__ :>>>> {obj.__dict__}')
        # obj.__dict__ :>>>> {'__module__': '__main__', '__init__': <function MyClass.__init__ at 0x0000023A62C2CB80>, '__call__': <function MyClass.__call__ at 0x0000023A62C2CC10>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}

        return obj


class MyClass(metaclass=MyType):
    def __init__(self, name, age):
        print(f"给当前 MyClass 类的对象初始化属性的时候会触发 __init__")
        self.name = name
        self.age = age

    def __call__(self, *args, **kwargs):
        print(f"当前 MyClass 类的对象被调用的时候会触发 __call__")
        return f'当前 MyClass 类 的 __call__ 的返回值 :>>>> {self.name}'

# MyClass 相当于给你一张纸
# 【一】类() 调用 ---> 一定会触发 __init__ 初始化对象的属性 #  __init__  给你人体骨架上个色
# 【二】在调用  __init__  之前要调用 __new__  # __new__ 相当于将你人体的骨架搭建好
m = MyClass('chosen', age=20)
# 【六】完成对象属性的初始化
print(m.name)

输出结果:
# 给当前 MyType 类的对象创建骨架的时候会触发 __new__
# MyType 类 的 __call__ 的 cls :>>>> <class '__main__.MyType'>
# MyType 类 的 __call__ 的 args :>>>> ('MyClass', (), {'__module__': '__main__', '__qualname__': 'MyClass', '__init__': <function MyClass.__init__ at 0x000001D9AC7E6290>, '__call__': <function MyClass.__call__ at 0x000001D9AC7E6320>})
# MyType 类 的 __call__ 的 kwargs :>>>> {}
# obj :>>>> <class '__main__.MyClass'>
# obj.__dict__ :>>>> {'__module__': '__main__', '__init__': <function MyClass.__init__ at 0x000001D9AC7E6290>, '__call__': <function MyClass.__call__ at 0x000001D9AC7E6320>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}
# 给当前 MyType 类的对象初始化属性的时候会触发 __init__
# 给当前 MyClass 类的对象初始化属性的时候会触发 __init__
# chosen
posted @ 2024-05-20 08:45  光头大炮  阅读(11)  评论(0编辑  收藏  举报