zl程序教程

您现在的位置是:首页 >  后端

当前栏目

python面向对象——封装,继承,多态

Python封装继承 面向对象 多态
2023-09-11 14:17:00 时间

目录

一、类的使用方法

二、魔法方法

2.1__init__()

2.2.__str()__

2.3.__str()__

三、继承

3.1概念

3.2多继承

3.3子类重写父类的同名属性和方法

3.4子类调用父类同名属性和方法

3.5多层继承

3.6super()的使用

四、封装

4.1私有属性和私有方法

4.2 修改私有属性的值

4.3多态

4.4类属性和实例属性

4.4.1类属性

4.4.2实例属性(对象属性)

4.4.3通过实例(对象)去修改类属性

总结

4.5静态方法和类方法

4.5.1. 类方法

4.5.2. 静态方法


一、类的使用方法

面向对象的三大特征有:封装性、继承性、多态性。

  1. :抽象的,是一张“手机设计图”。
  2. 对象:具体的,是一个“真正的手机实例”。

注意看代码!!!!里面包含了定义类、添加属性、方法及调用!!!!

# class Hero:  # 旧式类的定义形式
# class Hero():   # 旧式类的定义形式


class Hero(object):  # 新式类定义形式
    """info 是一个实例方法,类对象可以调用实例方法,实例方法的第一个参数一定是self"""
    def info(self):
        """在类的实例方法中,通过self获取该对象的属性,当对象调用实例方法时,Python会自动将对象本身的引用做为参数,传递到实例方法的第一个参数self里"""
        print("英雄 %s 的生命值 :%d" % (self.name, self.hp))
        print("英雄 %s 的攻击力 :%d" % (self.name, self.atk))
        print("英雄 %s 的护甲值 :%d" % (self.name, self.armor))

    def move(self):
        """实例方法"""
        print("正在前往事发地点...")

    def attack(self):
        """实例方法"""
        print("发出了一招强力的普通攻击...")


# Hero这个类 实例化了一个对象  taidamier(泰达米尔)
taidamier = Hero()

# 给对象添加属性,以及对应的属性值
taidamier.name = "泰达米尔"  # 姓名
taidamier.hp = 2600  # 生命值
taidamier.atk = 450  # 攻击力
taidamier.armor = 200  # 护甲值

print(taidamier)  # 打印对象,则默认打印对象在内存的地址,结果等同于info里的print(self)
print(id(taidamier))  # id(taidamier) 则是内存地址的十进制形式表示
print("英雄 %s 的生命值 :%d" % (taidamier.name, taidamier.hp))

# 对象调用实例方法info(),执行info()里的代码
# . 表示选择属性或者方法
taidamier.info()

注:

  • object 是Python 里所有类的最顶级父类;

  • 类名 的命名规则按照"大驼峰命名法";

  • info 是一个实例方法,第一个参数一般是self,表示实例对象本身,当然了可以将self换为其它的名字,其作用是一个变量 这个变量指向了实例对象

二、魔法方法

2.1__init__()

class Hero(object):
    """定义了一个英雄类,可以移动和攻击"""
    # Python 的类里提供的,两个下划线开始,两个下划线结束的方法,就是魔法方法,__init__()就是一个魔法方法,通常用来做属性初始化 或 赋值 操作。
    # 如果类面没有写__init__方法,Python会自动创建,但是不执行任何操作,
    # 如果为了能够在完成自己想要的功能,可以自己定义__init__方法,
    # 所以一个类里无论自己是否编写__init__方法 一定有__init__方法。

    def __init__(self):
        """ 方法,用来做变量初始化 或 赋值 操作,在类实例化对象的时候,会被自动调用"""
        self.name = "泰达米尔" # 姓名
        self.hp = 2600 # 生命值
        self.atk = 450  # 攻击力
        self.armor = 200  # 护甲值

    def info(self):
        """在类的实例方法中,通过self获取该对象的属性"""
        print("英雄 %s 的生命值 :%d" % (self.name, self.hp))
        print("英雄 %s 的攻击力 :%d" % (self.name, self.atk))
        print("英雄 %s 的护甲值 :%d" % (self.name, self.armor))
        
    def move(self):
        """实例方法"""
        print("正在前往事发地点...")

    def attack(self):
        """实例方法"""
        print("发出了一招强力的普通攻击...")


