类¶
1.类和对象¶
- 类:用来描述具有相同属性和方法的对象的集合。类定义了集合中每个对象共有的属性和方法。对象是类的实例。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
- 类属性(变量):类变量在整个实例化的对象中是公用的。类变量定义在类中,且在方法之外。类变量通常不作为实例变量使用。类变量也称作属性。如果有实例属性优先、对象优先取实例属性。
- 实例属性(变量):定义在方法中的变量只作用于当前实例的类。
- 私有属性和私有方法:不能通过对象直接访问,但是可以在本类内部访问,都不会被子类继承,子类也无法访问,用来处理类的内部事情,不通过对象处理,起到安全作用。对象不能访问私有权限的属性和方法。子类不能继承父类私有权限的属性和方法。定义一个方法间接修改私有属性的值。
- 实例方法:类中定义的函数。实例方法必须至少有一个参数,通常命名为 self,它代表类的实例本身。
- 类方法:要用修饰器@classmethod来标识其为类方法,可以通过实例对象和类对象调用。修改类属性。类方法是通过装饰器 @classmethod 来定义的,它们通过类本身来调用,而不是类的实例。类方法通常用于执行与类相关的操作,而不是与类的特定实例相关的操作。类方法的第一个参数通常是 cls,它代表类本身。
- 静态方法:静态方法是通过装饰器 @staticmethod 来定义的,它们既不依赖于类也不依赖于类的实例。静态方法就像普通的函数一样,只是它们被定义在类的作用域内。静态方法可以通过类名或类的实例来调用,但调用时不会传递特殊的第一个参数(如 self 或 cls)。
- 数据成员:类变量或实例变量用于处理类及其实例对象的相关数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,就可以对其进行改写,这个过程称为方法的覆盖(Override),也称为方法的重写。
- 实例化(Instance):创建一个类的实例、类的具体对象。
- init魔法方法:用来做变量初始化 或 赋值 操作,在类实例化对象的时候,会被自动调用。
- str魔法方法:打印类名,返回一个字符串,如果不是字符串会报错,只接收一个self参数。
- del魔法方法:它在对象被销毁之前被调用。它用于执行清理操作,比如关闭打开的文件、断开网络连接、释放资源等。del方法不接受任何参数,除了self之外。
2.定义类¶
class Demo:
"""经典类"""
pass
class Demo():
"""经典类"""
pass
class Demo(object):
"""新式类"""
pass
# object 是Python 里所有类的最顶级父类;
# 类名 的命名规则按照"大驼峰命名法";
3.创建对象¶
# 格式
# 对象名1 = 类名()
class Demo(object):
"""info 是一个实例方法,类对象可以调用实例方法,实例方法的第一个参数一定是self"""
def info(self):
"""当对象调用实例方法时,Python会自动将对象本身的引用做为参数,
传递到实例方法的第一个参数self里"""
print(self) # 谁去实例化self就是谁
print("self各不同,对象是出处")
eg1 = Demo() # 实例化了一个对象,
eg1.info() # 对象调用实例方法info(),执行info()里的代码 . 表示选择属性或者方法
print(eg1) # 打印对象,则默认打印对象在内存的地址,结果等同于info里的print(self)
4.添加和获取对象的属性¶
class Hero:
def move(self):
print("正在前往大龙")
def attack(self):
print('正在发出QWER')
ruiwen = Hero()
# 添加和获取对象属性
ruiwen.name = "瑞文"
ruiwen.hp = 2600
ruiwen.atk = 450
ruiwen.armor = 200
# 通过成员选择运算符,获取对象的属性值
print(f'英雄{ruiwen.name}的生命值{ruiwen.hp}')
print(f'英雄{ruiwen.name}的生命值{ruiwen.atk}')
print(f'英雄{ruiwen.name}的生命值{ruiwen.armor}')
# 对象创建并添加属性后,能否在类的实例方法里获取这些属性呢?如果可以的话,应该通过什么方式?
5.在方法内通过self获取对象属性¶
class Hero:
def move(self):
print("正在前往大龙")
def attack(self):
print('正在发出QWER')
def info(self):
"""在类的实例方法中,通过self获取该对象的属性"""
print(f'英雄{ruiwen.name}的生命值{ruiwen.hp}')
print(f'英雄{ruiwen.name}的攻击力{ruiwen.atk}')
print(f'英雄{ruiwen.name}的护甲值{ruiwen.armor}')
# 实例化
ruiwen = Hero()
# 添加和获取对象属性
ruiwen.name = "瑞文"
ruiwen.hp = 2600
ruiwen.atk = 450
ruiwen.armor = 200
# 通过 .成员选择运算符,获取对象的实例方法
ruiwen.info()
ruiwen.move()
ruiwen.attack()
# 创建对象后再去添加属性有点不合适,有没有简单的办法,可以在创建对象的时候,就已经拥有这些属性?
6.魔法方法init()¶
__init__()
方法,在创建一个对象时默认被调用,不需要手动调用__init__(self)
中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。
class Hero(object):
"""
Python 的类里提供的,两个下划线开始,两个下划线结束的方法,就是魔法方法,__init__()就是一个魔法方法,通常用来做属性初始化 或 赋值 操作。
如果类面没有写__init__方法,Python会自动创建,但是不执行任何操作,
如果为了能够在完成自己想要的功能,可以自己定义__init__方法,
所以一个类里无论自己是否编写__init__方法 一定有__init__方法。
"""
def __init__(self):
self.name = 'ruiwen'
self.hp = 2600
self.atk = 450
self.armor = 200
def move(self):
print('move')
def attack(self):
print('attack')
def info(self):
"""在类的实例方法中,通过self获取该对象的属性"""
print(f'英雄{ruiwen.name}的生命值{ruiwen.hp}')
print(f'英雄{ruiwen.name}的攻击力{ruiwen.atk}')
print(f'英雄{ruiwen.name}的护甲值{ruiwen.armor}')
ruiwen = Hero() # 实例化了一个英雄对象,并自动调用__init__()方法
# 只需要调用实例方法info(),即可获取英雄的属性
ruiwen.info()
ruiwen.move()
ruiwen.attack()
在类的方法里定义属性的固定值,则每个对象实例变量的属性值都是相同的。
一个游戏里往往有很多不同的英雄,能否让实例化的每个对象,都有不同的属性值呢?
7.有参数的init()¶
通过一个类,可以创建多个对象,就好比 通过一个模具创建多个实体一样
__init__(self)`中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么`__init__(self)`中出了self作为第一个形参外还需要2个形参,例如`__init__(self,x,y)
class Hero(object):
def __init__(self, name, skill, hp, atk, armor):
self.name = name
self.skill = skill
self.hp = hp
self.atk = atk
self.armor = armor
def move(self):
print('move')
def attack(self):
print('attack')
def info(self):
"""在类的实例方法中,通过self获取该对象的属性"""
print(f'英雄{self.name}的生命值{self.hp}')
print(f'英雄{self.name}的攻击力{self.atk}')
print(f'英雄{self.name}的护甲值{self.armor}')
# 实例化英雄对象时,参数会传递到对象的__init__()方法里
ruiwen = Hero("瑞文", "R", 2600, 450, 200)
feiaona = Hero("菲奥娜", "W", 3600, 1450, 200)
ruiwen.info() # 调用方法
# 不同对象的属性值的单独保存
print(id(ruiwen.name))
print(id(feiaona.name))
# 同一个类的不同对象,实例方法共享
print(id(ruiwen.move()))
print(id(feiaona.move()))
- 在类内部获取 属性 和 实例方法,通过self获取;
- 在类外部获取 属性 和 实例方法,通过对象名获取。
- 如果一个类有多个对象,每个对象的属性是各自保存的,都有各自独立的地址;
- 但是实例方法是所有对象共享的,只占用一份内存空间。类会通过self来判断是哪个对象调用了实例方法。
8.魔法方法str()¶
class Hero(object):
"""定义一个英雄类,可以移动和攻击"""
def __init__(self, name, skill, hp, atk, armor):
self.name = name
self.skill = skill
self.hp = hp
self.atk = atk
self.armor = armor
def move(self):
print('move')
def attack(self):
print('attack')
def __str__(self):
return f'英雄{self.name}数据: 生命值 {self.hp}, 攻击力 {self.atk}, 护甲值 {self.armor}'
ruiwen = Hero("瑞文", "R", 2600, 450, 200)
feiaona = Hero("菲奥娜", "W", 3600, 1450, 200)
# 如果没有__str__ 则默认打印 对象在内存的地址。
# 当类的实例化对象 拥有 __str__ 方法后,那么打印对象则打印 __str__ 的返回值。
print(ruiwen)
print(feiaona)
# 查看类的文档说明,也就是类的注释
print(Hero.__doc__)
- 在python中方法名如果是
__xxxx__()
的,那么就有特殊的功能,因此叫做“魔法”方法- 当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了
__str__(self)
方法,那么就会打印从在这个方法中return
的- 数据
__str__
方法通常返回一个字符串,作为这个对象的描述信息
9.魔法方法del()¶
创建对象后,python解释器默认调用
__init__()
方法;当删除对象时,python解释器也会默认调用一个方法,这个方法为
__del__()
方法
class Hero:
def __init__(self, name):
print('init方法被调用')
self.name = name
def __del__(self):
print('del方法被调用')
print(f'{self.name}被干掉了')
# 创建对象
ruiwen = Hero("瑞文")
print(f'{id(ruiwen)}被删除1次')
gailun = Hero("盖伦")
gailun1 = gailun
gailun2 = gailun
print(f'{id(gailun)}被删除1次')
del(gailun)
print(f'{id(gailun1)}被删除1次')
del(gailun1)
print(f'{id(gailun2)}被删除1次')
del(gailun2)
- 当有变量保存了一个对象的引用时,此对象的引用计数就会加1;
- 当使用del() 删除变量指向的对象时,则会减少对象的引用计数。如果对象的引用计数不为1,那么会让这个对象的引用计数减1,当对象的引用计数为0的时候,则对象才会被真正删除(内存被回收)。
10.继承的概念¶
- 在程序中,继承描述的是多个类之间的所属关系。
- 如果一个类A里面的属性和方法可以复用,则可以通过继承的方式,传递到类B里。
- 那么类A就是基类,也叫做父类;类B就是派生类,也叫做子类。
class A:
def __init__(self):
self.num = 10
def print_num(self):
print(self.num + 10)
class B(A):
pass
b = B()
print(b.num)
b.print_num()
11.单继承:子类只继承一个父类¶
# 定义一个Master类
class Master(object):
def __init__(self):
# 属性
self.kongfu = "古法煎饼果子配方"
# 实例方法
def make_cake(self):
print(f"{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() # 子类对象可以直接使用父类的方法
说明:¶
- 虽然子类没有定义
__init__
方法初始化属性,也没有定义实例方法,但是父类有。所以只要创建子类的对象,就默认执行了那个继承过来的__init__
方法总结:¶
- 子类在继承的时候,在定义类时,小括号()中为父类的名字
- 父类的属性、方法,会被继承给子类
故事情节:煎饼果子老师傅在煎饼果子界摸爬滚打几十年,拥有一身精湛的煎饼果子技术,并总结了一套"古法煎饼果子配方"。
可是老师傅年迈已久,在嗝屁之前希望把自己的配方传承下去,于是老师傅把配方传给他的徒弟大猫...
大猫掌握了师傅的配方,可以制作古法煎饼果子。但是大猫是个爱学习的好孩子,他希望学到更多的煎饼果子的做法,于是通过百度搜索,找到了一家煎饼果子培训学校。(多继承)
12.多继承:子类继承多个父类¶
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()
- 多继承可以继承多个父类,也继承了所有父类的属性和方法
- 注意:如果多个父类中有同名的 属性和方法,则默认使用第一个父类的属性和方法(根据类的魔法属性**mro**的顺序来查找)
- 多个父类中,不重名的属性和方法,不会有任何影响。
大猫掌握了 师傅的配方 和 学校的配方,通过研究,大猫在两个配方的基础上,创建了一种全新的煎饼果子配方,称之为 "猫氏煎饼果子配方"。(子类重写父类同名属性和方法)
13.子类重写父类的同名属性和方法¶
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__)
大猫的新配方大受欢迎,但是有些顾客希望也能吃到古法配方和 现代配方 的煎饼果子...(子类调用父类的同名属性和方法)
14.子类调用父类同名属性和方法¶
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() # 调用本类的实例方法
无论何时何地,self都表示是子类的对象。在调用父类方法时,通过传递self参数,来控制方法和属性的访问修改。
大猫的煎饼果子店非常红火,终于有一天,他成了世界首富!!
但是他也老了,所以他希望把 师傅的配方 和 学校的配方 以及自己的配方 继续传承下去...(多层继承)
15.多层继承¶
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)
大猫觉得配方传承下去没问题,但是钱是辛辛苦苦挣得血汗钱,不想传给徒弟。(私有权限)
16.通过super()来调用父类中方法¶
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__)
子类继承了多个父类,如果父类类名修改了,那么子类也要涉及多次修改。而且需要重复写多次调用,显得代码臃肿。
使用super() 可以逐一调用所有的父类方法,并且只执行一次。调用顺序遵循 mro 类属性的顺序。
注意:如果继承了多个父类,且父类都有同名方法,则默认只执行第一个父类的(同名方法只执行一次,目前super()不支持执行多个父类的同名方法)
super() 在Python2.3之后才有的机制,用于通常单继承的多层继承。
17.私有属性和私有方法¶
私有权限¶
面向对象三大特性:封装、继承、多态¶
封装的意义:¶
- 将属性和方法放到一起做为一个整体,然后通过实例化对象来处理;
- 隐藏内部实现细节,只需要和对象及其属性和方法交互就可以了;
- 对类的属性和方法增加 访问权限控制。
私有权限:在属性名和方法名 前面 加上两个下划线 __¶
- 类的私有属性 和 私有方法,都不能通过对象直接访问,但是可以在本类内部访问;
- 类的私有属性 和 私有方法,都不会被子类继承,子类也无法访问;
- 私有属性 和 私有方法 往往用来处理类的内部事情,不通过对象处理,起到安全作用。
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()
- Python中没有像C++中 public 和 private 这些关键字来区别公有属性和私有属性。
- Python是以属性命名方式来区分,如果在属性和方法名前面加了2个下划线'__',则表明该属性和方法是私有权限,否则为公有权限。
18.修改私有属性的值¶
- 如果需要修改一个对象的属性值,通常有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())
19.多态¶
大家先想一想在电视上有没有见过这样的事情: 有一个中医世家,父亲是当地一位非常有名的老大夫,看病看的非常好,儿子从小就跟着父亲学医,医术也不错. 突然县太爷家的千金生了重病,急需老大夫前去治病,但是老大夫又不在家,就请了老大夫的儿子前去给治病. 最后儿子也把病给治好了.
大家应该听说过这样的故事吧.
那么,在python语言中能不能做类似的事情,比如说 在需要调用父类对象方法的地方,我们也可以调用子类对象的方法呢?
当然可以! 要想这样,我们需要使用接下来要学习的知识点:多态. 好,下面我们就开始正式讲解 多态.在讲解的时候: 1.先讲解"多态"的概念 2.再讲解如何使用多态 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)函数变得更加灵活,额外增加了它的功能,提高了它的扩展性.
20.类属性和实例属性¶
在了解了类基本的东西之后,下面看一下python中这几个概念的区别
先来谈一下
类属性
和实例属性
在前面的例子中我们接触到的就是实例属性(对象属性),顾名思义,类属性就是
类对象
所拥有的属性,它被所有类对象
的实例对象
所共有,在内存中只存在一个副本,这个和C++中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象
和实例对象
访问
类属性¶
class People(object):
name = 'Tom' # 公有的类属性
__age = 12 # 私有的类属性
p = People()
print(p.name) # 正确
print(People.name) # 正确
print(p.__age) # 错误,不能在类外通过实例对象访问私有的类属性
print(People.__age) # 错误,不能在类外通过类对象访问私有的类属性
实例属性(对象属性)¶
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) # 错误
通过实例(对象)去修改类属性¶
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)
- 如果需要在类外修改
类属性
,必须通过类对象
去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性
,这种方式修改的是实例属性
,不会影响到类属性
,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性
,除非删除了该实例属性
。
21.静态方法和类方法¶
静态方法¶
需要通过修饰器
@staticmethod
来进行修饰,静态方法不需要多定义参数,可以通过对象和类来访问。
class People(object):
country = 'china'
@staticmethod
#静态方法
def get_country():
return People.country
p = People()
# 通过对象访问静态方法
p.get_contry()
# 通过类访问静态方法
print(People.get_country())
类方法¶
是类对象所拥有的方法,需要用修饰器
@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())
# 结果显示在用类方法对类属性修改之后,通过类对象和实例对象访问都发生了改变
- 从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;
- 实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。
- 静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类实例对象来引用