Python面向对象详解

⛄面向对象基本概念

面向过程和面向对象,是两种不同的编程方式。

面向过程

  1. 把完成某一个需求的所有步骤,从头到尾逐步实现;
  2. 根据开发需求,将某些功能独立的代码封装成一个又一个函数;
  3. 最后完成的代码,就是顺序地调用不同的函数。

特点:

  • 注重步骤与过程,不注重职责分工;
  • 如果需求复杂,代码会变得很复杂;
  • 开发复杂项目,没有固定的套路,开发难度很大!

面向对象

相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法。

  1. 在完成某一个需求前,首先确定职责——要做的事情(方法)
  2. 根据职责确定不同的对象,在对象内部封装不同的方法(多个);
  3. 最后完成的代码,就是顺序地让不同的对象调用不同的方法

特点

  • 注重对象和职责,不同的对象承担不同的职责;
  • 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路;
  • 需要在面向过程基础上,再学习一些面向对象的语法。

⛄类和对象

类和对象的概念

类和对象是面向对象编程的两个核心概念。

(1)类

  1. 是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用。

    • 特征被称为属性

    • 行为被称为方法

  2. 就相当于制造飞机时的图纸,是一个模板,是负责创建对象

(2)对象

  1. 对象由类创建出来的一个具体存在,可以直接使用。

  2. 哪一个类创建出来的对象,就拥有在哪一个类中定义的:

    • 属性

    • 方法

  3. 对象就相当于用图纸制造的飞机。在程序开发中,应该先有类再有对象

(3)类和对象的关系

  1. 类是模板,对象是根据类这个模板创建出来的,应该先有类再有对象
  2. 类只有一个,而对象可以有很多个
    • 不同的对象之间属性可能会各不相同
  3. 中定义了什么属性和方法,对象中就有什么属性和方法,不可能多,也不可能少。

类的设计

在程序开发中,要设计一个类,通常需要满足一下三个要素:

  1. 类名这类事物的名字,满足大驼峰命名法
  2. 属性这类事物具有什么样的特征;
  3. 方法这类事物具有什么样的行为。

大驼峰命名法:HelloWorld,每一个单词的首字母大写;单词与单词之间没有下划线。

(1)类名的确定

名词提炼法分析整个业务流程,出现的名词,通常就是找到的类。

(2)属性和方法的确定

  • 对象的特征描述,通常可以定义成属性
  • 对象具有的行为动词),通常可以定义成方法

提示:需求中没有涉及的属性或者方法在设计类时,不需要考虑。

⛄面向对象基础语法

dir()内置函数

Python中对象几乎是无所不在的,我们之前学习的变量数据函数都是对象。

Python中可以使用以下两个方法验证:

  1. 标识符/数据后输入一个.,然后按下TAB键,ipython会提示该对象能够调用的方法列表
  2. 使用内置函数dir()传入标识符/数据,可以查看对象内的所有属性及方法

提示:__方法名__格式的方法是Python 提供的内置方法/属性

__new__|方法|创建对象时,会被自动调用
__init__|方法|对象被初始化时,会被自动调用
__del__|方法|对象被从内存中销毁前,会被自动调用
__str__|方法|返回对象的描述信息,print函数输出使用

定义简单的类

面向对象是更大的封装,在一个类中封装多个方法,这样通过这个类创建出来的对象,就可以直接调用这些方法了!!

(1)定义只包含方法的类

# 类名的命名格式符合大驼峰命名法
class 类名:
	def 方法1(self, 参数列表)pass
	def 方法2(self, 参数列表)pass

方法的定义格式和之前学习过的函数几乎一样;区别在于第一个参数必须是self

(2)创建对象

当一个类定义完成之后,要使用这个类来创建对象:

对象变量 = 类名()

self的使用

