Python面向对象编程

1 概述

  • 『对象』是一个拥有属性和行为的个体,它是构成现实世界和程序世界的基本单位。
  • 『面向对象』是一种以对象为中心,去思考和组织代码的方式,它更关注"谁来做这件事"。
  • 『类』是用来描述一类事物的"模板",它规定了了一类事物所具有的属性和行为。『实例』是根据『类』创建出来的一个具体的『对象』。

2 基本使用

  • 类的定义与创建
    • 类名通常采用大驼峰命名法。

    • 类中所定义的函数,通常又称为方法。

    • __init__方法叫初始化方法,当我们对类进行实例化时,Python会自动调用__init__方法。

    • __init__方法的名字不能更改,否则 Python 无法自动调用。

    • __init__收到的第一个参数是当前正在创建的实例对象,形参通常用self。

    • __init__方法收到的除self以外的参数,通常用来设置实例的属性值,通过"点"语法实现:self.属性名 = 属性值

    • 代码示例
      *

      python 复制代码
        # 定义一个Person类
        class Person:
            # __init__: 初始化方法
            # self: 实例对象
            # __init__不需要手动调用,创建对象时会自动调用
            def __init__(self, name, age):
                # 给实例对象添加属性
                self.name = name
                self.age = age
        
        # 创建实例对象
        p1 = Person('张三', 26)
        p2 = Person('李四', 28)
        
        # 访问属性
        print(p1.name, p1.age)
        print(p2.name, p2.age)
        
        # 修改属性
        p1.name = '王五'
        print(p1.name, p1.age)
        
        # 查看实例的所有属性
        print(p1.__dict__)
        
        # 给实例追加属性
        p1.address='北京'
        print(p1.__dict__)
        
        # 通过type函数可以查看实例对象是哪个类创建出来的
        print(type(p1))
  • 自定义方法
    • 要想让对象具备相应的『行为』,就需要通过『自定义方法』来实现。

    • 代码示例
      *

      python 复制代码
        # 定义一个Person类
        class Person:
            def __init__(self, name, age):
                self.name = name
                self.age = age
        
            # 自定义方法
            # speak方法只有一份,属于类
            def speak(self, msg):
                print(f'我叫{self.name}, 年龄是{self.age}, 我想说: {msg}')
        
        
        # 创建实例对象
        p1 = Person('张三', 26)
        p2 = Person('李四', 28)
        print(p1.__dict__)
        print(p2.__dict__)
        
        # 执行p1.speak()时,先查找p1自身有没有speak方法,没有的话再查看类有没有speak方法
        p1.speak('我今天要加班')
        p2.speak('我今天要去看电影')
        
        def speak(msg):
            print(f'这个不是类的speak方法 {msg}')
        
        p1.speak = speak
        
        print(p1.__dict__)
        print(p2.__dict__)
        
        p1.speak('p1 hello world')
        p2.speak('p2 hello world')

3 实例属性和类属性

  • 实例属性

    • 通过实例.属性名 = 值定义在实例身上的属性,称为:实例属性。

    • 每个实例都有自己『独立的一份』实例属性,各个实例之间是互不影响的。

    • 实例属性只能通过实例.xxxx访问和修改,不能通过『类名』访问或修改。

    • 代码示例
      *

      python 复制代码
        # 定义一个Person类
        class Person:
            # 通过 实例名.属性名 = 值 给实例添加的属性,叫实例属性
            # 实例属性只能通过实例访问,不能通过类访问
            # 每个实例都有自己独有的实例属性
            def __init__(self, name, age):
                # 给实例对象添加属性
                self.name = name
                self.age = age
            
        p1 = Person('张三', 26)
        print(p1.name)
        # print(Person.name) # 不能访问
  • 类属性

    • 在类中直接写赋值语句(例如:a = 100),就会在类身上添加一个a属性,值为 100,此时的a就是『类属性』,它属于类本身,由类所拥有,并且该类创建出来的所有实例对象,都能去访问a属性。

    • 所有实例访问的,都是同一个类属性,所以类属性通常用于:存放公共数据。

    • 类属性即可以通过『类』访问,也可以『实例』访问。

    • 代码示例
      *

      python 复制代码
        # 定义一个Person类
        class Person:
            # 类属性, 属于类
            # 类属性可以通过类访问,也可以通过实例访问
            # 类属性通常用于保存公共数据
            max_age = 120
        
            # 给实例添加属性
            def __init__(self, name, age):
                # 给实例对象添加属性
                self.name = name
        
                # 限制age最大值
                if age <= Person.max_age:
                    self.age = age
                else:
                    print(f'年龄超出最大范围,已将年龄设置为最大值: {Person.max_age}')
                    self.age = Person.max_age
        
        p1 = Person('张三', 26)
        p2 = Person('李四', 28)
        
        # 类属性可以通过类访问,也可以通过实例访问
        print(p1.max_age)
        print(Person.max_age)
        
        p3 = Person('王五', 270)

