————————————————

在开始本章之前,我们先明确一个关系,对象与类的关系是实例关系而不是继承关系

对象与类的关系,类似于从一个模板(类)中复制粘贴(实例化)出一个新物体(对象) , 只要对象被创建出来后就不会直接受类的影响

继承的关系,是一种单向链关系,父类会直接影响到子类,而子类不会直接影响到父类

super函数的官方文档: https://docs.python.org/zh-cn/3/library/functions.html#super

类也是“对象”

【type类的实例:】

“用户自定义类”

  • (我们先称关键字 class 创建的类为“用户自定义类”)

如果我们尝试查询一个用python内置函数type( )去查询一个“用户自定义类” 得到的反馈是 也就意味着,“用户自定义类”本质上也是type类的一个实例对象而已。

  1. class Name(object):
  2. pass
  3. print(type(Name))
  1. # 输出
  2. <class 'type'>

因为“用户自定义类”的本质还是一个对象,所以类也可以像对象一样进行赋值

  1. class Name(object):
  2. pass
  3. ClassName = Name
  4. objA = ClassName()
  5. objB = Name()
  6. # objA和objB 是同一种类的实例

“动态创建类”

由于“用户自定义类”也只是对象, 因此我们可以像创建对象一样,在程序运行时动态的创建类 (例如在函数中,for循环中,等各种地方使用class关键字创建类)


init( )】:

对象初始化方法

init( )也可以被称为构造方法(前面提过了)

作用:初始化一个新建对象 何时调用:当你在创建一个新的实例对象时

注意:虽然init( )参与初始化对象的过程,但是init( )不是创建对象的方法

  1. class Name(object):
  2. def __init__(self):
  3. self.a="Hibari"
  4. obj = Name()
  5. print(obj.val) #正常输出,obj对象中含有val属性成员
  6. print(Name.val) #报错,Name类中不存在val属性成员
  1. #输出:
  2. #========================
  3. Hibari
  4. Traceback (most recent call last):
  5. File "E/", line 9, in <module>
  6. AttributeError: type object 'Name' has no attribute 'val'

产生上述结果的原因: 因为 self.a 这个属性是在init当中被定义的 而init这个方法会对创建出来的实例对象进行初始化(只要创建一个新的对象,就会被运行一次) (例如:添加新的属性和方法等初始化工作) 因此 self.a 只存在于 由Name类创建的对象obj中,而不存在于Name类中

通过dir( )方法查询即可

  1. class Name(object):
  2. def __init__(self):
  3. self.a="Hibari"
  4. obj = Name()
  5. for i in dir(obj):
  6. print("对象中的属性或者方法:", i)
  7. for i in dir(Name):
  8. print("类中的属性或者方法:", i)

QQ截图20210202154336.png


new( )】:

类初始化方法

new( )用于初始一个新建的类,用法和init( )基本一致

作用:初始化一个新建的类,并返回一个类type的实例即“用户自定义类” 何时调用:当你在创建一个新的类时(事实上是每次创建对象时都会运行的一次该方法)

注意:虽然new( )参与初始化“用户自定义类”和创建新实例对象的过程,但是new( )不是创建类的方法

提示:“用户自定义类”是type类的实例对象,由此可知“用户自定义类”是由type类中的new( )实例化后生成的

  1. class Name(object):
  2. def __new__(cls):
  3. cls.val = "Hibari"
  4. return super(Name, cls).__new__(cls)
  5. def __init__(self):
  6. pass
  7. obj = Name()
  8. print(obj.val)
  9. print(Name.val)
  1. #输出:
  2. #========================
  3. Hibari
  4. Hibari

产生上述结果的原因: cls.val 是在new中被定义的 而new这个方法会在类创建之初对类进行初始化,(只要创建一次新的类就会被运行一次) (例如:添加新的属性和方法等初始化工作) 因为cls.val是在类创建时添加进类的, 所以无论是由Name类创建的对象obj,还是Name类本身,都会有cls.val这个属性

  1. class Name(object):
  2. def __new__(cls):
  3. cls.val = "Hibari"
  4. return super(Name, cls).__new__(cls)
  5. def __init__(self):
  6. pass
  7. obj = Name()
  8. for i in dir(obj):
  9. print("对象中的属性或者方法:", i)
  10. for i in dir(Name):
  11. print("类中的属性或者方法:", i)

QQ截图20210202162439.png