Python中,给对象设置属性,只需要在类的外部的代码中直接通过.设置一个属性即可(这种方式虽然简单,但是不推荐使用)。

  • 在类封装的方法内部,self就表示当前调用方法的对象自己;
  • 由哪一个对象调用的方法,方法内的self就是哪一个对象的引用;
  • 调用方法时,程序员不需要传递self参数;
  • 在方法内部,可以通过self.访问对象的属性,可以通过self.调用其它对象方法;

在类的外部,通过变量名.访问对象的属性和方法;

在类封装的方法中,通过self.访问对象的属性和方法。

__init__方法

在日常开发中,不推荐在类的外部给对象增加属性;如果在运行时,没有找到属性,程序会报错;对象应该包含有哪些属性,应该封装在类的内部

  1. 当使用类名()创建对象时,会自动执行以下操作:

    • 为对象在内存中分配空间——创建对象

    • 为对象的属性设置初始值——初始化方法(__init__

  2. 这个初始化方法就是__init__方法,__init__是对象的内置方法

__init__方法是专门用来定义一个类具有哪些属性的方法!在__init__方法内部使用self.属性名 = 属性的初始值就可以定义属性

在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对__init__方法进行改造:

  • 把希望设置的属性值,定义成__init__方法的参数
  • 在方法内部使用se1f.属性 = 形参接收外部传递的参数
  • 在创建对象时,使用类名(属性1,属性2..…)调用
class People():
    def __init__(self, name):
        self.name = name
        print("初始化方法%s" % self.name)
xt = People("xiaotang")
xm = People("xiaoming")
# 使用id()函数查看数据在内存中的地址
print(xt, id(xt))
print(xm, id(xm))

# 输出结果
初始化方法xiaotang
初始化方法xiaoming
<__main__.People object at 0x0000027E373FC408> 2741116060680
<__main__.People object at 0x0000027E37E98348> 2741127185224

__del____str__方法

(1)__del__方法

  1. Python

    • 当使用类名()创建对象时,为对象分配完空间后,自动调用__init__方法;

    • 当一个对象被从内存中销毁前,自动调用__del__方法。

  2. 应用场景

    • __init__改造初始化方法,可以让创建对象更加灵活;

    • __del__如果希望在对象被销毁前,再做一些事情,可以考虑一下__del__方法。

  3. 生命周期

    • 一个对象从调用类名()创建,生命周期开始;

    • 一个对象的__del__方法一旦被调用,生命周期结束;

    • 在对象的生命周期内,可以访问对象属性,或者让对象调用方法。

class People():
    def __init__(self, name):
        self.name = name
        print("初始化方法%s" % self.name)
    def __del__(self):
        print("对象销毁,%s" % self.name)
xt = People("xiaotang")
xm = People("xiaoming")
print(xt, id(xt))
print(xm, id(xm))

# 输出结果
初始化方法xiaotang
初始化方法xiaoming
<__main__.People object at 0x0000027E37EC63C8> 2741127373768
<__main__.People object at 0x0000027E37EC6448> 2741127373896
对象销毁,xiaotang
对象销毁,xiaoming

(2)__str__方法

  • Python中,使用print 输出对象变量,默认情况下,会输出这个变量引用的对象是由哪一个类创建的对象,以及在内存中的地址十六进制表示);
  • 如果在开发中,希望使用print输出对象变量时,能够打印自定义的内容,就可以利用__str__这个内置方法了。

注意:__str__方法必须返回一个字符串

class People():
    def __init__(self, name):
        self.name = name
        print("初始化方法%s" % self.name)
    def __del__(self):
        print("对象销毁,%s" % self.name)
    def __str__(self):
        return "我的名字:%s" % self.name
xt = People("xiaotang")
xm = People("xiaoming")
print(xt, id(xt))
print(xm, id(xm))

# 输出结果(对比上一条代码的输出结果)
初始化方法xiaotang
初始化方法xiaoming
我的名字:xiaotang 2741123224968
我的名字:xiaoming 2741127144648
对象销毁,xiaotang
对象销毁,xiaoming

⛄面向对象三个基本特征