4 实例方法、类方法、静态方法

  • 实例方法
    • 类中所定义的方法,最终会保存在类身上,并且主要是通过实例调用,所以叫:实例方法。

    • 实例方法虽然最终会保存在类身上,但它主要是供实例使用的,所以才叫实例方法。

    • 因为收到了self参数,所以其内部可以:访问实例属性,调用实例方法。

    • 实例方法的主要作用:定义实例对象的具体行为。

    • 代码示例
      *

      python 复制代码
        # 定义一个Person类
        class Person:
            def __init__(self, name, age):
                self.name = name
                self.age = age
        
            # 供实例调用,实例方法
            def speak(self, msg):
                print(f'我叫{self.name}, 年龄是{self.age}, 我想说: {msg}')
        
            # 供实例调用,实例方法
            def run(self, distance):
                print(f'{self.name}奔跑了{distance}米')
        
        
        p1 = Person('张三', 26)
        p2 = Person('李四', 28)
        
        # 通过实例调用实例方法
        p1.speak('你好')
        p1.run(2000)
        
        # 通过类调用实例方法(不推荐)
        Person.run(p2, 3000)
  • 类方法
    • 使用@classmethod装饰器修饰,第一个参数是类本身,通常用形参cls接收。

    • 可通过『类名』或『实例名』调用,但强烈推荐通过类名调用以体现语义。

    • 因为收到了cls参数,所以内部可以访问类属性。

    • 一般用于:实现与类相关的逻辑,如:操作类级别的信息、工厂方法等。

    • 代码示例
      *

      python 复制代码
        from datetime import datetime
      
        # 定义一个Person类
        class Person:
            # 类属性
            max_age = 120
        
            def __init__(self, name, age):
                self.name = name
                # 限制age最大值
                if age <= Person.max_age:
                    self.age = age
                else:
                    print(f'年龄超出最大范围,已将年龄设置为最大值: {Person.max_age}')
                    self.age = Person.max_age
        
            # 供实例调用,实例方法
            def speak(self, msg):
                print(f'我叫{self.name}, 年龄是{self.age}, 我想说: {msg}')
        
            # 类方法。使用@classmethod装饰过的方法叫类方法。
            @classmethod
            def change_max_age(cls, value):
                cls.max_age = value
        
            @classmethod
            def create(cls, value):
                name, year = value.split('-')
                # 获取当前年份
                cur_year = datetime.now().year
                age = cur_year - int(year)
                # 创建并返回Person类的实例对象
                return cls(name, age)
        
        p1 = Person('张三', 135)
        
        # 调用类方法,修改类属性
        Person.change_max_age(150)
        
        p2 = Person('李四', 135)
        
        print(p1.__dict__)
        print(p2.__dict__)
        
        # 调用类方法,创建实例对象
        p3 = Person.create('王五-1994')
        print(p3.__dict__)
  • 静态方法
    • 使用@staticmethod装饰器修饰,方法没有self或cls参数,只是单纯的定义在类中。

    • 可通过『类名』或『实例名』调用,但强烈推荐通过类名调用以体现语义。

    • 由于没有self或cls参数,所以静态方法中通常:不访问类属性,也不访问实例属性。

    • 一般用于:定义与类相关,但可以独立使用的工具方法。

    • 代码示例
      *

      python 复制代码
        # 定义一个Person类
        class Person:
            def __init__(self, name, age):
                self.name = name
                self.age = age
        
            # 静态方法
            # 使用@staticmethod装饰过的方法叫静态方法
            @staticmethod
            # 判断是否为成年人
            def is_adult(age):
                if age >= 18:
                    return True
                else:
                    return False
        
        
        # 静态方法通过类调用
        print(Person.is_adult(20))

