python基础学习笔记13:面向对象的三大特性

2021/11/10 22:10:14

本文主要是介绍python基础学习笔记13:面向对象的三大特性,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1、面向对象有哪些特性

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

2、Python中的封装

在Python代码中,封装有两层含义:

  • 把现实世界中的主体中的属性和方法书写到类的里面的操作即为封装
class Person():
    # 封装属性
    # 封装方法
  • 封装可以为属性和方法添加为私有权限

2.1 封装中的私有属性和私有方法

在面向对象代码中,我们可以把属性和方法分为两大类

  • 公有(属性、方法)
  • 私有(属性、方法)

公有属性和公有方法:无论在类的内部还是在类的外部我们都可以对属性和方法进行操作。

但是有些情况下,我们不希望在类的外部对类内部的属性和方法进行操作。我们就可以把这个属性或方法封装成私有形式。

2.2 私有属性的访问限制

设置私有属性和私有方法的方式非常简单:在属性名和方法名 前面 加上两个下划线 __ 即可。

基本语法

class Girl():
    def __init__(self, name):
        self.name = name
        self.__age = 18

xiaomei = Girl('小美')
print(xiaomei.name)
print(xiaomei.__age)  # 报错,提示Girl对象没有__age属性

类中的私有属性和私有方法,不能被其子类继承。

由以上代码运行可知,私有属性不能在类的外部被直接访问。但是出于种种原因,我们想在外部对私有属性进行访问,该如何操作呢?

答:我们可以定义一个统计的访问"接口"(函数),专门用于实现私有属性的访问。

2.3 私有属性设置与访问接口

在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())

2.4 私有方法

私有方法的定义方式与私有属性基本一致,在方法名的前面添加两个下划线__方法名()

2.5 封装性到底有何意义

  • 以面向对象的编程思想进行项目开发
  • 封装数据属性:明确的区分内外,控制外部对隐藏的属性的操作行为(过滤掉异常数据)
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()

3、Python中的继承

3.1 什么是继承

我们接下来来聊聊Python代码中的“继承”:类是用来描述现实世界中同一组事务的共有特性的抽象模型,但是类也有上下级和范围之分,比如:生物 => 动物 => 哺乳动物 => 灵长型动物 => 人类 => 黄种人

从哲学上说,就是共性与个性之间的关系,比如:白马和马!所以,我们在OOP代码中,也一样要体现出类与类之间的共性与个性关系,这里就需要通过类的继承来体现。简单来说,如果一个类A使用了另一个类B的成员(属性和方法),我们就可以说A类继承了B类,同时这也体现了OOP中代码重用的特性

3.2 继承的基本语法

假设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()

3.3 与继承相关的几个概念

  • 继承:一个类从另一个已有的类获得其成员的相关特性,就叫作继承!
  • 派生:从一个已有的类产生一个新的类,称为派生!

很显然,继承和派生其实就是从不同的方向来描述的相同的概念而已,本质上是一样的!

  • 父类:也叫作基类,就是指已有被继承的类!
  • 子类:也叫作派生类或扩展类
  • 扩展:在子类中增加一些自己特有的特性,就叫作扩展,没有扩展,继承也就没有意义了!
  • 单继承:一个类只能继承自一个其他的类,不能继承多个类,单继承也是大多数面向对象语言的特性!
  • 多继承:一个类同时继承了多个父类, (C++、Python等语言都支持多继承)

3.4 单继承

单继承:一个类只能继承自一个其他的类,不能继承多个类。这个类会有具有父类的属性和方法。

基本语法

# 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()

3.5 单继承特性:传递性

在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()

3.6 编写面向对象代码中的常见问题

  • 问题1:在定义类时,其没有遵循类的命名规则

答:在Python中,类理论上是不区分大小写的。但是要遵循一定的命名规范:首字母必须是字母或下划线,其中可以包含字母、数字和下划线,而且要求其命名方式采用大驼峰。例如,电动汽车:EletricCar

  • 问题2:父类一定要继承object么?Car(object)

答:在Python面向对象代码中,建议在编写父类时,让其自动继承object类。但是其实不写也可以,因为默认情况下,Python中的所有类都继承自object。
在这里插入图片描述

3.7 多继承

什么是多继承?所谓的多继承就是允许一个类同时继承自多个类的特性。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()

注意:虽然多继承允许我们同时继承自多个类,但是实际开发中,应尽量避免使用多继承,因为如果两个类中出现了相同的属性和方法就会产生命名冲突

3.8 子类扩展:重写父类属性和方法

扩展特性:继承让子类继承父类的所有公共属性和方法,但是如果仅仅是为了继承公共属性和方法,继承就没有实际的意义了,应该是在继承以后,子类应该有一些自己的属性和方法。

什么是重写?

重写也叫作覆盖,就是当子类成员与父类成员名字相同的时候,从父类继承下来的成员会重新定义!

此时,通过子类实例化出来的对象访问相关成员的时候,真正其作用的是子类中定义的成员!

上面单继承例子中 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 中查找对应的方法。

3.9 super()调用父类属性和方法

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()

3.10 MRO属性或MRO方法:方法解析顺序

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才终止)

4、Python中的多态

4.1 什么是多态

多态指的是一类事物有多种形态。

定义

多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果。

  • 多态依赖继承
  • 子类方法必须要重写父类方法

首先定义一个父类,其可能拥有多个子类对象。当我们调用一个公共方法时,传递的对象不同,则返回的结果不同。

好处

调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!

4.2 多态原理图

在这里插入图片描述
公共接口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)

5、面向对象其他特性

5.1 类属性

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)

5.2 类属性代码实现

定义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}个实例对象')

5.3 类方法

为什么需要类方法,在面向对象中,特别强调数据封装性。所以不建议直接在类的外部对属性进行直接设置和获取。所以我们如果想操作类属性,建议使用类方法。

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()

类方法主要用于操作类属性或类中的其他方法。

5.4 静态方法

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

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

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

示例代码

# 开发一款游戏
class Game(object):
    # 开始游戏,打印游戏功能菜单
    @staticmethod
    def menu():
        print('1、开始游戏')
        print('2、游戏暂停')
        print('3、退出游戏')

# 开始游戏、打印菜单
Game.menu()

6、综合案例

需求分析

设计一个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()


这篇关于python基础学习笔记13:面向对象的三大特性的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程