# 实例化了一个英雄对象,并自动调用__init__()方法
taidamier = Hero()

# 通过.成员选择运算符,获取对象的实例方法
taidamier.info()# 只需要调用实例方法info(),即可获取英雄的属性
taidamier.move()
taidamier.attack()
-------------输出结果------------
英雄 泰达米尔 的生命值 :2600
英雄 泰达米尔 的攻击力 :450
英雄 泰达米尔 的护甲值 :200
正在前往事发地点...
发出了一招强力的普通攻击...

Process finished with exit code 0
  • __init__()方法,在创建一个对象时默认被调用,不需要手动调用
  • __init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。
class Hero(object):
    """定义了一个英雄类,可以移动和攻击"""

    def __init__(self, name, skill, hp, atk, armor):
        """ __init__() 方法,用来做变量初始化 或 赋值 操作"""
        # 英雄名
        self.name = name
        # 技能
        self.skill = skill
        # 生命值:
        self.hp = hp
        # 攻击力
        self.atk = atk
        # 护甲值
        self.armor = armor

    def move(self):
        """实例方法"""
        print("%s 正在前往事发地点..." % self.name)

    def attack(self):
        """实例方法"""
        print("发出了一招强力的%s..." % self.skill)

    def info(self):
        print("英雄 %s 的生命值 :%d" % (self.name, self.hp))
        print("英雄 %s 的攻击力 :%d" % (self.name, self.atk))
        print("英雄 %s 的护甲值 :%d" % (self.name, self.armor))


# 实例化英雄对象时,参数会传递到对象的__init__()方法里
taidamier = Hero("泰达米尔", "旋风斩", 2600, 450, 200)
gailun = Hero("盖伦", "大宝剑", 4200, 260, 400)


print(gailun)    #<__main__.Hero object at 0x000001855DBDC7F0>
print(taidamier)   #<__main__.Hero object at 0x000001855DBDC7B8>

# 不同对象的属性值的单独保存
print(id(taidamier.name))    #1772266640
print(id(gailun.name))       #1772266640

# 同一个类的不同对象,实例方法共享
print(id(taidamier.move()))    #泰达米尔 正在前往事发地点...
print(id(gailun.move()))     #盖伦 正在前往事发地点...

print(taidamier.move())    #None
print(gailun.move())      #None
  1. 在类内部获取 属性 和 实例方法,通过self获取;
  2. 在类外部获取 属性 和 实例方法,通过对象名获取。

  3. 如果一个类有多个对象,每个对象的属性是各自保存的,都有各自独立的地址;

  4. 但是实例方法是所有对象共享的,只占用一份内存空间。类会通过self来判断是哪个对象调用了实例方法。
  5. __init__(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)中出了self作为第一个形参外还需要2个形参,例如__init__(self,x,y)

2.2.__str()__

当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了str方法,那么就会打印从在这个方法中 return 的数据。

  • 在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做“魔法”方法
  • 当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__(self)方法,那么就会打印从在这个方法中 return 的数据
  • __str__方法通常返回一个字符串,作为这个对象的描述信息
class Hero(object):
    """定义了一个英雄类,可以移动和攻击"""

    def __init__(self, name, skill, hp, atk, armor):
        """ __init__() 方法,用来做变量初始化 或 赋值 操作"""
        # 英雄名
        self.name = name  # 实例变量
        # 技能
        self.skill = skill
        # 生命值:
        self.hp = hp   # 实例变量
        # 攻击力
        self.atk = atk
        # 护甲值
        self.armor = armor

    def move(self):
        """实例方法"""
        print("%s 正在前往事发地点..." % self.name)

    def attack(self):
        """实例方法"""
        print("发出了一招强力的%s..." % self.skill)

    # def info(self):
    #     print("英雄 %s 的生命值 :%d" % (self.name, self.hp))
    #     print("英雄 %s 的攻击力 :%d" % (self.name, self.atk))
    #     print("英雄 %s 的护甲值 :%d" % (self.name, self.armor))


    def __str__(self):
        """
            这个方法是一个魔法方法 (Magic Method) ,用来显示信息
            该方法需要 return 一个数据,并且只有self一个参数,当在类的外部 print(对象) 则打印这个数据
        """
        return "英雄 <%s> 数据: 生命值 %d, 攻击力 %d, 护甲值 %d" % (self.name, self.hp, self.atk, self.armor)