5 继承

5.1 基本语法

  • 概念:是指一个类,可以继承另一个类的属性和方法。

  • 作用:可以实现代码的复用与扩展,避免重复编写相同的代码,让程序结构更简洁、更高效。

  • 示例1
    *

    python 复制代码
      # 定义一个Person类
      class Person:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          # 供实例调用,实例方法
          def speak(self, msg):
              print(f'我叫{self.name}, 年龄是{self.age}, 我想说: {msg}')
      
      
      # 定义一个Student类(子类、派生类),继承自Person类(父类、基类)
      class Student(Person):
          pass
      
      
      # 创建Student实例对象
      s1 = Student('小明', 18)
      
      print(s1.__dict__)
      print(type(s1))
      
      # 查找speak方法的过程:先找s1实例自身,再找Student类,再找基类Person类
      s1.speak('你好')
  • 示例2
    *

    python 复制代码
      # 定义一个Person类
      class Person:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          # 供实例调用,实例方法
          def speak(self, msg):
              print(f'我叫{self.name}, 年龄是{self.age}, 我想说: {msg}')
      
      
      # 定义一个Student类(子类、派生类),继承自Person类(父类、基类)
      class Student(Person):
          def __init__(self, name, age, code, grade):
              # 调用父类方法初始化,对继承属性进行初始化。方式1(推荐)
              super().__init__(name, age)
              # 调用父类方法初始化,对继承属性进行初始化。方式2
              # Person.__init__(self, name, age)
      
              # 子类属性单独初始化
              self.code = code
              self.grade = grade
      
          def study(self, msg):
              print(f'我叫{self.name}, 年级是{self.grade} 我想说: {msg}')
      
      
      # 创建Student实例对象
      s1 = Student('小明', 18, 10030, '高三')
      
      s1.study('好好学习')

5.2 方法重写

  • 如果子类中定义了与父类同名的方法,则会子类会"覆盖"父类中的方法,又称:"重写"。

  • 示例
    *

    python 复制代码
      # 定义一个Person类
      class Person:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          # 供实例调用,实例方法
          def speak(self, msg):
              print(f'我叫{self.name}, 年龄是{self.age}, 我想说: {msg}')
      
      
      # 定义一个Student类,继承自Person类
      class Student(Person):
          def __init__(self, name, age, code, grade):
              super().__init__(name, age)
      
              self.code = code
              self.grade = grade
      
          # 方法重写:当子类中定义了一个与父类中相同的方法,那么子类中的方法就会"覆盖"父类的方法
          def speak(self, msg):
              print(f'我是学生, 我叫{self.name}, 年级是{self.grade}, 学号是{self.code}, 我想说: {msg}')
      
      
      s1 = Student('小明', 18, '高三', 10030)
      
      s1.speak('好好学习')

5.3 两个常用方法

  • isinstance(obj, Class):判断对象是否为指定类或其子类的实例。

  • issubclass(Sub, Super):判断一个类是否是另一个类的子类。

  • 示例
    *

    python 复制代码
      # 定义一个Person类
      class Person:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
      # 定义一个Student类,继承自Person类
      class Student(Person):
          def __init__(self, name, age, code, grade):
              super().__init__(name, age)
              self.code = code
              self.grade = grade
      
      p1 = Person('张三', 35)
      s1 = Student('小明', 18, 10010, '高三')
      
      # 判断对象是否为指定类或其子类的实例
      print(isinstance(p1, Person))
      print(isinstance(s1, Student))
      print(isinstance(s1, Person))
      print(isinstance(p1, Student))
      
      # 判断某个类是否是另一个类的子类
      print(issubclass(Person, Student))
      print(issubclass(Student, Person))

