————————————————
在开始本章之前,我们先明确一个关系,对象与类的关系是实例关系,而不是继承关系
对象与类的关系,类似于从一个模板(类)中复制粘贴(实例化)出一个新物体(对象) , 只要对象被创建出来后就不会直接受类的影响
继承的关系,是一种单向链关系,父类会直接影响到子类,而子类不会直接影响到父类
super函数的官方文档: https://docs.python.org/zh-cn/3/library/functions.html#super
类也是“对象”
【type类的实例:】
“用户自定义类”
- (我们先称关键字 class 创建的类为“用户自定义类”)
如果我们尝试查询一个用python内置函数type( )去查询一个“用户自定义类” 得到的反馈是
也就意味着,“用户自定义类”本质上也是type类的一个实例对象而已。
class Name(object):passprint(type(Name))
# 输出<class 'type'>
因为“用户自定义类”的本质还是一个对象,所以类也可以像对象一样进行赋值
class Name(object):passClassName = NameobjA = ClassName()objB = Name()# objA和objB 是同一种类的实例
“动态创建类”
由于“用户自定义类”也只是对象, 因此我们可以像创建对象一样,在程序运行时动态的创建类 (例如在函数中,for循环中,等各种地方使用class关键字创建类)
【init( )】:
对象初始化方法
init( )也可以被称为构造方法(前面提过了)
作用:初始化一个新建对象 何时调用:当你在创建一个新的实例对象时
注意:虽然init( )参与初始化对象的过程,但是init( )不是创建对象的方法
class Name(object):def __init__(self):self.a="Hibari"obj = Name()print(obj.val) #正常输出,obj对象中含有val属性成员print(Name.val) #报错,Name类中不存在val属性成员
#输出:#========================HibariTraceback (most recent call last):File "E/", line 9, in <module>AttributeError: type object 'Name' has no attribute 'val'
产生上述结果的原因: 因为 self.a 这个属性是在init当中被定义的 而init这个方法会对创建出来的实例对象进行初始化(只要创建一个新的对象,就会被运行一次) (例如:添加新的属性和方法等初始化工作) 因此 self.a 只存在于 由Name类创建的对象obj中,而不存在于Name类中
通过dir( )方法查询即可
class Name(object):def __init__(self):self.a="Hibari"obj = Name()for i in dir(obj):print("对象中的属性或者方法:", i)for i in dir(Name):print("类中的属性或者方法:", i)

【new( )】:
类初始化方法
new( )用于初始一个新建的类,用法和init( )基本一致
作用:初始化一个新建的类,并返回一个类type的实例即“用户自定义类” 何时调用:当你在创建一个新的类时(事实上是每次创建对象时都会运行的一次该方法)
注意:虽然new( )参与初始化“用户自定义类”和创建新实例对象的过程,但是new( )不是创建类的方法
提示:“用户自定义类”是type类的实例对象,由此可知“用户自定义类”是由type类中的new( )实例化后生成的
class Name(object):def __new__(cls):cls.val = "Hibari"return super(Name, cls).__new__(cls)def __init__(self):passobj = Name()print(obj.val)print(Name.val)
#输出:#========================HibariHibari
产生上述结果的原因: cls.val 是在new中被定义的 而new这个方法会在类创建之初对类进行初始化,(只要创建一次新的类就会被运行一次) (例如:添加新的属性和方法等初始化工作) 因为cls.val是在类创建时添加进类的, 所以无论是由Name类创建的对象obj,还是Name类本身,都会有cls.val这个属性
class Name(object):def __new__(cls):cls.val = "Hibari"return super(Name, cls).__new__(cls)def __init__(self):passobj = Name()for i in dir(obj):print("对象中的属性或者方法:", i)for i in dir(Name):print("类中的属性或者方法:", i)

运行特点
new会在每次创建实例的时候对类进行初始化,并完成创建实例的任务
- (return super( ).new(cls) 就是用来创建实例的语句,这句语句在new( )中必不可少)
如果尝试在new方法中修改属性,会对类造成影响,
- (也会对已创建的实例对象造成影响)
为什么new( )每次都是先运行,然后再运行init( )? 因为要先有类,并创建出对象后,才能对对象进行初始化
class Name(object):val = 1def __new__(cls):print("\n创建新类")cls.val = cls.val+1return super(Name, cls).__new__(cls) # 这个返回值是__new__的关键,是用来创建实例的def __init__(self):print("创建新实例")for i in range(4):obj = Name()print("查询实例中的属性val值:", obj.val)