taidamier = Hero("泰达米尔", "旋风斩", 2600, 450, 200)
gailun = Hero("盖伦", "大宝剑", 4200, 260, 400)

# 如果没有__str__ 则默认打印 对象在内存的地址。
# 当类的实例化对象 拥有 __str__ 方法后,那么打印对象则打印 __str__ 的返回值。
print(taidamier)
print(gailun)

# 查看类的文档说明,也就是类的注释
print(Hero.__doc__)

====================输出结构============
英雄 <泰达米尔> 数据: 生命值 2600, 攻击力 450, 护甲值 200
英雄 <盖伦> 数据: 生命值 4200, 攻击力 260, 护甲值 400
定义了一个英雄类,可以移动和攻击

2.3.__str()__

创建对象后,python解释器默认调用__init__()方法;

当删除对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法

当有变量保存了一个对象的引用时,此对象的引用计数就会加1;

当使用del() 删除变量指向的对象时,则会减少对象的引用计数。如果对象的引用计数不为1,那么会让这个对象的引用计数减1,当对象的引用计数为0的时候,则对象才会被真正删除(内存被回收)。

class Hero(object):

    # 初始化方法
    # 创建完对象后会自动被调用
    def __init__(self, name):
        print('__init__方法被调用')
        self.name = name

    # 当对象被删除时,会自动被调用
    def __del__(self):
        print("__del__方法被调用")
        print("%s 被 GM 干掉了..." % self.name)


# 创建对象
taidamier = Hero("泰达米尔")
# 删除对象
print("%d 被删除1次" % id(taidamier))
del(taidamier)

print("--" * 10)

gailun = Hero("盖伦")
gailun1 = gailun
gailun2 = gailun

print("%d 被删除1次" % id(gailun))
del(gailun)
print("%d 被删除1次" % id(gailun1))
del(gailun1)
print("%d 被删除1次" % id(gailun2))
del(gailun2)

————————————————————————输出结果————————————————————
__init__方法被调用
2096969993184 被删除1次
__del__方法被调用
泰达米尔 被 GM 干掉了...
--------------------
__init__方法被调用
2096969993352 被删除1次
2096969993352 被删除1次
2096969993352 被删除1次
__del__方法被调用
盖伦 被 GM 干掉了...

三、继承

3.1概念

  • 继承描述的是多个类之间的所属关系。如果一个类A里面的属性和方法可以复用,则可以通过继承的方式,传递到类B里。那么类A就是基类,也叫做父类;类B就是派生类,也叫做子类。
  •  
  • 虽然子类没有定义__init__方法初始化属性,也没有定义实例方法,但是父类有。所以只要创建子类的对象,就默认执行了那个继承过来的__init__方法
# 定义一个Master类
class Master(object):
    def __init__(self):
        # 属性
        self.kongfu = "古法煎饼果子配方" 

    # 实例方法
    def make_cake(self):
        print("按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


# 定义Prentice类,继承了 Master,则Prentice是子类,Master是父类。
class Prentice(Master): 
    # 子类可以继承父类所有的属性和方法,哪怕子类没有自己的属性和方法,也可以使用父类的属性和方法。
    pass                

# laoli = Master()
# print(laoli.kongfu)
# laoli.make_cake()

damao = Prentice()  # 创建子类实例对象
print(damao.kongfu) # 子类对象可以直接使用父类的属性
damao.make_cake()   # 子类对象可以直接使用父类的方法

3.2多继承

  • 多继承可以继承多个父类,也继承了所有父类的属性和方法
  • 注意:如果多个父类中有同名的 属性和方法,则默认使用第一个父类的属性和方法(根据类的魔法属性mro的顺序来查找)
  • 多个父类中,不重名的属性和方法,不会有任何影响。
class Master(object):
    def __init__(self):
        self.kongfu = "古法煎饼果子配方"  # 实例变量,属性

    def make_cake(self):                    # 实例方法,方法
        print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)

    def dayandai(self):
        print("师傅的大烟袋..")