5.4 多重继承

  • 多重继承指一个类同时继承多个父类,从而拥有多个父类的属性和方法。

  • 示例
    *

    python 复制代码
      # 定义一个Person类
      class Person:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          # 供实例调用,实例方法
          def speak(self):
              print(f'我叫{self.name}, 年龄是{self.age}')
      
      
      class Worker:
          def __init__(self, company):
              self.company = company
      
          def do_work(self):
              print(f'我在{self.company}做兼职')
      
      
      class Student(Person, Worker):
          def __init__(self, name, age, company, code, grade):
              Person.__init__(self, name, age)
              Worker.__init__(self, company)
      
              self.code = code
              self.grade = grade
      
          def study(self):
              print(f'我在努力学习,争取考进{self.grade}前十')
      
      
      s1 = Student('小明', 18, '顺丰快递', 10010, '高三')
      s1.speak()
      s1.do_work()
      s1.study()
      
      print(s1.__dict__)
      
      # 类的__mro__属性:用于记录属性和方法的查找顺序
      print(Student.__mro__)

6 访问控制

6.1 三种访问权限

权限类型 定义方式 在当前类内部访问 在子类内部访问 在类外部访问
公有属性 属性名 ✅ 能 ✅ 能 ✅ 能
受保护属性 _属性名 ✅ 能 ✅ 能 ⚠️ 能 (但不推荐)
私有属性 __属性名 ✅ 能 ❌ 不能 ❌ 不能
  • 示例代码
    *

    python 复制代码
      # 定义一个Person类
      class Person:
          def __init__(self, name, age, idcard):
              self.name = name  # 公有属性: 类中、子类中、类外部都可以访问
              self._age = age   # 受保护属性:类中、子类中可以访问,类外不可以访问
              self.__idcard = idcard # 私有属性: 仅可以在类中访问
      
          # 供实例调用,实例方法
          def speak(self, msg):
              print(f'我叫{self.name}, 年龄是{self._age}, 身份证号{self.__idcard}, 我想说: {msg}')
      
      
      class Student(Person):
          def hello(self):
              # 可以访问公有属性name和受保护属性age
              # 不能访问私有属性idcard
              print(f'我是学生, 我叫是{self.name}, 年龄是{self._age}, 我想说 hello')
      
      
      p1 = Person('张三', 35, '610323********8820')
      p1.speak('你好')

6.2 getter与setter

  • 在面向对象编程中,我们会把一些内部数据保护起来,但同时还想提供一个"安全的通道"让外部访问。这时候我们就用到:

    • getter:读取属性的方法
    • setter:修改属性的方法
  • 在 Python 中:通过@property和@xxx.setter语法,把普通方法变成像属性一样使用的方法。

  • 示例代码
    *

    python 复制代码
      # 定义一个Person类
      class Person:
          def __init__(self, name, age, idcard):
              self.name = name  # 公有属性: 类中、子类中、类外部都可以访问
              self._age = age   # 受保护属性:类中、子类中可以访问,类外不可以访问
              self.__idcard = idcard # 私有属性: 仅可以在类中访问
      
          # 注册age属性的getter方法,当访问Person实例的age属性时,下面的age方法就会被自动调用
          @property
          def age(self):
              return self._age
      
          # 注册age属性的setter方法,当修改Person实例的age属性时,下面的age方法就会被自动调用
          @age.setter
          def age(self, age):
              self._age = age
      
          # 供实例调用,实例方法
          def speak(self, msg):
              print(f'我叫{self.name}, 年龄是{self._age}, 身份证号{self.__idcard}, 我想说: {msg}')
      
      p1 = Person('张三', 18, '610323********3657')
      
      print(p1.name)
      # 既可以在类外访问age,也可以在类外修改age
      print(p1.age)
      p1.age = 20