运行特点

new会在每次创建实例的时候对类进行初始化,并完成创建实例的任务

  • (return super( ).new(cls) 就是用来创建实例的语句,这句语句在new( )中必不可少)

如果尝试在new方法中修改属性,会对类造成影响,

  • (也会对已创建的实例对象造成影响)


为什么new( )每次都是先运行,然后再运行init( )? 因为要先有类,并创建出对象后,才能对对象进行初始化

  1. class Name(object):
  2. val = 1
  3. def __new__(cls):
  4. print("\n创建新类")
  5. cls.val = cls.val+1
  6. return super(Name, cls).__new__(cls) # 这个返回值是__new__的关键,是用来创建实例的
  7. def __init__(self):
  8. print("创建新实例")
  9. for i in range(4):
  10. obj = Name()
  11. print("查询实例中的属性val值:", obj.val)

QQ截图20210203020228.png

self 和 cls

(self 和 cls 都是自定义的名称,但是按照规范,默认写成self和cls,这样看范例也方便) 从下边的案例可以知道 cls通常在类中,用来表示类自己 self通常在对象中,用来表示对象自己

  1. class Base(object):
  2. pass
  3. class ClassName(Base):
  4. def __new__(cls):
  5. print("cls:", cls)
  6. print("cls的类型:", type(cls))
  7. print("")
  8. return super().__new__(cls)
  9. def __init__(self):
  10. print("self:", self)
  11. print("self的类型:", type(self))
  12. obj = ClassName()
  13. print("")
  14. print("obj:", obj)
  15. print("obj的类型:",type(obj))
  16. print("")
  17. print("ClassName:", ClassName)
  18. print("obj的类型:", type(ClassName))
  1. # 输出
  2. #==================================================
  3. cls <class '__main__.ClassName'>
  4. cls的类型: <class 'type'>
  5. self <__main__.ClassName object at 0x0000021A0AF4D7F0>
  6. self的类型: <class '__main__.ClassName'>
  7. obj: <__main__.ClassName object at 0x0000021A0AF4D7F0>
  8. obj的类型: <class '__main__.ClassName'>
  9. ClassName: <class '__main__.ClassName'>
  10. obj的类型: <class 'type'>

call( )】:

重载运算符括号( )

作用: call这个方法可以用来重载括号( )这个运算符 对象名.call(参数) 可以直接写成 对象名.(参数)

何时被调用: 这个方法一般在创建对象时不常用,但在创建“用户自定义类”时,和调用函数方法时会被调用

  • (一个由 用户创建的函数 是一个实例,是类的实例对象)
  • (“用户自定义类”也是一个实例的实例对象)

特点: 无法被(类)自己直接使用,只能由被(类)创建出来的实例对象使用

  • (具体原因还是请参考手册,这只是我测试出来的结果)
  1. class Name(object):
  2. def __call__(self, s):
  3. print("正在运行call函数:",s)
  4. obj = Name()
  5. obj("Hibari") # 这两句语句是没有区别的
  6. obj.__call__("Hibari") # 即 obj("Hibari") 和 obj.__call__("Hibari") 是相同的语句
  1. # 输出
  2. #==================================================
  3. 正在运行call函数: Hibari
  4. 正在运行call函数: Hibari

无法被自己直接调用

call】的特点: 只有(类)进行实例出来的对象,才能通过重载的方式去调用call 类本身无法直接使用call