class School(object):
    def __init__(self):
        self.kongfu = "现代煎饼果子配方"

    def make_cake(self):
        print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)

    def xiaoyandai(self):
        print("学校的小烟袋..")

# class Prentice(School, Master):  # 多继承,继承了多个父类(School在前)
#     pass

# damao = Prentice()
# print(damao.kongfu)
# damao.make_cake()
# damao.dayandai()
# damao.xiaoyandai()


class Prentice(Master, School):  # 多继承,继承了多个父类(Master在前)
    pass

damao = Prentice()
print(damao.kongfu) # 执行Master的属性
damao.make_cake() # 执行Master的实例方法

# 子类的魔法属性__mro__决定了属性和方法的查找顺序
print(Prentice.__mro__)

damao.dayandai() # 不重名不受影响
damao.xiaoyandai()

3.3子类重写父类的同名属性和方法

class Master(object):
    def __init__(self):
        self.kongfu = "古法煎饼果子配方" 

    def make_cake(self): 
        print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


class School(object):
    def __init__(self):
        self.kongfu = "现代煎饼果子配方"

    def make_cake(self):
        print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


class Prentice(School, Master):  # 多继承,继承了多个父类
    def __init__(self):
        self.kongfu = "猫氏煎饼果子配方"

    def make_cake(self):
        print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


# 如果子类和父类的方法名和属性名相同,则默认使用子类的
# 叫 子类重写父类的同名方法和属性
damao = Prentice()
print(damao.kongfu) # 子类和父类有同名属性,则默认使用子类的
damao.make_cake() # 子类和父类有同名方法,则默认使用子类的

# 子类的魔法属性__mro__决定了属性和方法的查找顺序
print(Prentice.__mro__)

3.4子类调用父类同名属性和方法

class Master(object):
    def __init__(self):
        self.kongfu = "古法煎饼果子配方"  # 实例变量,属性

    def make_cake(self):                    # 实例方法,方法
        print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


class School(object):
    def __init__(self):
        self.kongfu = "现代煎饼果子配方"

    def make_cake(self):
        print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


class Prentice(School, Master):  # 多继承,继承了多个父类
    def __init__(self):
        self.kongfu = "猫氏煎饼果子配方"

    def make_cake(self):
        print("执行子类的__init__方法前,self.kongfu属性:%s" % self.kongfu)
        self.__init__() # 执行本类的__init__方法,做属性初始化 self.kongfu = "猫氏...."
        print("执行子类的__init__方法前,self.kongfu属性:%s" % self.kongfu)
        print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


    # 调用父类方法格式:父类类名.父类方法(self)
    def make_old_cake(self):
        # 不推荐这样访问父类的实例属性,相当于创建了一个新的父类对象
        # print("直接调用Master类的kongfu属性:%s" % Master().kongfu)

        # 可以通过执行Master类的__init__方法,来修改self的属性值
        print("执行Master类的__init__方法前,self.kongfu属性:%s" % self.kongfu)
        Master.__init__(self) # 调用了父类Master的__init__方法 self.kongfu = "古法...."
        print("执行Master类的__init__方法后,self.kongfu属性:%s" % self.kongfu)
        Master.make_cake(self) # 调用父类Master的实例方法


    def make_new_cake(self):
        # 不推荐这样访问类的实例属性,相当于创建了一个新的父类对象
        # print("直接调用School类的kongfu属性:%s" % School().kongfu)

        # 可以通过执行School类的__init__方法,来修改self的属性值
        print("执行School类的__init__方法前,self.kongfu属性:%s" % self.kongfu)
        School.__init__(self) # 调用了父类School的__init__方法 self.kongfu = "现代...."
        print("执行School类的__init__方法后,self.kongfu属性:%s" % self.kongfu)
        School.make_cake(self) # 调用父类School的实例方法