面向对象三大特性:

  1. 封装,根据职责将属性和方法封装到一个抽象的类中。

    • 定义类的准则
  2. 继承,实现代码的重用,相同的代码不需要重复的编写。

    • 设计类的技巧

    • 子类针对自己特有的需求,编写特定的代码

  3. 多态,不同的子类对象调用相同的父类方法,产生不同的执行结果。

    • 多态可以增加代码的灵活度

    • 以继承和重写父类方法为前提

    • 是调用方法的技巧,不会影响到类的内部设计

⛄封装

封装概述

  • 封装是面向对象编程的一大特点;
  • 面向对象编程的第一步,将属性方法封装到一个抽象的中;
  • 外界使用创建对象,然后让对象调用方法
  • 对象方法的细节都被封装在类的内部;
class Gun:
    def __init__(self, model):
        # 枪的型号
        self.model = model
        # 子弹数量
        self.bullet_count = 0
    
    def add_bullet(self, count):
        self.bullet_count += count
    
    def shoot(self):
        if self.bullet_count <= 0:
            print("没有子弹了")
            return
        self.bullet_count -= 1
        print("%s发射子弹[%d]..."%(self.model, self.bullet_count))

class Soldier:
    def __init__(self, name):
        # 姓名
        self.name = name
        # 士兵出事没有枪,None表示什么都没有
        self.gun = None
    
    def fire(self):
        if self.gun is None:
            print("%s没有枪..."% self.name)
            return
        print("冲啊....[%s]"% self.name)
        self.gun.add_bullet(50)
        self.gun.shoot()
        
# 创建对象
ak47 = Gun("ak47")
person = Soldier("xiaotang")
person.fire()
print("*"*20)
person.gun = ak47
person.fire()

# 输出结果
xiaotang没有枪...
********************
冲啊....[xiaotang]
ak47发射子弹[49]...

is与==区别

(1)身份运算符

身份运算符用于比较两个对象的内存地址是否一致,是否是对同一个对象的引用。在Python中针对None比较时,建议使用is判断。

运算符 描述 实例
is is是判断两个标识符是不是引用同一对象 x is y,类似于id(x) == id(y)
is not is not是判断两个标识符是不是引用不同对象 x is not y,类似于id(a) != id(y)

(2)is==区别

is用于判断两个变量引用对象是否为同一个

==用于判断引用变量的值是否相等。

a = [1,2,3]
b = [1,2,3]
print(b is a)
print(b == a)

# 输出结果
False
True

私有属性和私有方法

  1. 在实际开发中,对象某些属性方法可能只希望在对象的内部被使用,而不希望在外部被访问

    • 私有属性就是对象不希望公开的属性;

    • 私有方法就是对象不希望公开的方法;

  2. 在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或者方法

    • 私有属性/方法,在外界不能够被直接访问;

    • 在对象的方法内部,是可以访问对象的私有属性/方法的。

  3. 伪私有属性和私有方法(不建议使用),Python中,并没有真正意义的私有。

    • 在给属性、方法命名时,实际是对名称做了一些特殊处理,使得外界无法访问到;

    • 处理方式:在名称前面加上_类名,即_类名__名称

class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.__height = 180
    def __mimi(self):
        print("我的身高是%d"% self.__height)

xiaotang = People("小汤", 20)
# 私有属性,外部不能访问
# AttributeError: 'People' object has no attribute '__height'
# print(xiaotang.__height)
# AttributeError: 'People' object has no attribute '__mimi'
# xiaotang.__mimi()   
print(xiaotang._People__height)   # 180
xiaotang._People__mimi()

# 输出结果
180
我的身高是180

单例设计模式

  1. 设计模式

    • 设计模式前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案;

    • 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

  2. 单例设计模式

    • 目的–让类创建的对象,在系统中只有唯一的一个实例

    • 每一次执行类名()返回的对象,内存地址是相同的

  3. 单例设计模式的应用场景

    • 音乐播放对象

    • 回收站对象

    • 打印机对象

