Python学习笔记(十二):类和对象

Python学习笔记(十二):类和对象

关于类和对象

  • Python学习笔记(十二):类和对象
  • 一.父类方法重写
  • 二.调用父类的构造方法
    • super()
  • 三.类的特殊方法
    • __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):     # 重写test类的name()方法
    def name(self):
        print("youchanwill")

TEST = restart() #创建restart对象
TEST.name() #调用restart类中重写的name() 类方法
youchanwill

显然调用的是重写之后的name()类方法
调用被重写的方法,调用该函数时备注类名                                  

TEST = restart() #创建restart对象
test.name(TEST) #调用test类中的 name() 方法
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): #test1中的name属性和say()会遮蔽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)   #调用test1类的构造方法
        
        #super(test3,self).__init__(name) 执行效果相同
        #test1.__init__(self,name)#使用未绑定方法调用test1类构造方法
        
        test2.__init__(self,age) #调用其它父类的构造方法,需手动给 self传值
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): #test的空子类
    pass

def name(self): #定义的实例方法
    print("正在调用实例方法")
    
TEST = test1()
TEST.say = name #为子类对象动态添加 say() 方法
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")) #使用 type() 函数创建类

TEST = test() #创建一个test实例对象
TEST.say() #调用say()方法和 name 属性
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
[1, 6]是否是list类的实例:  True

print('list是否是tuple类的子类: ', issubclass(list, tuple))# 不是tuple类及其子类的实例,输出False
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     # 为序列值指定value值 
    summer = 2
    autumn = 3
    winter = 4

在Seasons枚举类中,spring, summer, autumn,winter 都是该类的成员(类变量)

枚举类的每个成员都由 2 部分组成,name属性值为该枚举值的变量名(如 spring),value代表该枚举值的序号(通常从1开始)

枚举类不能用来实例化对象,但访问枚举类成员的方式有多种

print(Seasons.spring) #调用枚举成员的 3 种方式
print(Seasons['spring'])
print(Seasons(1))

print(Seasons.spring.value) #调取枚举成员中的value 和 name
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
@unique #添加 unique 装饰器
class Seasons(Enum):
    spring = 1     # 为序列值指定value值 
    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.必须显式继承自 type2.类中需要定义并实现 __new__() 方法,该方法一定要返回该类的一个实例对象,因为在使用元类创建类时,该 __new__() 方法会自动被执行,用来修改新建的类
class FirstMetaClass(type): #定义一个元类
    def __new__(cls, name, bases, attrs):
        attrs['name'] = "you" # 动态为该类添加一个name属性
        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()) #调用test类的 say() 方法
a.say(test1()) #调用test1类的 say() 方法
a.say(test2()) #调用test2的 say() 方法
调用的是test类的say方法
调用的是test1类的say方法
调用的是test2类的say方法

通过给TEST类中的say() 函数添加一个name参数,其内部利用传入的name调用 say()方法

当调用TEST类中的say()方法时,传给name参数的是哪个类的实例对象,就会调用那个类中的say()方法

Python这种由多态衍生出的编程机制,称为“鸭子模型”或“鸭子类型”

你可能感兴趣的:(Python,Note,python,类)