7 魔法方法

  • 概念:以 __xxx__ 命名的特殊方法(双下划线开头和结尾)。

  • 特点:不需要手动调,在特定场景由 Python 自动调用。

    • __str__:当调用print(对象)或str(对象) 时
    • __len__:当调用len(对象)时
    • __lt__:当执行对象1 < 对象2时
    • __gt__:当执行对象1 > 对象2时
    • __eq__:当执行对象1 == 对象2时
    • __getattr__:当访问不存在的属性时
  • 示例代码
    *

    python 复制代码
      # 定义一个Person类
      class Person:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          # 执行print(Person实例对象) 或 str(Person实例对象) 时调用
          def __str__(self):
              return f'{self.name}, {self.age}'
      
          # 执行len(Person实例对象)时调用
          def __len__(self):
              return len(self.__dict__)
      
          # 执行 Person实例对象1 < Person实例对象2 时调用
          def __lt__(self, other):
              return self.age < other.age
      
          # 执行 Person实例对象1 == Person实例对象2 时调用
          def __eq__(self, other):
              return self.__dict__ == other.__dict__
      
          # 访问Person实例对象不存在的属性时调用
          def __getattr__(self, item):
              return f'您访问的{item}属性不存在'
      
          # 供实例调用,实例方法
          def speak(self, msg):
              print(f'我叫{self.name}, 年龄是{self.age}, 我想说: {msg}')
      
      p1 = Person('张三', 35)
      p2 = Person('李四', 40)
      
      # 底层调用的是__str__方法
      print(p1)
      
      # 底层调用的是__len__方法
      l1 = len(p1)
      print(l1)
      
      # 底层调用的是__lt__方法
      print(p1 < p2)
      
      # 底层调用的是__eq__方法
      print(p1 == p2)
      
      print(p1.address)

8 object类

  • object是所有类的最终祖先,所有的类,无论写与不写,都继承自object。

  • object提供了所有对象都会有的一组最基本方法,例如

    • __init__(self):在对实例对象初始化时调用
    • __str__(self):print(obj)或 int(obj)时调用
    • __class__:返回对象所属的类
  • 示例代码
    *

    python 复制代码
      # 定义一个Person类
      class Person:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
      
      # 验证Person类继承了object类
      print('Person类是否继承自object类', issubclass(Person, object))
      
      
      print('int类是否继承自object类', issubclass(int, object))
      print('str类是否继承自object类', issubclass(str, object))
      print('list类是否继承自object类', issubclass(list, object))
      
      
      p1 = Person('张三', 19)
      print('p1是否为object类的实例', isinstance(p1, object))
      print('[1, 2, 3]是否为object类的实例', isinstance([1, 2, 3], object))
      print('10086是否为object类的实例', isinstance(10086, object))
      
      print(p1.__dict__) # 打印p1自身的属性
      print(dir(p1))     # 打印p1可以访问到的属性
      
      # 打印object类的所有属性
      # for key in object.__dict__:
      #     print(key)

9 多态

  • 多态就是"多种形态",即:不同的对象调使用同一个方法名调用方法时,会表现出不同的行为。

9.1 标准多态

  • 基于继承实现多态,又叫:『传统多态』或『标准多态』。

  • 常见于Java、C++ 这样的强类型语言中,Python支持『标准多态』。

  • 代码示例
    *

    python 复制代码
      class Animal:
          def speak(self):
              print('动物正在发出声音')
      
      class Dog(Animal):
          def speak(self):
              print('汪汪汪')
      
      class Cat(Animal):
          def speak(self):
              print('喵喵喵')
      
      
      def make_sound(animal:Animal):
          # 多态的体现
          animal.speak()
      
      
      
      # 创建实例对象
      a1 = Animal()
      d1 = Dog()
      c1 = Cat()
      
      make_sound(a1)
      make_sound(d1)
      make_sound(c1)