self 和 cls
(self 和 cls 都是自定义的名称,但是按照规范,默认写成self和cls,这样看范例也方便) 从下边的案例可以知道 cls通常在类中,用来表示类自己 self通常在对象中,用来表示对象自己
class Base(object):passclass ClassName(Base):def __new__(cls):print("cls:", cls)print("cls的类型:", type(cls))print("")return super().__new__(cls)def __init__(self):print("self:", self)print("self的类型:", type(self))obj = ClassName()print("")print("obj:", obj)print("obj的类型:",type(obj))print("")print("ClassName:", ClassName)print("obj的类型:", type(ClassName))
# 输出#==================================================cls: <class '__main__.ClassName'>cls的类型: <class 'type'>self: <__main__.ClassName object at 0x0000021A0AF4D7F0>self的类型: <class '__main__.ClassName'>obj: <__main__.ClassName object at 0x0000021A0AF4D7F0>obj的类型: <class '__main__.ClassName'>ClassName: <class '__main__.ClassName'>obj的类型: <class 'type'>
【call( )】:
重载运算符括号( )
作用: call这个方法可以用来重载括号( )这个运算符 对象名.call(参数) 可以直接写成 对象名.(参数)
何时被调用: 这个方法一般在创建对象时不常用,但在创建“用户自定义类”时,和调用函数方法时会被调用
- (一个由 用户创建的函数 是一个实例,是类
的实例对象) - (“用户自定义类”也是一个实例
的实例对象) 特点: 无法被(类)自己直接使用,只能由被(类)创建出来的实例对象使用
- (具体原因还是请参考手册,这只是我测试出来的结果)
class Name(object):def __call__(self, s):print("正在运行call函数:",s)obj = Name()obj("Hibari") # 这两句语句是没有区别的obj.__call__("Hibari") # 即 obj("Hibari") 和 obj.__call__("Hibari") 是相同的语句
# 输出#==================================================正在运行call函数: Hibari正在运行call函数: Hibari
无法被自己直接调用
【call】的特点: 只有(类)进行实例出来的对象,才能通过重载的方式去调用call 类本身无法直接使用call
原因: “用户自定义类”本身也是一个实例对象,是
的实例对象,里面也有call, 无法直接调用是为了防止“用户自定义类”call和 的call发生冲突
- (所以 Name类 使用重载运算符()去调用call是调用
的call, - (而 Name类实例化 obj对象 使用重载运算符()去调用call是,是调用了 Name类 的call
class Name(object):def __call__(self, s):print("正在运行-call方法")try:Name("Hibari") # 类尝试用重载的方法调用__call__except TypeError as e:print("TypeError:", e)Name.__call__(Name, "Hibari") # 类直接调用__call__obj = Name() # 将类实例化成对象obj.__call__("Hibari") # 对象直接调用__call__obj("Hibari") # 对象通过重载的方式调用__call__
# 输出#==================================================TypeError: object() takes no parameters正在运行-call方法正在运行-call方法正在运行-call方法
类创建实例与call
当我们创建一个“用户自定义类”时,本质上是创建了一个type类的实例
细心的朋友会发现,当“用户自定义类”要创建实例对象时, 必须在“用户自定义类”的名字后面跟一个括号()
其实本质上 就是调用了type类实例化出来的 对象“用户自定义类”中的call方法
class Name(object):passobj = Name() # 这两句语句都调用了 Name实例中__call__的方法obj = Name.__call__()
“用户自定义类”中的call
- “用户自定义类”的call直接关联是type里定义的call
(这个call的作用是用来帮助“用户自定义类”生成实例的) (注意:在“用户自定义类”中定义的call,和这个call几乎毫无关联)
- 而实例对象的call的直接关联的“用户自定义类”中定义的call
(在生成实例前是上面那个call,生成实例后就被替换掉了)
下面两个范例,其实是做了同一件事#======================================class ClassName(object):def __call__(self):passobj = ClassName() # 调用由type类创建的实例ClassName对象中的__call__():(其实就是创建对象)obj() # 调用由ClassName类创建的实例obj对象中的__call__()# 这两__call__除了名字一样外,毫无关联#======================================class ClassName(object):def __call__(self):obj = ClassName.__call__() #obj.__call__() #
【type( )函数:】
生成类:type( )
除了使用 calss来创建类以外,其实还可以通过下面这个方法来生成一个类

def func(self): # 外部定义的函数print("New function")print(self.value)return Noneval = 10 # 外部定义的变量ClassName = type('ClassName', (object,), dict(function = func, value = val))# 通过type()创建一个新的类obj = ClassName()obj.function()
#输出#========================New function10
# 注释# 下面两种写法是一样的#========================dict(function = func, value = val)){'function':func, 'value':val}
# 如果希望上面那段代码美观一点,可以这么写#========================def func(self):print("New function")print(self.value)return Noneval = 10classname = 'ClassName'superclasses = (object,)attributedict = {'function':func, 'value':val}ClassName = type(classname, superclasses, attributedict))obj = ClassName()obj.function()#========================classname:说明: 类名数据类型: 字符串(str)superclasses:说明: 超类(基类)数据类型: typeattributedict :说明: attribute dict属性字典,用于映射属性和方法的字典数据类型: 字典(dict)
————————————————
“类”的本质
【对象的生成历程】
如果细心的同学会发现,创建“用户自定义类”的实例,就是在类名后面加个括号
class Name(object):passName() # 创建实例
由此可知创建实例就是调用call 但是call方法本身并不能生成实例,他只是重载的运算符()
class Name(object):passName.__call__() # 创建实例(本质是调用__call__)
真正用来创建实例的方法是new方法,new能先初始化类后,返回实例 但上面我们在创建实例对象时,我们只调用了call。
如果我们希望 call能返回实例,就必须把new放到call中, 并把new的返回值作为call的返回值 这样在调用call时就能返回实例了 (注:这个call是由type内定义的,所以没有写在class里)
class Name(object):def __new__(cls):return super(Name, cls).__new__(cls)Name.__call__() # 创建实例
同时我们知道,在new运行完成之后,会使用init进行实例化操作 因此,也一起也会把init一起放进call
class Name(object):def __new__(cls):return super(Name, cls).__new__(cls)def __init__(self):passName.__call__() # 创建实例
【伪类】
(这部分请自动忽略,我写着玩的) (这个词是非官方的词,所以别去网上搜了,找不到的) (伪类:仿类工作原理的对象)
根据【对象的生成历程】可以大致了解了类是怎么生成对象的
- “用户自定义类”创建实例化对象,
本质上是通过调用“用户自定义类”(type的对象)中的call( )实现的。
- 而call( )里的本质是调用new生成实例,然后调用init初始化实例
最后将new的返回值(即实例)作为call的反回值
根据上述特点,我们可以做一个伪类(可以生成对象的对象)
class Name(object):val = "Hibari"def __new__(cls,s="类"): # 初始化当前(类\伪类),并创建实例对象cls.__s = sprint(cls.__s,":正在创建对象")return super(Name, cls).__new__(cls)def __init__(self): # 初始化对象print(self.__s,":正在初始化对象")self.__s = "生成对象"print(self.__s,"-数据类型为:",type(self),"\n")def __call__(self): # 重载运算符,并仿照类生成对象的方式运行__new__和__init__obj = self.__new__(Name,"伪类")obj.__init__()return objobjA = Name() #由类生成实例对象伪类obj1 = objA() #由伪类生成实例对象obj2 = objA()obj1.val="UID:7565634" # 分别尝试修改伪类创建的对象的属性值obj2.val="HibariCN" #print(objA.val) #查询伪类中属性print(obj1.val) #分别查询伪类创建的对象属性print(obj2.val)
#输出#============================================类 :正在创建对象类 :正在初始化对象生成对象 -数据类型为: <class '__main__.Name'>伪类 :正在创建对象伪类 :正在初始化对象生成对象 -数据类型为: <class '__main__.Name'>伪类 :正在创建对象伪类 :正在初始化对象生成对象 -数据类型为: <class '__main__.Name'>HibariUID:7565634HibariCN#============================================
通过最后的修改伪类创建的属性,发现完全可以各改各的,说明成功创建了一个能生成对象的对象(伪类) 当然这也是类的基本原理应该也差不多了
【class 的本质】
所有用class创建的类,本质上都是执行了type( object_or_name, base, dict )这个函数
val = "Hibari"def func():passName = type("Name", (object,), {"val": val, "func": func})
只不过调用type这个过程通过重载隐藏掉了而已, 取而代之的是熟悉的class定义方式
class Name(object):val = "Hibari"def func():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: 通常init和call方法直接继承自type类就好了,不必修改
# 定义元类(继承type类)class NameMeta(type):def __init__(self, name, bases, dic):print("正在初始化类")super().__init__(name, bases, dic)passdef __new__(cls, name, bases, dic):print("正在生成新类")return type.__new__(cls, name, bases, dic)def __call__(cls, *args, **kwargs):obj = cls.__new__(cls)cls.__init__(cls, *args, **kwargs)print("正在创建新对象")return obj
+ 重新重载type( )函数
定义元类后,type类的 type( )函数会被重新重载 (函数名变为元类名)
# 定义元类class NameMeta(type):def __new__(cls, name, bases, dic):return type.__new__(cls, name, bases, dic)type(name, bases, dic) # type类 用于创建类的函数NameMeta(name, bases, dic) # 元类NameMeta 用于创建类的函数
创建实例类
通过关键字class 就可以实现创建类了 与平常的类创建方法基本相同,括号内依旧写基类名, 只是最后的一个参数必须是(metaclass = 元类名)
class NewCls(object, metaclass=NameMeta):pass
示例
# 元类class NameMeta(type):def __init__(self, name, bases, dic):print("正在初始化类")super().__init__(name, bases, dic)passdef __new__(cls, name, bases, dic):print("正在生成新类")return type.__new__(cls, name, bases, dic)def __call__(cls, *args, **kwargs):obj = cls.__new__(cls)cls.__init__(cls, *args, **kwargs)print("正在创建新对象")return objpass# 由元类创建的实例类class NewCls(object, metaclass=NameMeta):pass#===============================================# 输出正在生成新类正在初始化类
# 元类class NameMeta(type):def __init__(self, name, bases, dic):print("正在初始化类")super().__init__(name, bases, dic)passdef __new__(cls, name, bases, dic):print("正在生成新类")return type.__new__(cls, name, bases, dic)def __call__(cls, *args, **kwargs): # 这个方法只有当类开始创建对象时才会被调用obj = cls.__new__(cls)cls.__init__(cls, *args, **kwargs)print("正在创建新对象")return objpass# 由元类创建的实例类class NewCls(object, metaclass=NameMeta):pass# 由实例类创建的对象obj = NewCls()#===============================================# 输出正在生成新类正在初始化类正在创建新对象
name:是字符串(用来储存实例类的名字) bases:是元组(用来储存基类名字) dic:是字典(用来储存属性和方法)
# 元类class NameMeta(type):def __new__(cls, name, bases, dic):print("name:",name)print("bases,",bases)print("dic:")for i in dic:print(" ",i)return type.__new__(cls, name, bases, dic)pass# 创建实例类class NewCls(object, metaclass=NameMeta):val = 10def func():return Nonepass#===============================================# 输出name: NewClsbases, (<class 'object'>,)dic:__module____qualname__valfunc
如何在元类中为实例类创建属性和方法
class NameMeta(type):def __new__(cls, name, bases, dic):val = "Hibari" # 创建属性和方法def func(cls): # 注意:func和val 只能被元类和实例类使用print(cls.val) # 生成的对象,和实例类的派生类无法使用passdic["function"] = func # 将属性和方法储存到字典里dic["value"] = val# 返回一个实例类return type.__new__(cls, name, bases, dic)# 创建实例类class NewCls(object, metaclass=NameMeta):pass# 创建对象,并调用对象内的方法NewCls().function()#===============================================# 输出Hibari
扩展线索:
由于我不是很需要使用元类,所以不继续探索下去了 如果由兴趣,可以去找找下面这两个关键词
- super( )函数
- ORM(Object Relational Mapping),对象-关系映射表