# 实例化对象,自动执行子类的__init__方法
damao = Prentice()

damao.make_cake() # 调用子类的方法(默认重写了父类的同名方法)

print("--" * 10)
damao.make_old_cake() # 进入实例方法去调用父类Master的方法

print("--" * 10)
damao.make_new_cake() # 进入实例方法去调用父类School的方法

print("--" * 10)
damao.make_cake() # 调用本类的实例方法

=================================结果==========================================
执行子类的__init__方法前,self.kongfu属性:猫氏煎饼果子配方
执行子类的__init__方法前,self.kongfu属性:猫氏煎饼果子配方
[猫氏] 按照 <猫氏煎饼果子配方> 制作了一份煎饼果子...
--------------------
执行Master类的__init__方法前,self.kongfu属性:猫氏煎饼果子配方
执行Master类的__init__方法后,self.kongfu属性:古法煎饼果子配方
[古法] 按照 <古法煎饼果子配方> 制作了一份煎饼果子...
--------------------
执行School类的__init__方法前,self.kongfu属性:古法煎饼果子配方
执行School类的__init__方法后,self.kongfu属性:现代煎饼果子配方
[现代] 按照 <现代煎饼果子配方> 制作了一份煎饼果子...
--------------------
执行子类的__init__方法前,self.kongfu属性:现代煎饼果子配方
执行子类的__init__方法前,self.kongfu属性:猫氏煎饼果子配方
[猫氏] 按照 <猫氏煎饼果子配方> 制作了一份煎饼果子...

3.5多层继承

class Master(object):
    def __init__(self):
        self.kongfu = "古法煎饼果子配方"  

    def make_cake(self):
        print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


class School(object):
    def __init__(self):
        self.kongfu = "现代煎饼果子配方"

    def make_cake(self):
        print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


class Prentice(School, Master):  # 多继承,继承了多个父类
    def __init__(self):
        self.kongfu = "猫氏煎饼果子配方"
        self.money = 10000  # 亿美金

    def make_cake(self):
        self.__init__() # 执行本类的__init__方法,做属性初始化 self.kongfu = "猫氏...."
        print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


    # 调用父类方法格式:父类类名.父类方法(self)
    def make_old_cake(self):
        Master.__init__(self) # 调用了父类Master的__init__方法 self.kongfu = "古法...."
        Master.make_cake(self) # 调用了父类Master的实例方法


    def make_new_cake(self):
        School.__init__(self) # 调用了父类School的__init__方法 self.kongfu = "现代...."
        School.make_cake(self) # 调用父类School的实例方法,

class PrenticePrentice(Prentice): # 多层继承
    pass


pp = PrenticePrentice()
pp.make_cake() # 调用父类的实例方法
pp.make_new_cake() 
pp.make_old_cake()

print(pp.money)

3.6super()的使用

子类继承了多个父类,如果父类类名修改了,那么子类也要涉及多次修改。而且需要重复写多次调用,显得代码臃肿。

使用super() 可以逐一调用所有的父类方法,并且只执行一次。调用顺序遵循 mro 类属性的顺序。

注意:如果继承了多个父类,且父类都有同名方法,则默认只执行第一个父类的(同名方法只执行一次,目前super()不支持执行多个父类的同名方法)

super() 在Python2.3之后才有的机制,用于通常单继承的多层继承。

class Master(object):
    def __init__(self):
        self.kongfu = "古法煎饼果子配方"  # 实例变量,属性

    def make_cake(self):  # 实例方法,方法
        print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)


# 父类是 Master类
class School(Master):
    def __init__(self):
        self.kongfu = "现代煎饼果子配方"

    def make_cake(self):
        print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)
        super().__init__()  # 执行父类的构造方法
        super().make_cake()  # 执行父类的实例方法