(1)__new__方法

  • 使用类名()创建对象时,Python的解释器首先会调用__new__方法为对象分配空间

  • __new__是一个由object基类提供的内置的静态方法,主要作用有两个:

    • 在内存中为对象分配空间

    • 返回对象的引用

  • Python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法。

重写__new__方法的代码非常固定:

  • 重写__new__方法一定要return super().__new__(cls)
  • 否则Python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
  • 注意:__new__是一个静态方法,在调用时需要主动传递cls参数
class MusicPlayer(object):
    
    def __new__(cls):
        # 创建对象时,new方法会被自动调用
        print("分配空间")
        # 为对象分配空间,返回对象的引用
        return super().__new__(cls)
    
    def __init__(self):
        print("初始化")

# 创建对象
player = MusicPlayer()
print(player)      

# 输出结果
分配空间
初始化
<__main__.MusicPlayer object at 0x000002343B97DD48>

(2)Python中的单例

单例——让类创建的对象,在系统中只有唯一的一个实例

  • 定义一个类属性,初始值是None,用于记录单例对象的引用
  • 重写__new__方法;
  • 如果类属性is None,调用父类方法分配空间,并在类属性中记录结果;
  • 返回类属性中记录的对象引用
# 单例设计模式实现
class MusicPlayers():
    
    # 记录第一个被创建对象的引用
    instance = None
    
    def __new__(cls):
        print("分配空间")
        # 判断类属性是否为空对象
        if cls.instance is None:
            # 调用父类的方法,为第一个对象分配空间
            cls.instance = super().__new__(cls)
        return cls.instance
    def __init__(self):
        print("初始化")

# 创建多个实例,无论调用多少次创建对象类的方法,得到的对象内存地址都是相同的
player1 = MusicPlayers()
player2 = MusicPlayers()

print(player1)
print(player2)   

# 输出结果
分配空间
初始化
分配空间
初始化
<__main__.MusicPlayers object at 0x000002343B945248>
<__main__.MusicPlayers object at 0x000002343B945248>

(3)存在的问题

问题所在

  • 在每次使用类名()创建对象时,Python的解释器都会自动调用两个方法。

    • __new__分配空间

    • __init__对象初始化

  • 在上一小节对__new__方法改造之后,每次都会得到第一次被创建对象的引用。

  • 但是:初始化方法还会被再次调用

需求:让初始化动作只被执行一次。

解决办法

  • 定义一个类属性init_flag标记是否执行过初始化动作,初始值为False
  • __init__方法中,判断init_flag,如果为False就执行初始化动作;
  • 然后将init_flag设置为True
  • 这样,再次自动调用__init__方法时,初始化动作就不会被再次执行了。
class MPlayers():
    
    # 记录第一个被创建对象的引用
    instance = None
    
    # 记录是否执行过初始化的动作
    init_flag = False
    
    def __new__(cls):
        print("分配空间")
        # 判断类属性是否为空对象
        if cls.instance is None:
            # 调用父类的方法,为第一个对象分配空间
            cls.instance = super().__new__(cls)
        return cls.instance
    
    def __init__(self):
        if MPlayers.init_flag:
            return
        print("初始化了,一次")
        MPlayers.init_flag = True
        
# 创建多个实例,无论调用多少次创建对象类的方法,得到的对象内存地址都是相同的
player3 = MPlayers()
player4 = MPlayers()

print(player3)
print(player4)

# 输出结果
分配空间
初始化了,一次
分配空间
<__main__.MPlayers object at 0x000001E149BB2F08>
<__main__.MPlayers object at 0x000001E149BB2F08>

⛄继承

继承概述

(1)继承的概念:子类拥有父类的所有方法属性

(2)继承的语法

class 类名(父类名):
    pass

子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发;

子类中应该根据职责,封装子类特有的属性和方法

子类 父类
派生类 基类

(3)继承的传递性

  • C类是B类继承,B类又从A类继承;
  • 那么C类就具有B类和A类的所有属性和方法;
  • 子类拥有父类以及父类的父类中封装的所有属性和方法。

方法重写

(1)方法的重写