9.2 鸭子多态

  • 鸭子类型:如果一个东西看起来像鸭子,叫起来也像鸭子,那它就是鸭子,鸭子类型指一种编程风格,它并不依靠查找对象类型,来确定其是否具有正确的实现,而是直接调用或使用其方法或属性。

  • 官方文档

  • 特点:不需要继承,只要传进来的对象,有对应实现就可以。

  • Python 中支持"鸭子多态"。

  • 示例
    *

    python 复制代码
      class Dog:
          def speak(self):
              print('汪汪汪')
      
      class Cat:
          def speak(self):
              print('喵喵喵')
      
      class Pig:
          def speak(self):
              print('哼哼哼')
      
      
      def make_sound(animal):
          # 多态的体现
          animal.speak()
      
      
      # 创建实例对象
      d1 = Dog()
      c1 = Cat()
      p1 = Pig()
      
      make_sound(d1)
      make_sound(c1)
      make_sound(p1)

10 抽象类

  • 抽象类(Abstract Class) 是一种 不能被直接实例化 的类,通常作为"规范",让子类去继承并实现其中定义的抽象方法,本身只定义规范,不需要提供完整实现。

  • 示例代码
    *

    python 复制代码
      from abc import ABC, abstractmethod
    
      # 继承了ABC的类是抽象类
      class MustRun(ABC):
          # 抽象方法
          @abstractmethod
          def run(self):
              pass
          # 也可以写普通方法
          def speak(self):
              print(f"我叫{self.name}")
      
      
      class Person(MustRun):
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def run(self):
              print(f'我叫{self.name}, 年龄{self.age}, 正在奔跑')
      
      # 不能实例化对象
      # m1 = MustRun()
      
      p1 = Person('张三', 30)
      p1.run()
      p1.speak()

11 内存分析

  • 内存分为两个部分:栈内存、堆内存;变量在栈内存中,对象在堆内存中。
  • Python 中变量里保存的不是存数据,而是指向堆中对象的引用(内存地址)。
  • 不可变对象:重新赋值会创建新对象
    • int 类的实例对象,是不可变对象,所以修改变量 a 时,会创建新对象,不会影响其他引用(b)
    • Python 中常见的不可变对象有:int 、float 、bool 、str 、tuple 、frozenset 、None。
    • Python 中常见的可变对象有:list 、dict 、set 、自定义类的实例对象。
  • 可变对象:修改内容不改变地址
  • 自定义类对象的内存表示
相关推荐
知识分享小能手1 小时前
C学习教程,从入门到精通,C语言概述 —— 知识点详解(1)
c语言·开发语言·学习
华研前沿标杆游学1 小时前
企业参访入口 | 走进华为坂田基地,深圳华为坂田基地考察游学参访
python
Chen_harmony1 小时前
一、C++入门基础
开发语言·c++
学逆向的1 小时前
远程线程注入
开发语言·jvm·网络安全·win32
朝朝辞暮i1 小时前
VLA 系统学习第 12 课:为什么机器人不能只看一帧?——时间序列、Observation History 与 Action Chunk
人工智能·python·深度学习·神经网络·vla
朝朝辞暮i2 小时前
VLA 系统学习第 8 课:Optimizer 到底在干什么?——从 Learning Rate 到 SGD、Momentum 和 Adam
人工智能·python·深度学习·神经网络·vla
镜子AI2 小时前
教培机构多模态内容跨模态检索系统:基于CLIP的统一表征与图文混合召回算法
人工智能·python·算法·机器学习
落魄大学生之流水线上谋生计2 小时前
Selenium 入门到实战:用 Python 写出稳定的浏览器自动化
python·selenium·自动化
阡陌数智2 小时前
RAG 系统的召回退化:向量库、分块、重排全链路问题排查与优化实践
开发语言·人工智能·语言模型·自然语言处理·推荐算法