# 父类是 School 和 Master
class Prentice(School, Master):  # 多继承,继承了多个父类
    def __init__(self):
        self.kongfu = "猫氏煎饼果子配方"

    def make_cake(self):
        self.__init__()  # 执行本类的__init__方法,做属性初始化 self.kongfu = "猫氏...."
        print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)

    def make_all_cake(self):
        # 方式1. 指定执行父类的方法(代码臃肿)
        # School.__init__(self)
        # School.make_cake(self)
        #
        # Master.__init__(self)
        # Master.make_cake(self)
        #
        # self.__init__()
        # self.make_cake()

        # 方法2. super() 带参数版本,只支持新式类
        # super(Prentice, self).__init__() # 执行父类的 __init__方法 
        # super(Prentice, self).make_cake()
        # self.make_cake()

        # 方法3. super()的简化版,只支持新式类
        super().__init__()  # 执行父类的 __init__方法 
        super().make_cake()  # 执行父类的 实例方法
        self.make_cake()  # 执行本类的实例方法


damao = Prentice()
damao.make_cake()
damao.make_all_cake()

# print(Prentice.__mro__)

四、封装

封装的意义:

  1. 将属性和方法放到一起做为一个整体,然后通过实例化对象来处理;
  2. 隐藏内部实现细节,只需要和对象及其属性和方法交互就可以了;
  3. 对类的属性和方法增加 访问权限控制。

4.1私有属性和私有方法

私有权限:在属性名和方法名 前面 加上两个下划线 __

  1. 类的私有属性 和 私有方法,都不能通过对象直接访问,但是可以在本类内部访问;
  2. 类的私有属性 和 私有方法,都不会被子类继承,子类也无法访问;
  3. 私有属性 和 私有方法 往往用来处理类的内部事情,不通过对象处理,起到安全作用。
class Master(object):
    def __init__(self):
        self.kongfu = "古法煎饼果子配方" 
    def make_cake(self):          
        print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)

class Prentice(School, Master):
    def __init__(self):
        self.kongfu = "猫氏煎饼果子配方"
        # 私有属性,可以在类内部通过self调用,但不能通过对象访问
        self.__money = 10000  

    # 私有方法,可以在类内部通过self调用,但不能通过对象访问
    def __print_info(self):
        print(self.kongfu)
        print(self.__money)

    def make_cake(self):
        self.__init__()
        print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)

    def make_old_cake(self):
        Master.__init__(self) 
        Master.make_cake(self)


    def make_new_cake(self):
        School.__init__(self) 
        School.make_cake(self)

class PrenticePrentice(Prentice):
    pass


damao = Prentice()
# 对象不能访问私有权限的属性和方法
# print(damao.__money)
# damao.__print_info()


pp = PrenticePrentice()
# 子类不能继承父类私有权限的属性和方法
print(pp.__money) 
pp.__print_info()

4.2 修改私有属性的值

  • 如果需要修改一个对象的属性值,通常有2种方法

    1. 对象名.属性名 = 数据 ----> 直接修改
    2. 对象名.方法名() ----> 间接修改
  • 私有属性不能直接访问,所以无法通过第一种方式修改,一般的通过第二种方式修改私有属性的值:定义一个可以调用的公有方法,在这个公有方法内访问修改。

