面向对象的三大特性:封装性、继承性、多态性
在Python代码中,封装有两层含义:
class Person(): # 封装属性 # 封装方法
在面向对象代码中,我们可以把属性和方法分为两大类
公有属性和公有方法:无论在类的内部还是在类的外部我们都可以对属性和方法进行操作。
但是有些情况下,我们不希望在类的外部对类内部的属性和方法进行操作。我们就可以把这个属性或方法封装成私有形式。
设置私有属性和私有方法的方式非常简单:在属性名和方法名 前面 加上两个下划线 __
即可。
基本语法
class Girl(): def __init__(self, name): self.name = name self.__age = 18 xiaomei = Girl('小美') print(xiaomei.name) print(xiaomei.__age) # 报错,提示Girl对象没有__age属性
类中的私有属性和私有方法,不能被其子类继承。
由以上代码运行可知,私有属性不能在类的外部被直接访问。但是出于种种原因,我们想在外部对私有属性进行访问,该如何操作呢?
答:我们可以定义一个统计的访问"接口"(函数),专门用于实现私有属性的访问。
在Python中,一般定义函数名’ get_xx ‘用来获取私有属性,定义’ set_xx '用来修改私有属性值。
class Girl(): def __init__(self, name): self.name = name self.__age = 18 # 公共方法:提供给外部的访问接口 def get_age(self): # 本人访问:允许直接访问 # 外人访问:加上限制条件 return self.__age # 公共方法:提供给外部的设置接口 def set_age(self, age): self.__age = age girl = Girl('小美') girl.set_age(19) print(girl.get_age())
私有方法的定义方式与私有属性基本一致,在方法名的前面添加两个下划线__方法名()
class People(): def __init__(self, name, age): self.__name = name self.__age = age def tell_info(self): print('Name:<%s> Age:<%s>' % (self.__name, self.__age)) # 对私有属性的访问接口 def set_info(self, name, age): if not isinstance(name, str): print('名字必须是字符串类型') return if not isinstance(age, int): print('年龄必须是数字类型') return self.__name = name self.__age = age p = People('jack', 38) p.tell_info() p.set_info('jennifer', 18) p.tell_info() p.set_info(123, 35) p.tell_info()
class ATM: def __card(self): print('插卡') def __auth(self): print('用户认证') def __input(self): print('输入取款金额') def __print_bill(self): print('打印账单') def __take_money(self): print('取款') # 定义一个对外提供服务的公共方法 def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() atm = ATM() atm.withdraw()
我们接下来来聊聊Python代码中的“继承”:类是用来描述现实世界中同一组事务的共有特性的抽象模型,但是类也有上下级和范围之分,比如:生物 => 动物 => 哺乳动物 => 灵长型动物 => 人类 => 黄种人
从哲学上说,就是共性与个性之间的关系,比如:白马和马!所以,我们在OOP代码中,也一样要体现出类与类之间的共性与个性关系,这里就需要通过类的继承来体现。简单来说,如果一个类A使用了另一个类B的成员(属性和方法),我们就可以说A类继承了B类,同时这也体现了OOP中代码重用的特性!
假设A类要继承B类中的所有属性和方法(私有属性和私有方法除外)
class B(object): pass clss A(B): pass a = A() a.B中的所有公共属性 a.B中的所有公共方法
案例
Person类与Teacher、Student类之间的继承关系
class Person(object): def eat(self): print('i can eat food!') def speak(self): print('i can speak!') class Teacher(Person): pass class Student(Person): pass teacher = Teacher() teacher.eat() teacher.speak() student = Student() student.eat() studnet.speak()
很显然,继承和派生其实就是从不同的方向来描述的相同的概念而已,本质上是一样的!
单继承:一个类只能继承自一个其他的类,不能继承多个类。这个类会有具有父类的属性和方法。
基本语法
# 1、定义一个共性类(父类) class Person(object): pass # 2、定义一个个性类(子类) class Teacher(Person): pass
案例
比如汽车可以分为两种类型(汽油车、电动车)
# 1、定义一个共性类(车类) class Car(object): def run(self): print('i can run') # 2、定义汽油车 class GasolineCar(Car): pass # 3、定义电动车 class EletricCar(Car): pass bwm = GasolineCar() bwm.run()
在Python继承中,如A类继承了B类,B类又继承了C类。则根据继承的传递性,则A类也会自动继承C类中所有公共的属性和方法
class C(object): def func(self): print('我是C类中的相关方法func') class B(C): pass class A(B): pass a = A() a.func()
答:在Python中,类理论上是不区分大小写的。但是要遵循一定的命名规范:首字母必须是字母或下划线,其中可以包含字母、数字和下划线,而且要求其命名方式采用大驼峰。例如,电动汽车:EletricCar
答:在Python面向对象代码中,建议在编写父类时,让其自动继承object类。但是其实不写也可以,因为默认情况下,Python中的所有类都继承自object。
什么是多继承?所谓的多继承就是允许一个类同时继承自多个类的特性。Python语言是少数支持多继承的一门编程语言。
基本语法
class B(object): pass class C(object): pass class A(B, C): pass a = A() a.B中的所有属性和方法 a.C中的所有属性和方法
案例
汽油车、电动车 => 混合动力汽车(汽车 + 电动)
class GasolineCar(object): def run_with_gasoline(self): print('i can run with gasoline') class EletricCar(object): def run_with_eletric(self): print('i can run with eletric') class HybridCar(GasolineCar, EletricCar): pass tesla = HybridCar() tesla.run_with_gasoline() tesla.run_with_eletric()
注意:虽然多继承允许我们同时继承自多个类,但是实际开发中,应尽量避免使用多继承,因为如果两个类中出现了相同的属性和方法就会产生命名冲突
扩展特性:继承让子类继承父类的所有公共属性和方法,但是如果仅仅是为了继承公共属性和方法,继承就没有实际的意义了,应该是在继承以后,子类应该有一些自己的属性和方法。
什么是重写?
重写也叫作覆盖,就是当子类成员与父类成员名字相同的时候,从父类继承下来的成员会重新定义!
此时,通过子类实例化出来的对象访问相关成员的时候,真正其作用的是子类中定义的成员!
上面单继承例子中 Animal 的子类 Cat和Dog 继承了父类的属性和方法,但是我们狗类Dog 有自己的叫声’汪汪叫’,猫类 Cat 有自己的叫声 ‘喵喵叫’ ,这时我们需要对父类的 call() 方法进行重构。如下:
class Animal(object): def eat(self): print('i can eat') def call(self): print('i can call') class Dog(Animal): pass class Cat(Animal): pass wangcai = Dog() wangcai.eat() wangcai.call() miaomiao = Cat() miaomiao.eat() miaomiao.call()
Dog、Cat子类重写父类Animal中的call方法:
class Animal(object): def eat(self): print('i can eat') # 公共方法 def call(self): print('i can call') class Dog(Animal): # 重写父类的call方法 def call(self): print('i can wang wang wang') class Cat(Animal): # 重写父类的call方法 def call(self): print('i can miao miao miao') wangcai = Dog() wangcai.eat() wangcai.call() miaomiao = Cat() miaomiao.eat() miaomiao.call()
思考:重写父类中的call方法以后,此时父类中的call方法还在不在?
答:还在,只不过是在其子类中被覆盖了。类方法的调用顺序,当我们在子类中重构父类的方法后,Cat子类的实例先会在自己的类 Cat 中查找该方法,当找不到该方法时才会去父类 Animal 中查找对应的方法。
super()
:调用父类属性或方法,完整写法:super(当前类名, self).属性或方法()
,在Python3以后版本中,调用父类的属性和方法我们只需要使用super().属性或super().方法名()
就可以完成调用了。
案例:Car汽车类、GasolineCar汽油车、ElectricCar电动车
class Car(object): def __init__(self, brand, model, color): self.brand = brand self.model = model self.color = color def run(self): print('i can run') class GasolineCar(Car): def __init__(self, brand, model, color): super().__init__(brand, model, color) def run(self): print('i can run with gasoline') class ElectricCar(Car): def __init__(self, brand, model, color): super().__init__(brand, model, color) # 电池属性 self.battery = 70 def run(self): print(f'i can run with electric,remain:{self.battery}') bwm = GasolineCar('宝马', 'X5', '白色') bwm.run() tesla = ElectricCar('特斯拉', 'Model S', '红色') tesla.run()
MRO(Method Resolution Order):方法解析顺序,我们可以通过类名.__mro__
或类名.mro()
获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找到。
class Car(object): def __init__(self, brand, model, color): self.brand = brand self.model = model self.color = color def run(self): print('i can run') class GasolineCar(Car): def __init__(self, brand, model, color): super().__init__(brand, model, color) def run(self): print('i can run with gasoline') class ElectricCar(Car): def __init__(self, brand, model, color): super().__init__(brand, model, color) # 电池属性 self.battery = 70 def run(self): print(f'i can run with electric,remain:{self.battery}') print(ElectricCar.__mro__) print(ElectricCar.mro())
object类还是所有类的基类(因为这个查找关系到object才终止)
多态指的是一类事物有多种形态。
定义
多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果。
首先定义一个父类,其可能拥有多个子类对象。当我们调用一个公共方法时,传递的对象不同,则返回的结果不同。
好处
调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!
公共接口service就是多态的体现,随着传入水果对象的不同,能返回不同的结果。
代码实现
多态:可以基于继承也可以不基于继承
class Fruit(object): # 公共方法 def makejuice(self): print('i can make juice') class Apple(Fruit): def makejuice(self): print('i can make apple juice') class Banana(Fruit): def makejuice(self): print('i can make banana juice') class Orange(Fruit): def makejuice(self): print('i can make orange juice') class Peach(Fruit): def makejuice(self): print('i can make peach juice') # 定义公共方法如service def service(obj): obj.makejuice() apple = Apple() banana = Banana() orange = Orange() for i in (apple, banana, orange): service(i)
Python中,属性可以分为实例属性和类属性。
类属性就是 类对象中定义的属性,它被该类的所有实例对象所共有。通常用来记录 与这类相关 的特征,类属性 不会用于记录 具体对象的特征。
在Python中,一切皆对象。类也是一个特殊的对象,我们可以单独为类定义属性。
class Person(object): # 定义类属性 count = 0 def __init(self, name, age): self.name = name self.age = age p1 = Person('Tom', 23) p2 = Person('Harry', 26)
定义count类属性,用于记录实例化Person类,产生对象的数量。
class Person(object): # 定义类属性count count = 0 # 定义一个__init__魔术方法,用于进行初始化操作 def __init__(self, name): self.name = name # 对count类属性进行+1操作,用于记录这个Person类一共生成了多少个对象 Person.count += 1 # 1、实例化对象p1 p1 = Person('Tom') p2 = Person('Harry') p3 = Person('Jennifer') # 2、在类外部输出类属性 print(f'我们共使用Person类生成了{Person.count}个实例对象')
为什么需要类方法,在面向对象中,特别强调数据封装性。所以不建议直接在类的外部对属性进行直接设置和获取。所以我们如果想操作类属性,建议使用类方法。
class Tool(object): # 定义一个类属性count count = 0 # 定义一个__init__初始化方法 def __init__(self, name): self.name = name Tool.count += 1 # 封装一个类方法:专门实现对Tool.count类属性进行操作 @classmethod def get_count(cls): print(f'我们使用Tool类共实例化了{cls.count}个工具') t1 = Tool('斧头') t2 = Tool('榔头') t3 = Tool('铁锹') Tool.get_count()
类方法主要用于操作类属性或类中的其他方法。
在开发时,如果需要在类中封装一个方法,这个方法:
这个时候,可以把这个方法封装成一个静态方法。
示例代码
# 开发一款游戏 class Game(object): # 开始游戏,打印游戏功能菜单 @staticmethod def menu(): print('1、开始游戏') print('2、游戏暂停') print('3、退出游戏') # 开始游戏、打印菜单 Game.menu()
需求分析
设计一个Game
类。
属性
top_score
记录游戏的历史最高分player_name
记录当前游戏的玩家姓名方法
show_help
显示游戏帮助信息show_top_score
显示历史最高分start_game
开始当前玩家的游戏示例代码
class Game(object): # 1、定义类属性top_score top_score = 0 # 2、定义初始化方法__init__ def __init__(self, player_name): self.player_name = player_name # 3、定义静态方法,用于输出帮助信息 @staticmethod def show_help(): print('游戏帮助信息') # 4、定义类方法 @classmethod def show_top_score(cls): print(f'本游戏历史最高分:{cls.top_score}') # 5、定义实例方法,start_game() def start_game(self): print(f'{self.player_name},游戏开始了,你准备好了么?') # 实例化类生成实例对象 mario = Game('itheima') mario.start_game() # 显示历史最高分 Game.show_top_score() # 弹出游戏帮助信息 Game.show_help()