【一】__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__负责定制化该对象,即是在对象创建好之后初始化变量
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】什么是元类
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】总结
- 元类属于面向对象中比较高阶的用法,无特殊需求和特殊情况外不建议使用
- 如果想高度定制类的产生过程
- 如果想高度定制对象的产生过程
【四】元类结合__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