————————————————
在开始本章之前,我们先明确一个关系,对象与类的关系是实例关系,而不是继承关系
对象与类的关系,类似于从一个模板(类)中复制粘贴(实例化)出一个新物体(对象) , 只要对象被创建出来后就不会直接受类的影响
继承的关系,是一种单向链关系,父类会直接影响到子类,而子类不会直接影响到父类
super函数的官方文档: https://docs.python.org/zh-cn/3/library/functions.html#super
类也是“对象”
【type类的实例:】
“用户自定义类”
- (我们先称关键字 class 创建的类为“用户自定义类”)
如果我们尝试查询一个用python内置函数type( )去查询一个“用户自定义类” 得到的反馈是
也就意味着,“用户自定义类”本质上也是type类的一个实例对象而已。
class Name(object):
pass
print(type(Name))
# 输出
<class 'type'>
因为“用户自定义类”的本质还是一个对象,所以类也可以像对象一样进行赋值
class Name(object):
pass
ClassName = Name
objA = 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属性成员
#输出:
#========================
Hibari
Traceback (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):
pass
obj = Name()
print(obj.val)
print(Name.val)
#输出:
#========================
Hibari
Hibari
产生上述结果的原因: 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):
pass
obj = 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 = 1
def __new__(cls):
print("\n创建新类")
cls.val = cls.val+1
return 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):
pass
class 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):
pass
obj = Name() # 这两句语句都调用了 Name实例中__call__的方法
obj = Name.__call__()
“用户自定义类”中的call
- “用户自定义类”的call直接关联是type里定义的call
(这个call的作用是用来帮助“用户自定义类”生成实例的) (注意:在“用户自定义类”中定义的call,和这个call几乎毫无关联)
- 而实例对象的call的直接关联的“用户自定义类”中定义的call
(在生成实例前是上面那个call,生成实例后就被替换掉了)
下面两个范例,其实是做了同一件事
#======================================
class ClassName(object):
def __call__(self):
pass
obj = 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 None
val = 10 # 外部定义的变量
ClassName = type('ClassName', (object,), dict(function = func, value = val))
# 通过type()创建一个新的类
obj = ClassName()
obj.function()
#输出
#========================
New function
10
# 注释
# 下面两种写法是一样的
#========================
dict(function = func, value = val))
{'function':func, 'value':val}
# 如果希望上面那段代码美观一点,可以这么写
#========================
def func(self):
print("New function")
print(self.value)
return None
val = 10
classname = 'ClassName'
superclasses = (object,)
attributedict = {'function':func, 'value':val}
ClassName = type(classname, superclasses, attributedict))
obj = ClassName()
obj.function()
#========================
classname:
说明: 类名
数据类型: 字符串(str)
superclasses:
说明: 超类(基类)
数据类型: type
attributedict :
说明: attribute dict属性字典,用于映射属性和方法的字典
数据类型: 字典(dict)
————————————————
“类”的本质
【对象的生成历程】
如果细心的同学会发现,创建“用户自定义类”的实例,就是在类名后面加个括号
class Name(object):
pass
Name() # 创建实例
由此可知创建实例就是调用call 但是call方法本身并不能生成实例,他只是重载的运算符()
class Name(object):
pass
Name.__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):
pass
Name.__call__() # 创建实例
【伪类】
(这部分请自动忽略,我写着玩的) (这个词是非官方的词,所以别去网上搜了,找不到的) (伪类:仿类工作原理的对象)
根据【对象的生成历程】可以大致了解了类是怎么生成对象的
- “用户自定义类”创建实例化对象,
本质上是通过调用“用户自定义类”(type的对象)中的call( )实现的。
- 而call( )里的本质是调用new生成实例,然后调用init初始化实例
最后将new的返回值(即实例)作为call的反回值
根据上述特点,我们可以做一个伪类(可以生成对象的对象)
class Name(object):
val = "Hibari"
def __new__(cls,s="类"): # 初始化当前(类\伪类),并创建实例对象
cls.__s = s
print(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 obj
objA = 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'>
Hibari
UID:7565634
HibariCN
#============================================
通过最后的修改伪类创建的属性,发现完全可以各改各的,说明成功创建了一个能生成对象的对象(伪类) 当然这也是类的基本原理应该也差不多了
【class 的本质】
所有用class创建的类,本质上都是执行了type( object_or_name, base, dict )这个函数
val = "Hibari"
def func():
pass
Name = 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)
pass
def __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)
pass
def __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
pass
# 由元类创建的实例类
class NewCls(object, metaclass=NameMeta):
pass
#===============================================
# 输出
正在生成新类
正在初始化类
# 元类
class NameMeta(type):
def __init__(self, name, bases, dic):
print("正在初始化类")
super().__init__(name, bases, dic)
pass
def __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
pass
# 由元类创建的实例类
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 = 10
def func():
return None
pass
#===============================================
# 输出
name: NewCls
bases, (<class 'object'>,)
dic:
__module__
__qualname__
val
func
如何在元类中为实例类创建属性和方法
class NameMeta(type):
def __new__(cls, name, bases, dic):
val = "Hibari" # 创建属性和方法
def func(cls): # 注意:func和val 只能被元类和实例类使用
print(cls.val) # 生成的对象,和实例类的派生类无法使用
pass
dic["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),对象-关系映射表