子类拥有父类的所有方法属性

子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发

(2)应用场景

父类的方法实现不能满足子类需求时,可以对方法进行重写(override

重写父类方法有两种情况:①覆盖父类的方法;②对父类方法进行扩展。

①覆盖父类的方法

  • 如果在开发中,父类的方法实现子类的方法实现完全不同
  • 就可以使用覆盖的方式,在子类中重新编写父类的方法实现;

【具体的实现方式】,就相当于在子类中定义了一个和父类同名的方法并且实现。

重写之后,在运行时,只会调用子类中重写的方法,而不再会调用父类封装的方法

②对父类方法进行扩展

如果在开发中,子类的方法实现中包含父类的方法实现,父类原本封装的方法实现是子类方法的一部分。

使用扩展的方式:

  • 在子类中重写父类的方法;
  • 在需要的位置使用super().父类方法来调用父类方法的执行;
  • 代码其他的位置针对子类的需求,编写子类特有的代码实现。

(3)super

  • Pythonsuper是一个特殊的类;
  • super()就是使用super类创建出来的对象;
  • 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现。

(4)调用父类方法的另外一种方式(知道)

Python 2.x时,如果需要调用父类的方法,还可以使用以下方式:

父类名.方法(self)
  • 这种方式,目前在Python 3.x还支持这种方式
  • 这种方法不推荐使用,因为一旦父类发生变化,方法调用位置的类名同样需要修改

提示:

在开发时,父类名和super()两种方式不要混用;

如果使用当前子类名调用方法,会形成递归调用,出现死循环。

class People:
    
    def __init__(self, name):
        self.name = name
    
    def language(self):
        print("地球语言:汉语、俄语、西班牙语、英语、德语...")
    
class Person(People):
    
    def language(self):
        super().language()
        print("%s是中国人,说汉语"%self.name)
    
xiaotang = Person("小汤") 
print(xiaotang.name)
xiaotang.language()  

# 输出结果
小汤
地球语言:汉语、俄语、西班牙语、英语、德语...
小汤是中国人,说汉语

父类的私有属性和私有方法

父类的私有属性和私有方法

  • 子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法;
  • 子类对象可以通过父类的公有方法间接访问私有属性或私有方法。

私有属性、方法是对象的隐私,不对外公开,外界以及子类都不能直接访问;

私有属性、方法通常用于做一些内部的事情。

class People:
    
    def __init__(self, name):
        self.name = name
        # 私有属性
        self.__num = 8000000000
    
    def language(self):
        print("地球语言:汉语、俄语、西班牙语、英语、德语...")
    
    # 私有方法
    def __protect(self):
        print("人数:%d,这是一个秘密"% self.__num)
   
	# 通过公有方法间接调用私有属性和私有方法
    def people_num(self):
        self.__protect()
    
class Person(People):
    
    def language(self):
        super().language()
        print("%s是中国人,说汉语"%self.name)
        # 访问父类中私有方法,是访问不到的
        # self.__protect()
        self.people_num()
        
xiaotang = Person("小汤") 
print(xiaotang.name)
# 访问父类中私有属性,是访问不到的
# print(xiaotang.__num)
xiaotang.language()  

# 输出结果
小汤
地球语言:汉语、俄语、西班牙语、英语、德语...
小汤是中国人,说汉语
人数:8000000000,这是一个秘密

多继承

子类可以拥有多个父类,并且具有所有父类的属性和方法。

例如:孩子会继承父亲和母亲的特性

# 语法
class 子类名(父类名1,父类名2...):
    pass

多继承的使用注意事项

​ 如果不同的父类中存在同名的方法子类对象在调用方法时,会调用哪一个父类中的方法。开发时,应该尽量避免这种容易产生混淆的情况!如果父类之间存在同名的属性或方法,应该尽量避免使用多继承。

python中的MRO----方法搜索顺序(了解)

  • python中针对类提供了一个内置属性__mro__可以查看方法的搜索顺序。
  • MROmethod resolution order,主要用于在多继承时判断方法、属性的调用路径
print(*.__mro__)
# 输出结果
(<class '__main__.*'>,<class '__main__.*'>,<class 'object'>)
  • 在搜索方法时,是按照__mro__的输出结果从左至右的顺序查找的;
  • 如果在当前类中找到方法,就直接执行,不再搜索;
  • 如果没有找到,就查找下一个类中是否有对应的方法,如果找到,就直接执行,不再搜索;
  • 如果找到最后一个类,还没有找到方法,程序报错。

新式类与旧式(经典)类

objectPython为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看。

  • 新式类:以object为基类的类,推荐使用;
  • 经典类:不以object为基类的类,不推荐使用;
  • Python 3.x中定义类时,如果没有指定父类,会默认使用object作为该类的基类–Python 3.x中定义的类都是新式类;
  • Python 2.x中定义类时,如果没有指定父类,则不会以object作为基类。

​ 新式类和经典类在多继承时–会影响到方法的搜索顺序

为了保证编写的代码能够同时在Python 2.xPython 3.x运行!今后在定义类时,如果没有父类,建议统一继承自object

# 语法
class 类名(object):
    pass

⛄多态

多态,不同的子类对象调用相同的父类方法,产生不同的执行结果。

  • 多态可以增加代码的灵活度

  • 以继承和重写父类方法为前提

  • 是调用方法的技巧,不会影响到类的内部设计

class Dog:
    def __init__(self, name):
        self.name = name
    
    def game(self):
         print("%s简单的玩耍"%self.name)

class T_Dog(Dog):
    def game(self):
        print("%s在天上玩耍"%self.name)

class Person:
    def __init__(self, name):
        self.name = name
    
    def game_with_dog(self, dog):
        print("%s和%s一起快乐的玩耍"%(self.name, dog.name))
        dog.game()
# 定义一个狗对象
# wangcai = Dog("旺财")
wangcai = T_Dog("飞天旺财")
# 定义一个小明对象
X_Ming = Person("小明")
# 调用一起玩耍的方法
X_Ming.game_with_dog(wangcai)

# 输出结果
小明和飞天旺财一起快乐的玩耍
飞天旺财在天上玩耍

⛄类属性和类方法

类的结构

(1)实例

使用类名()创建对象(分配空间、对象初始化)后,内存中就有了一个对象的实实在在的存在–实例。

  • 创建出来的对象叫做类的实例
  • 创建对象的动作叫做实例化
  • 对象的属性叫做实例属性
  • 对象调用的方法叫做实例方法

在程序执行时:

  • 对象各自拥有自己的实例属性

  • 调用对象方法,可以通过self.

    • 访问自己的属性

    • 调用自己的方法

结论

  • 每一个对象都有自己独立的内存空间,保存各自不同的属性;
  • 多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部。

(2)类是一个特殊的对象

Python 中一切皆对象:

  • class AAA:定义的类属于类对象
  • obj1=AAA() 属于实例对象
  • 在程序运行时,同样会被加载到内存;

  • Python中,是一个特殊的对象–类对象;

  • 在程序运行时,类对象在内存中只有一份,使用一个类可以创建出很多个对象实例

  • 除了封装实例属性方法外,类对象还可以拥有自己的属性方法

    • 类属性

    • 类方法

  • 通过类名.的方式可以访问类的属性或者调用类的方法

    • 类名.类属性

    • 类名.方法名()

类属性和实例属性

(1)概念和使用

  • 类属性就是在类对象中定义的属性;
  • 通常用来记录与这个类相关的特征
  • 类属性不会用于记录具体对象的特征。

(2)属性的获取机制

Python中属性的获取存在一个向上查找机制。因此,要访问类属性有两种方式:

  • 类名.类属性
  • 对象.类属性(不推荐),遵循向上查找机制(先在对象内部查找,没有就会向上查找类属性)

注意:如果使用【对象.类属性=值赋值语句】,只会给对象添加一个属性,而不会影响到类属性的值。

class Tool(object):
    # 使用赋值语句,定义类属性,记录创建工具对象的总数
    count = 0
    def __init__(self, name):
        self.name = name
        # 计数
        Tool.count += 1

# 创建工具对象
tool1 = Tool("钳子")
tool2 = Tool("斧头")
tool3 = Tool("锤子")
# 使用Tool类创建了多少个对象
print("现在创建了%d个工具"% Tool.count)

# 输出结果
现在创建了3个工具

类方法和静态方法

(1)类方法

类属性就是针对类对象定义的属性

  • 使用赋值语句在class关键字下方可以定义类属性;
  • 类属性用于记录与这个类相关的特征。

类方法就是针对类对象定义的方法

  • 类方法内部可以直接访问类属性或者调用其他的类方法
# 语法
@classmethod 
def 类方法名(cls):
	pass
  • 类方法需要用修饰器@classmethod来标识,告诉解释器这是一个类方法。

  • 类方法的第一个参数应该是cls

    • 哪一个类调用的方法,方法内的cls就是哪一个类的引用

    • 这个参数实例方法的第一个参数是self类似;

    • 提示:使用其他名称也可以,不过习惯使用cls

  • 通过类名.调用类方法,调用方法时,不需要传递cls参数。

  • 在方法内部

    • 可以通过c1s.访问类的属性

    • 也可以通过c1s.调用其他的类方法

class Tool(object):
    # 使用赋值语句,定义类属性,记录创建工具对象的总数
    count = 0
    def __init__(self, name):
        self.name = name
        # 计数
        Tool.count += 1
    
    @classmethod
    def show_tool_count(cls):
        print("工具对象的总数:%d" % cls.count)

# 创建工具对象
tool1 = Tool("钳子")
tool2 = Tool("斧头")
tool3 = Tool("锤子")
# 使用Tool类创建了多少个对象
print("现在创建了%d个工具"% Tool.count)
Tool.show_tool_count()

# 输出结果
现在创建了3个工具
工具对象的总数:3

在类方法内部,可以直接使用cls访问类属性或者调用类方法。

(2)静态方法

  • 在开发时,如果需要在类中封装一个方法,这个方法:

    • 不需要访问实例属性或者调用实例方法

    • 不需要访问类属性或者调用类方法

  • 这个时候,可以把这个方法封装成一个静态方法语法如下。

# 语法
@staticmethod 
def 静态方法名():
	pass
  • 静态方法需要用修饰器@staticmethod 来标识,告诉解释器这是一个静态方法。

  • 通过类名.调用静态方法,不需要创建对象。

class Game:
    # 类属性
    top_score = 100
    
    # 实例属性,在初始化方法内部定义
    def __init__(self,player_name) -> None:
        self.name = player_name
    
    # 静态方法
    @staticmethod
    def show_help():
        print("帮助信息")
    
    # 类方法
    @classmethod
    def show_top_score(cls):
        # print("历史最高分:%d"%Game.top_score)
        print("历史最高分:%d"%cls.top_score)
    
    # 实例方法
    def start_game(self):
        print("%s开始的游戏"%self.name)

# 【类名.】
Game.show_help()
Game.show_top_score()
# 创建对象,【对象.】
Tom = Game("小汤")
Tom.start_game()

# 输出结果
帮助信息
历史最高分:100
小汤开始的游戏

小结

  1. 实例方法–方法内部需要访问实例属性
  2. 实例方法–方法内部可以使用类名.访问类属性
  3. 类方法–方法内部只需要访问类属性,或类方法
  4. 静态方法–方法内部,不需要访问实例属性和类属性

如果方法内部即需要访问实例属性,又需要访问类属性,应该定义成什么方法?(实例方法)

笔者不才,请多交流!!!

参考文献:黑马程序员《Python入门教程完整版》

欢迎大家关注预览我的博客Blog:HeartLoveLife
能力有限,敬请谅解!!

你可能感兴趣的:(Python,Python基础,面向对象,python,开发语言)