class Master(object):
    def __init__(self):
        self.kongfu = "古法煎饼果子配方" 
    def make_cake(self):          
        print("[古法] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)

class School(object):
    def __init__(self):
        self.kongfu = "现代煎饼果子配方"

    def make_cake(self):
        print("[现代] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)

class Prentice(School, Master):
    def __init__(self):
        self.kongfu = "猫氏煎饼果子配方"
        # 私有属性,可以在类内部通过self调用,但不能通过对象访问
        self.__money = 10000  


    # 现代软件开发中,通常会定义get_xxx()方法和set_xxx()方法来获取和修改私有属性值。

    # 返回私有属性的值
    def get_money(self):
        return self.__money

    # 接收参数,修改私有属性的值
    def set_money(self, num):
        self.__money = num


    def make_cake(self):
        self.__init__()
        print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." % self.kongfu)

    def make_old_cake(self):
        Master.__init__(self) 
        Master.make_cake(self)

    def make_new_cake(self):
        School.__init__(self) 
        School.make_cake(self)

class PrenticePrentice(Prentice):
    pass


damao = Prentice()
# 对象不能访问私有权限的属性和方法
# print(damao.__money)
# damao.__print_info()

# 可以通过访问公有方法set_money()来修改私有属性的值
damao.set_money(100)

# 可以通过访问公有方法get_money()来获取私有属性的值
print(damao.get_money())

4.3多态

  • 定义:在需要使用父类对象的地方,也可以使用子类对象, 这种情况就叫多态.
  • 可以按照以下几个步骤来写代码:

    1.子类继承父类
    2.子类重写父类中的方法
    3.通过对象调用这个方法

    # 定义父类
    class Father:
        def cure(self):
            print("父亲给病人治病...")


    # 定义子类继承父类
    class Son(Father):
        # 重写父类中的方法
        def cure(self):
            print("儿子给病人治病...")


    # 定义函数,在里面 调用 医生的cure函数
    def call_cure(doctor):

        # 调用医生治病的方法
        doctor.cure()


    # 创建父类对象
    father = Father()
    # 调用函数,把父类对象传递函数
    call_cure(father)


    # 创建子类对象
    son = Son()
    # 调用函数,把子类对象传递函数
    call_cure(son)
=====================输出结果=================
父亲给病人治病...
儿子给病人治病...

多态的好处:

​ 给call_cure(doctor)函数传递哪个对象,在它里面就会调用哪个对象的cure()方法,也就是说在它里面既可以调用son对象的cure()方法,也能调用father对象的cure()方法,当然了也可以在它里面调用Father类其它子类对象的cure()方法,这样可以让call_cure(doctor)函数变得更加灵活,额外增加了它的功能,提高了它的扩展性.

4.4类属性和实例属性

4.4.1类属性

class People(object):
    name = 'Tom'  # 公有的类属性
    __age = 12  # 私有的类属性

p = People()

print(p.name)  # 正确
print(People.name)  # 正确
print(p.__age)  # 错误,不能在类外通过实例对象访问私有的类属性
print(People.__age) # 错误,不能在类外通过类对象访问私有的类属性

4.4.2实例属性(对象属性)

class People(object):
    address = '山东'  # 类属性
    def __init__(self):
        self.name = 'xiaowang'  # 实例属性
        self.age = 20  # 实例属性

p = People()
p.age = 12  # 实例属性
print(p.address)  # 正确
print(p.name)  # 正确
print(p.age)  # 正确

print(People.address)  # 正确
print(People.name)  # 错误
print(People.age)  # 错误

4.4.3通过实例(对象)去修改类属性

class People(object):
    country = 'china' #类属性


print(People.country)
p = People()
print(p.country)
p.country = 'japan' 
print(p.country)  # 实例属性会屏蔽掉同名的类属性
print(People.country)
del p.country  # 删除实例属性
print(p.country)

总结

  • 如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性

4.5静态方法和类方法

4.5.1. 类方法

是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和类对象去访问。

class People(object):
    country = 'china'

    #类方法,用classmethod来进行修饰
    @classmethod
    def get_country(cls):
        return cls.country

p = People()
print(p.get_country())    #可以用过实例对象引用
print(People.get_country())    #可以通过类对象引用

类方法还有一个用途就是可以对类属性进行修改:

class People(object):
    country = 'china'

    #类方法,用classmethod来进行修饰
    @classmethod
    def get_country(cls):
        return cls.country

    @classmethod
    def set_country(cls,country):
        cls.country = country


p = People()
print(p.get_country())   #可以用过实例对象访问
print(People.get_country())    #可以通过类访问

p.set_country('japan')   

print(p.get_country())
print(People.get_country())

结果显示在用类方法对类属性修改之后,通过类对象和实例对象访问都发生了改变

4.5.2. 静态方法

需要通过修饰器@staticmethod来进行修饰,静态方法不需要多定义参数,可以通过对象和类来访问。

class People(object):
    country = 'china'

    @staticmethod
    #静态方法
    def get_country():
        return People.country


p = People()
# 通过对象访问静态方法
p.get_contry()

# 通过类访问静态方法
print(People.get_country())

总结

  1. 从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;
  2. 实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。
  3. 静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类实例对象来引用