Python学习笔记(十二):类和对象
关于类和对象
- Python学习笔记(十二):类和对象
- 一.父类方法重写
- 二.调用父类的构造方法
-
- 三.类的特殊方法
-
- __slots __
- type()
- issubclass和isinstance函数
- __bases __ 和__subclasses __()
- 四.Enum枚举类
-
- 五.MetaClass
- 六.多态
一.父类方法重写
重写(覆盖),指的是对类中已有方法的内部实现进行修改
使用类名调用其类方法,不会为该方法的第一个self参数自定绑定值,需要手动为self参数赋值
通过类名调用实例方法又被称为未绑定方法
class test:
def name(self):
print("name")
def age(self):
print("age")
class restart(test):
def name(self):
print("youchanwill")
TEST = restart()
TEST.name()
youchanwill
显然调用的是重写之后的name()类方法
调用被重写的方法,调用该函数时备注类名
TEST = restart()
test.name(TEST)
name
使用类名调用其类方法,需要手动为该方法的第一个self参数赋值
通过类名调用实例方法称为未绑定方法
二.调用父类的构造方法
如果子类继承的多个父类中包含同名的类实例方法,则子类对象在调用该方法时,会优先选择排在最前面的父类中的实例方法
class test1:
def __init__(self,name):
self.name = name
def say(self):
print("Name:",self.name)
class test2:
def __init__(self,age):
self.age = age
def display(self):
print("Age:",self.age)
class test3(test1, test2):
pass
TEST = test3("youchanwill")
TEST.say()
Name: youchanwill
TEST.display()
AttributeError: 'test3' object has no attribute 'age'
test3类同时继承test1和test2,其中test1在前,所以在创建TEST对象时,会调用从test1继承来的构造函数
因为test1类的构造方法“遮蔽”了test2类的构造方法,所以在创建TEST对象时,test2类的构造方法未得到执行导致出错
super()
在子类中的构造方法中,调用父类构造方法的方式:
1.未绑定方法
2.使用super()函数,涉及多继承,该函数只能调用第一个直接父类的构造方法
涉及到多继承时,在子类构造函数中,调用第一个父类构造方法的方式有以上2 种,调用其它父类构造方法只能使用未绑定方法
super() 函数语法格式
super().__init__(self,...)
class test1:
def __init__(self,name):
self.name = name
def say(self):
print("Name:",self.name)
class test2:
def __init__(self,age):
self.age = age
def display(self):
print("Age:",self.age)
class test3(test1, test2):
pass
def __init__(self,name,age):
super().__init__(name)
test2.__init__(self,age)
TEST = test3("youchanwill","20")
TEST.say()
TEST.display()
Name: youchanwill
Age: 20
test3类自定义的构造方法中,调用test1类构造方法,可以使用super() 函数,也可以使用未绑定方法
调用test2类的构造方法,只能使用未绑定方法
三.类的特殊方法
__slots __
__slots__属性,可以避免频繁的给实例对象动态地添加属性或方法
__slots__只能限制为实例对象动态添加属性和方法,无法限制动态地为类添加属性和方法
_slots__属性值其实是元组,只有其中指定的元素,才可以作为动态添加的属性或者方法的名称
class test:
__slots__ = ('name','age')
该类的实例对象仅限于动态添加 name、age这两个属性以及 name()、age()这两个方法
对于动态添加的方法,__slots__ 限制的是其方法名,并不限制参数的个数
class test:
__slots__ = ('name','age')
def name(self,name):
print("正在调用实例方法",self.name)
TEST = test()
TEST.name = "you"
TEST.say = name
TEST.say(TEST,"chanwill")
AttributeError: 'test' object has no attribute 'say'
根据__slots__属性的设置,test类的实例对象是不能动态添加以 say 为名称的方法的
__slots__ 属性限制的对象是类的实例对象,而不是类
def name(self):
print("正在调用实例方法")
test.say = name
TEST = test()
TEST.say()
正在调用实例方法
可以为类动态添加类方法和静态方法
__slots__ 属性对由该类派生出来的子类不起作用
class test:
__slots__ = ('name','age' )
class test1(test):
pass
def name(self):
print("正在调用实例方法")
TEST = test1()
TEST.say = name
TEST.say(TEST)
正在调用实例方法
__slots__ 属性只对当前所在的类起限制作用
如果子类也要限制外界为其实例对象动态地添加属性和方法,必须在子类中设置 __slots__ 属性
如果为子类也设置有__slots__ 属性,那么子类实例对象允许动态添加的属性和方法,是子类中 __slots__ 属性和父类 __slots__ 属性的和
type()
type()函数
查看某个变量的具体类型
创建一个自定义类型(创建一个类)
type(obj)
用来查看某个变量(类对象)的具体类型,obj 表示某个变量或者类对象
type(name, bases, dict)
用来创建类,name 表示类的名称,bases表示一个元组(该类的父类),dict 表示一个字典(类内定义的属性或者方法)
def say(self):
print("you")
test = type("test",(object,),dict(say = say, name = "chanwll"))
TEST = test()
TEST.say()
print(TEST.name)
you
chanwll
当元组 (object,) 中只有一个元素时,最后的逗号不能省略
通过 type() 创建了类,其类名为test,继承自 objects 类,该类中包含一个 say() 方法和一个 name 属性
判断 dict 字典中添加的是方法还是属性:
在key-value对中,值为普通变量("chanwill"),表示为类添加了一个类属性;
如果值为外部定义的函数(say() ),表示为类添加了一个实例方法
使用 type() 函数创建的类,和直接使用 class 定义的类并无差别
在使用 class 定义类时,Python解释器底层是用 type() 来创建这个类
issubclass和isinstance函数
issubclass(cls, class_or_tuple):检查cls是否为后一个类或元组包含的多个类中任意类的子类
isinstance(obj, class_or_tuple):检查obj是否为后一个类或元组包含的多个类中任意类的对象
a_list = [1, 6]
print('[1, 6]是否是list类的实例: ', isinstance(a_list, list))
[1, 6]是否是list类的实例: True
print('list是否是tuple类的子类: ', issubclass(list, tuple))
list是否是tuple类的子类: False
issubclass() 的第一个参数是类名
isinstance() 的第一个参数是变量
issubclass 用于判断是否为子类,而 isinstance() 用于判断是否为该类或子类的实例
issubclass() 和 isinstance() 两个函数的第二个参数都可使用元组
__bases __ 和__subclasses __()
__bases__
可以查看该类的所有直接父类,该属性返回所有直接父类组成的元组
class A:
pass
class B:
pass
class C(A, B):
pass`在这里插入代码片`
print('类A的所有父类:', A.__bases__)
print('类B的所有父类:', B.__bases__)
print('类C的所有父类:', C.__bases__)
类A的所有父类: (<class 'object'>,)
类B的所有父类: (<class 'object'>,)
类C的所有父类: (<class '__main__.A'>, <class '__main__.B'>)
__subclasses__()
查看该类的所有直接子类,该方法返回该类的所有子类组成的列表
class A:
pass
class B:
pass
class C(A, B):
pass
print('类A的所有子类:', A.__subclasses__())
print('类B的所有子类:', B.__subclasses__())
类A的所有子类: [<class '__main__.C'>]
类B的所有子类: [<class '__main__.C'>]
四.Enum枚举类
对于实例化对象个数固定的类,可以用枚举类来定义
from enum import Enum
class Seasons(Enum):
spring = 1
summer = 2
autumn = 3
winter = 4
在Seasons枚举类中,spring, summer, autumn,winter 都是该类的成员(类变量)
枚举类的每个成员都由 2 部分组成,name属性值为该枚举值的变量名(如 spring),value代表该枚举值的序号(通常从1开始)
枚举类不能用来实例化对象,但访问枚举类成员的方式有多种
print(Seasons.spring)
print(Seasons['spring'])
print(Seasons(1))
print(Seasons.spring.value)
print(Seasons.spring.name)
for seasons in Seasons:
print(seasons)
枚举类的相关操作
枚举类成员之间不能比较大小,可以用 == 或者 is 进行比较是否相等
print(Seasons.spring == Seasons.spring)
print(Seasons.spring.name is Seasons.spring.value)
True
False
枚举类中各个成员的值,不能在类的外部做任何修改
__members__ 属性,包含枚举类中所有成员的字典,通过遍历该属性,访问枚举类中的各个成员
for name,member in Seasons.__members__.items():
print(name,"->",member)
spring -> Seasons.spring
summer -> Seasons.summer
autumn -> Seasons.autumn
winter -> Seasons.winter
Python枚举类中各个成员的name必须互不相同,但value可以相同
可以借助@unique 装饰器,当枚举类中出现相同值的成员时,程序会报 ValueError 错误
from enum import Enum,unique
@unique
class Seasons(Enum):
spring = 1
summer = 1
autumn = 1
winter = 1
print(Seasons['summer'])
ValueError: duplicate values found in <enum 'Seasons'>: summer -> spring, autumn -> spring, winter -> spring
除了通过继承Enum类的方法创建枚举类,还可以使用Enum()函数创建枚举类
from enum import Enum
Seasons = Enum("Seasons",('spring','summer','autumn','winter'))
for seasons in Seasons:
print(seasons)
Seasons.spring
Seasons.summer
Seasons.autumn
Seasons.winter
五.MetaClass
MetaClass元类,可以对类内部的定义(类属性和类方法)进行动态的修改
为了实现在创建类时,能够动态地改变类中定义的属性或者方法
MetaClass 元类必须符合以下条件:
1.必须显式继承自 type 类
2.类中需要定义并实现 __new__() 方法,该方法一定要返回该类的一个实例对象,因为在使用元类创建类时,该 __new__() 方法会自动被执行,用来修改新建的类
class FirstMetaClass(type):
def __new__(cls, name, bases, attrs):
attrs['name'] = "you"
attrs['say'] = lambda self: print("调用 say() 实例方法")
return super().__new__(cls,name,bases,attrs)
cls:动态修改的类
name:动态修改的类名
bases:被动态修改的类的所有父类
attr:被动态修改的类的所有属性、方法组成的字典
类继承自 type 类,内部实现了__new__() 方法,所以FirstMetaCLass是一个元类
上面的例子中,通过FirstMetaClass元类创建的类,会额外添加 name 属性和 say() 方法
class test(object,metaclass=FirstMetaClass):
pass
TEST = test()
print(TEST.name)
TEST.say()
you
调用 say() 实例方法
在创建类时,通过在标注父类的同时指定元类(格式为metaclass=元类名)
当Python解释器在创建这该类时,FirstMetaClass元类中的 __new__ 方法就会被调用,从而实现动态修改类属性或者类方法的目的
FirstMetaClass 元类的 __new__() 方法动态地为test类添加了name属性和 say()方法,即便该类在定义时是空类也依然有
对于 MetaClass 元类,多用于创建 API
六.多态
类的多态特性,要满足前提条件:
继承:多态一定是发生在子类和父类之间
重写:子类重写了父类的方法
class test:
def say(self):
print("调用test类的say方法")
class test1(test):
def say(self):
print("调用的是test1类的say方法")
class test2(test):
def say(self):
print("调用的是test2类的say方法")
a = test()
a.say()
a = test1()
a.say()
a = test2()
a.say()
调用test类的say方法
调用的是test1类的say方法
调用的是test2类的say方法
同一变量a在执行同一个say()方法时,a实际表示不同的类实例对象,a.say() 调用的并不是同一个类中的say()方法,这就是多态
class TEST:
def say(self,name):
name.say()
class test:
def say(self):
print("调用的是test类的say方法")
class test1(test):
def say(self):
print("调用的是test1类的say方法")
class test2(test):
def say(self):
print("调用的是test2类的say方法")
a = TEST()
a.say(test())
a.say(test1())
a.say(test2())
调用的是test类的say方法
调用的是test1类的say方法
调用的是test2类的say方法
通过给TEST类中的say() 函数添加一个name参数,其内部利用传入的name调用 say()方法
当调用TEST类中的say()方法时,传给name参数的是哪个类的实例对象,就会调用那个类中的say()方法
Python这种由多态衍生出的编程机制,称为“鸭子模型”或“鸭子类型”