原因: “用户自定义类”本身也是一个实例对象,是的实例对象,里面也有call, 无法直接调用是为了防止“用户自定义类”callcall发生冲突

  • (所以 Name类 使用重载运算符()去调用call是调用call
  • (而 Name类实例化 obj对象 使用重载运算符()去调用call是,是调用了 Name类 的call
  1. class Name(object):
  2. def __call__(self, s):
  3. print("正在运行-call方法")
  4. try:
  5. Name("Hibari") # 类尝试用重载的方法调用__call__
  6. except TypeError as e:
  7. print("TypeError:", e)
  8. Name.__call__(Name, "Hibari") # 类直接调用__call__
  9. obj = Name() # 将类实例化成对象
  10. obj.__call__("Hibari") # 对象直接调用__call__
  11. obj("Hibari") # 对象通过重载的方式调用__call__
  1. # 输出
  2. #==================================================
  3. TypeError: object() takes no parameters
  4. 正在运行-call方法
  5. 正在运行-call方法
  6. 正在运行-call方法

类创建实例与call

当我们创建一个“用户自定义类”时,本质上是创建了一个type类的实例

细心的朋友会发现,当“用户自定义类”要创建实例对象时, 必须在“用户自定义类”的名字后面跟一个括号()

其实本质上 就是调用了type类实例化出来的 对象“用户自定义类”中的call方法

  1. class Name(object):
  2. pass
  3. obj = Name() # 这两句语句都调用了 Name实例中__call__的方法
  4. obj = Name.__call__()

“用户自定义类”中的call

  • “用户自定义类”的call直接关联是type里定义的call

(这个call的作用是用来帮助“用户自定义类”生成实例的) (注意:在“用户自定义类”中定义的call,和这个call几乎毫无关联)

  • 而实例对象的call的直接关联的“用户自定义类”中定义的call

(在生成实例前是上面那个call,生成实例后就被替换掉了)

  1. 下面两个范例,其实是做了同一件事
  2. #======================================
  3. class ClassName(object):
  4. def __call__(self):
  5. pass
  6. obj = ClassName() # 调用由type类创建的实例ClassName对象中的__call__():(其实就是创建对象)
  7. obj() # 调用由ClassName类创建的实例obj对象中的__call__()
  8. # 这两__call__除了名字一样外,毫无关联
  9. #======================================
  10. class ClassName(object):
  11. def __call__(self):
  12. obj = ClassName.__call__() #
  13. obj.__call__() #

【type( )函数:】

生成类:type( )

除了使用 calss来创建类以外,其实还可以通过下面这个方法来生成一个类

L- 元类 - 图4

  1. def func(self): # 外部定义的函数
  2. print("New function")
  3. print(self.value)
  4. return None
  5. val = 10 # 外部定义的变量
  6. ClassName = type('ClassName', (object,), dict(function = func, value = val))
  7. # 通过type()创建一个新的类
  8. obj = ClassName()
  9. obj.function()
  1. #输出
  2. #========================
  3. New function
  4. 10
  1. # 注释
  2. # 下面两种写法是一样的
  3. #========================
  4. dict(function = func, value = val))
  5. {'function':func, 'value':val}
  1. # 如果希望上面那段代码美观一点,可以这么写
  2. #========================
  3. def func(self):
  4. print("New function")
  5. print(self.value)
  6. return None
  7. val = 10
  8. classname = 'ClassName'
  9. superclasses = (object,)
  10. attributedict = {'function':func, 'value':val}
  11. ClassName = type(classname, superclasses, attributedict))
  12. obj = ClassName()
  13. obj.function()
  14. #========================
  15. classname
  16. 说明: 类名
  17. 数据类型: 字符串(str
  18. superclasses
  19. 说明: 超类(基类)
  20. 数据类型: type
  21. attributedict
  22. 说明: attribute dict属性字典,用于映射属性和方法的字典
  23. 数据类型: 字典(dict

————————————————


“类”的本质

【对象的生成历程】

如果细心的同学会发现,创建“用户自定义类”的实例,就是在类名后面加个括号

  1. class Name(object):
  2. pass
  3. Name() # 创建实例

由此可知创建实例就是调用call 但是call方法本身并不能生成实例,他只是重载的运算符()

  1. class Name(object):
  2. pass
  3. Name.__call__() # 创建实例(本质是调用__call__)

真正用来创建实例的方法是new方法,new能先初始化类后,返回实例 但上面我们在创建实例对象时,我们只调用了call

如果我们希望 call能返回实例,就必须把new放到call中, 并把new的返回值作为call的返回值 这样在调用call时就能返回实例了 (注:这个call是由type内定义的,所以没有写在class里)

  1. class Name(object):
  2. def __new__(cls):
  3. return super(Name, cls).__new__(cls)
  4. Name.__call__() # 创建实例

同时我们知道,在new运行完成之后,会使用init进行实例化操作 因此,也一起也会把init一起放进call

  1. class Name(object):
  2. def __new__(cls):
  3. return super(Name, cls).__new__(cls)
  4. def __init__(self):
  5. pass
  6. Name.__call__() # 创建实例

【伪类】

(这部分请自动忽略,我写着玩的) (这个词是非官方的词,所以别去网上搜了,找不到的) (伪类:仿类工作原理的对象)

根据【对象的生成历程】可以大致了解了类是怎么生成对象的

  • “用户自定义类”创建实例化对象,

本质上是通过调用“用户自定义类”(type的对象)中的call( )实现的。

  • call( )里的本质是调用new生成实例,然后调用init初始化实例

最后将new的返回值(即实例)作为call的反回值

根据上述特点,我们可以做一个伪类(可以生成对象的对象)

  1. class Name(object):
  2. val = "Hibari"
  3. def __new__(cls,s="类"): # 初始化当前(类\伪类),并创建实例对象
  4. cls.__s = s
  5. print(cls.__s,":正在创建对象")
  6. return super(Name, cls).__new__(cls)
  7. def __init__(self): # 初始化对象
  8. print(self.__s,":正在初始化对象")
  9. self.__s = "生成对象"
  10. print(self.__s,"-数据类型为:",type(self),"\n")
  11. def __call__(self): # 重载运算符,并仿照类生成对象的方式运行__new__和__init__
  12. obj = self.__new__(Name,"伪类")
  13. obj.__init__()
  14. return obj
  15. objA = Name() #由类生成实例对象伪类
  16. obj1 = objA() #由伪类生成实例对象
  17. obj2 = objA()
  18. obj1.val="UID:7565634" # 分别尝试修改伪类创建的对象的属性值
  19. obj2.val="HibariCN" #
  20. print(objA.val) #查询伪类中属性
  21. print(obj1.val) #分别查询伪类创建的对象属性
  22. print(obj2.val)
  1. #输出
  2. #============================================
  3. :正在创建对象
  4. :正在初始化对象
  5. 生成对象 -数据类型为: <class '__main__.Name'>
  6. 伪类 :正在创建对象
  7. 伪类 :正在初始化对象
  8. 生成对象 -数据类型为: <class '__main__.Name'>
  9. 伪类 :正在创建对象
  10. 伪类 :正在初始化对象
  11. 生成对象 -数据类型为: <class '__main__.Name'>
  12. Hibari
  13. UID:7565634
  14. HibariCN
  15. #============================================

通过最后的修改伪类创建的属性,发现完全可以各改各的,说明成功创建了一个能生成对象的对象(伪类) 当然这也是类的基本原理应该也差不多了


【class 的本质】

所有用class创建的类,本质上都是执行了type( object_or_name, base, dict )这个函数

  1. val = "Hibari"
  2. def func():
  3. pass
  4. Name = type("Name", (object,), {"val": val, "func": func})

只不过调用type这个过程通过重载隐藏掉了而已, 取而代之的是熟悉的class定义方式

  1. class Name(object):
  2. val = "Hibari"
  3. def func():
  4. pass

————————————————


元类:MetaClass

type类: 所有的“用户自定义类”都是它的实例化对象 所有的元类(MetaClass)都是以他为基础的派生类

元类: 是继承自type类的类,具有和type相似的属性,元类的实例化对象就是类 元类也被称为超类(从类的角度上看,称为类模板应该会更好理解)

元类的本质: 元类本质是通过继承type,并修改type继承来的init( )、new( )call( )方法, 并重新重载class方法实现的

约定习惯:为了方便识别类与元类,通常元类名是以 Meta 或者 Metaclass 结尾


定义元类

new(cls, name, bases, dic) 作用: 初始化元类,创建实例类并返回 何时被调用: 用元类创建实例类时 作用对象: 元类

init(self, name, base, dic) 作用: 初始化类时 何时被调用: 当new被调用后 作用对象: 实例类

call(cls, args, *kwargs) 作用: 实例类 创建(实例化) 对象 何时被调用: 当实例类创建对象时被调用 如何调用: 当实例类开始创建对象时,该方法会先调用new,再调用init,最后将实例返 注意: call不参与创建实例类,只参与生成对象

注意1: 元类中定义的方法和属性,只能被实例类调用,不能被实例类创建的对象直接调用 (在实例类中,与私有属性类似私有部分)

注意2: 通常initcall方法直接继承自type类就好了,不必修改

  1. # 定义元类(继承type类)
  2. class NameMeta(type):
  3. def __init__(self, name, bases, dic):
  4. print("正在初始化类")
  5. super().__init__(name, bases, dic)
  6. pass
  7. def __new__(cls, name, bases, dic):
  8. print("正在生成新类")
  9. return type.__new__(cls, name, bases, dic)
  10. def __call__(cls, *args, **kwargs):
  11. obj = cls.__new__(cls)
  12. cls.__init__(cls, *args, **kwargs)
  13. print("正在创建新对象")
  14. return obj

+ 重新重载type( )函数

定义元类后,type类的 type( )函数会被重新重载 (函数名变为元类名)

  1. # 定义元类
  2. class NameMeta(type):
  3. def __new__(cls, name, bases, dic):
  4. return type.__new__(cls, name, bases, dic)
  5. type(name, bases, dic) # type类 用于创建类的函数
  6. NameMeta(name, bases, dic) # 元类NameMeta 用于创建类的函数

创建实例类

通过关键字class 就可以实现创建类了 与平常的类创建方法基本相同,括号内依旧写基类名, 只是最后的一个参数必须是(metaclass = 元类名)

  1. class NewCls(object, metaclass=NameMeta):
  2. pass

示例

  1. # 元类
  2. class NameMeta(type):
  3. def __init__(self, name, bases, dic):
  4. print("正在初始化类")
  5. super().__init__(name, bases, dic)
  6. pass
  7. def __new__(cls, name, bases, dic):
  8. print("正在生成新类")
  9. return type.__new__(cls, name, bases, dic)
  10. def __call__(cls, *args, **kwargs):
  11. obj = cls.__new__(cls)
  12. cls.__init__(cls, *args, **kwargs)
  13. print("正在创建新对象")
  14. return obj
  15. pass
  16. # 由元类创建的实例类
  17. class NewCls(object, metaclass=NameMeta):
  18. pass
  19. #===============================================
  20. # 输出
  21. 正在生成新类
  22. 正在初始化类
  1. # 元类
  2. class NameMeta(type):
  3. def __init__(self, name, bases, dic):
  4. print("正在初始化类")
  5. super().__init__(name, bases, dic)
  6. pass
  7. def __new__(cls, name, bases, dic):
  8. print("正在生成新类")
  9. return type.__new__(cls, name, bases, dic)
  10. def __call__(cls, *args, **kwargs): # 这个方法只有当类开始创建对象时才会被调用
  11. obj = cls.__new__(cls)
  12. cls.__init__(cls, *args, **kwargs)
  13. print("正在创建新对象")
  14. return obj
  15. pass
  16. # 由元类创建的实例类
  17. class NewCls(object, metaclass=NameMeta):
  18. pass
  19. # 由实例类创建的对象
  20. obj = NewCls()
  21. #===============================================
  22. # 输出
  23. 正在生成新类
  24. 正在初始化类
  25. 正在创建新对象

name:是字符串(用来储存实例类的名字) bases:是元组(用来储存基类名字) dic:是字典(用来储存属性和方法)

  1. # 元类
  2. class NameMeta(type):
  3. def __new__(cls, name, bases, dic):
  4. print("name:",name)
  5. print("bases,",bases)
  6. print("dic:")
  7. for i in dic:
  8. print(" ",i)
  9. return type.__new__(cls, name, bases, dic)
  10. pass
  11. # 创建实例类
  12. class NewCls(object, metaclass=NameMeta):
  13. val = 10
  14. def func():
  15. return None
  16. pass
  17. #===============================================
  18. # 输出
  19. name: NewCls
  20. bases, (<class 'object'>,)
  21. dic
  22. __module__
  23. __qualname__
  24. val
  25. func

如何在元类中为实例类创建属性和方法

  1. class NameMeta(type):
  2. def __new__(cls, name, bases, dic):
  3. val = "Hibari" # 创建属性和方法
  4. def func(cls): # 注意:func和val 只能被元类和实例类使用
  5. print(cls.val) # 生成的对象,和实例类的派生类无法使用
  6. pass
  7. dic["function"] = func # 将属性和方法储存到字典里
  8. dic["value"] = val
  9. # 返回一个实例类
  10. return type.__new__(cls, name, bases, dic)
  11. # 创建实例类
  12. class NewCls(object, metaclass=NameMeta):
  13. pass
  14. # 创建对象,并调用对象内的方法
  15. NewCls().function()
  16. #===============================================
  17. # 输出
  18. Hibari

扩展线索:

由于我不是很需要使用元类,所以不继续探索下去了 如果由兴趣,可以去找找下面这两个关键词

  • super( )函数
  • ORM(Object Relational Mapping),对象-关系映射表