面向对象程序设计

133 阅读7分钟

面向过程与面向对象

image.png

类和对象

具有相同属性和行为的一类实体(对象)的抽象就是类

Python中一切皆对象

  • 不同的数据类型属性不同的类
  • 使用内置函数type()查看对象的数据类型

自定义类

语法结构

  • 类名首字母大写
class 类名():
    pass

创建对象

语法结构

对象名 = 类名()

类的组成

类的组成部分

  • 类属性: 直接定义在类中,方法外的变量称为类属性,被该类的所有对象所共享,使用类名打点访问
  • 实例属性: 定义在__init__方法中,使用self打点的变量称为实例属性

image.png

  • 实例方法: 定义在类中,方法带在默认参数self的函数称为实例方法,使用对象名打点访问的方法

image.png

  • 静态方法: 使用@staticmethod修饰的方法,使用类名直接访问的方法

image.png

  • 类方法: 使用@classmethod修饰的方法,带有默认参数cls,使用类名打点访问

image.png

创建对象

image.png

image.png

动态绑定属性和方法

  • 类是模板,可以创建N多个对象,每个对象的属性名是相同的,属性值是不同的
  • Python是动态语言,在创建对象之后,可以动态地绑定属性和方法

image.png

image.png

image.png

访问权限控制

访问限制

  • 对属性或方法添加单下划线、双下划线、首尾双下划线,限制访问权限
  • 单下划线
    • 以单下划线开头,表示protected受保护的成员,只允许类本身和子类进行访问,可以通过对象名去访问
  • 双下划线
    • 双下划线表示private私有的成员,只允许定义该方法的类本身访问,不能通过类的对象访问,但可以通过 “对象名._类名__xxx” 方式访问
  • 首尾双下划线
    • 表示特殊的方法,一般是系统定义的名字
class Student:
    # 首尾双下划线,表示特殊的方法,系统定义
    def __init__(self, name, age, gender):
        self._name = name  # 以单下划线开头,表示受保护的成员,只能类本身和子类访问
        self.__age = age  # 以双下划线开头,表示是私有的,只能类本身使用
        self.gender = gender  # 普通的实例属性,在类的外部和内部以及子类都可以访问

    # 以单下划线开头,表示是受保护的方法
    def _func1(self):
        print("子类和本身可以访问")

    # 以双下划线开头,表示的是私有方法
    def __func2(self):
        print("只有定义的类可以访问")

    # 这是一个普通的实例方法,在类的外部使用对象名打点访问
    # 在类的内部,使用self打点访问
    def show(self):
        self._func1()  # 类本身访问受保护的方法
        self.__func2()  # 类本身访问私有的方法
        print(self._name)  # 类本身访问受保护的实例属性
        print(self.__age)  # 类本身访问私有的实例属性


if __name__ == '__main__':
    # 创建一个Student类型的对象
    stu = Student('ivan', 18, '男')
    # 访问受保护的实例属性
    print(stu._name)
    # 访问私有的实例属性
    # print(stu.__age)    # 程序报错,出了类的定义范围

    # 访问受保护的实例方法
    stu._func1()
    # 访问私有的实例方法
    # stu.__func2()

    # 可以使用以下的形式访问类对象的私有成员,但不建议
    print(stu._Student__age)

    stu._Student__func2()

面向对象的三大特征

封装

  • 隐藏内部细节,提高程序的安全性(健壮性)
class Student:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def show(self):
        print(f"我叫:{self.__name}, 我今年:{self.__age}岁了")


if __name__ == '__main__':
    # 程序运行没有报错,但是运行结果不符合实际情况,年龄被赋予了不正确的值,所以程序不够安全
    stu = Student('inva', -23)
    stu.show()


class Student:
    def __init__(self, name):
        self.__name = name

    # 使用@property
    @property
    def age(self):  # 设置只读属性
        return self.__age

    # 设置赋值操作
    @age.setter
    def age(self, value):
        if value < 0 or value > 130:
            print("年龄不在正确的区间范围,设置的年龄范围应该是0-130之间")
            self.__age = 18  # 设置默认值
        else:
            self.__age = value

    def show(self):
        print(f"我叫:{self.__name}, 我今年:{self.__age}岁了")


if __name__ == '__main__':
    stu = Student('inva')
    stu.age = -23
    print(stu.age)
    stu.show()
    stu.age = 20
    stu.show()

继承

  • 实现继承,表示这个类拥有了被继承类的所有公有成员和受保护成员,被继承的类称为父类或基类,新的类称为子类或派生类
  • 作用:实现代码的复用(重复使用),通过继承可以理顺类与类之间的关系
  • 语法结构:
class 类名(sup[er_class_lst):
    pass
class Person:  # 默认继承了object类
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def show(self):
        print(self.name, self.age)


# Student类继承Person类
class Student(Person):
    # 编写初始化方法
    def __init__(self, name, age, stuno):
        # 调用父类的初始化方法
        super().__init__(name, age)  # 给name和age进行赋值
        self.stuno = stuno  # 给自己特有的属性进行复制

    def des(self):
        print(self.stuno)


# Doctor类继承Person类
class Doctor(Person):
    def __init__(self, name, age, department):
        # 调用父类的初始化方法
        Person.__init__(self, name, age)  # 给name和age进行赋值
        self.department = department  # 给自己特有的属性进行复制

    def num(self):
        print(self.department)


if __name__ == '__main__':
    # 创建Student类的对象
    stu = Student('ivan', 20, 'st1001')
    stu.show()
    stu.des()

    # 创建Doctor类的对象
    doc = Doctor('jack', 20, 'dt1001')
    doc.show()
    doc.num()

多继承

  • 一个子类可以有多个“直接父类”,这样就具备了“多个父类”的特点
class FatherA:
    def __init__(self, name):
        self.name = name

    def showA(self):
        print("FatherA", self.name)


class FatherB:
    def __init__(self, age):
        self.age = age

    def showB(self):
        print("FatherB", self.age)


class Son(FatherA, FatherB):
    def __init__(self, name, age, gender):
        FatherA.__init__(self, name)  # 给name赋值
        FatherB.__init__(self, age)  # 给age赋值
        self.gender = gender


if __name__ == '__main__':
    son = Son("James", 30, "Male")
    son.showA()
    son.showB()

方法重写

  • 当父类(基类)中的某个方法不完全适合子类(派生类)时,就需要在子类中重写父类的方法
  • 子类重写后的方法中可以通过super().xxx()调用父类中被重写的方法
class Person:  # 默认继承了object类
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def show(self):
        print(self.name, self.age)


# Student类继承Person类
class Student(Person):
    # 编写初始化方法
    def __init__(self, name, age, stuno):
        # 调用父类的初始化方法
        super().__init__(name, age)  # 给name和age进行赋值
        self.stuno = stuno  # 给自己特有的属性进行复制

    # 重写父类的方法
    def show(self):
        # 可以去调用父类的show方法,也可以重写实现方法的代码,显示输出内容
        # 调用父类的方法
        super().show()
        # 再编写自己个性化的内容
        print(f"我的学号是:{self.stuno}")


# Doctor类继承Person类
class Doctor(Person):
    def __init__(self, name, age, department):
        # 调用父类的初始化方法
        Person.__init__(self, name, age)  # 给name和age进行赋值
        self.department = department  # 给自己特有的属性进行复制

    # 重写父类的方法
    def show(self):
        # 重写实现方法的代码,显示输出内容
        print(f"我叫{self.name},年龄是:{self.age},我的工作科室是:{self.department}")


if __name__ == '__main__':
    # 创建Student类的对象
    stu = Student('ivan', 20, 'st1001')
    stu.show()

    # 创建Doctor类的对象
    doc = Doctor('jack', 20, 'dt1001')
    doc.show()

object类

  • object类是所有类的父类,因此所有类都有object类的属性和方法
  • 内置函数 dir() 可以查看指定对象所有属性
  • object有一个 str() 方法,用于返回一个对于“对象的描述”,对应于内置函数 str() 经常用于print()方法,帮我们查看对象的信息,所以我们经常会对 str() 进行重写
print(dir(object))


# Person默认继承object类,所有object可以省略不写
class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def show(self):
        print(f"大家好,我叫{self.name},今年{self.age}岁了")

    # 重写__str__方法
    def __str__(self):
        return '这是一个人类,具有name和age两个实例属性'


if __name__ == '__main__':
    per = Person('ivan', 20)
    per.show()
    print(dir(per))
    print(per)  # 当直接输出对象名时,默认调用__str__()
    print(per.__str__())

    # Person类对象,调用父类的__dir__方法
    # 以下这两句代码功能相同
    print(per.__dir__())
    print(dir(per))

多态

  • 简单地说,多态就是“具有多种形态”,它指的是:即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用方法,再运行过程中根据变量所引用对象的类型,动态决定调用哪个对象中的方法
  • Python语言中的多态,不关心对象的数据类型,不关心是否具有继承关系,只关心对象的行为(方法)
# 以下三个类都有一个同名的方法 eat()
class Person:
    def eat(self):
        print('人吃饭')


class Cat:
    def eat(self):
        print('猫吃鱼')


class Dog:
    def eat(self):
        print('狗吃骨头')


def fun(obj):  # 函数的定义处,obj是函数的形式参数
    obj.eat()  # 对象名打点调用 eat 方法


if __name__ == '__main__':
    per = Person()
    cat = Cat()
    dog = Dog()

    # 调用fun函数,不关心对象的数据类型,只关心是否具有这个方法
    fun(per)
    fun(cat)
    fun(dog)

特殊方法和特殊属性

特殊方法

  • Python中的运算符实际上是通过调用特殊方法实现的

image.png

特殊属性

  • 常用的特殊属性

image.png