news 2026/6/2 1:09:30

Python 面向对象编程核心:对象、实例化、封装与变量作用域

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python 面向对象编程核心:对象、实例化、封装与变量作用域

目录

1. 对象与类:蓝图与实例

1.1 什么是类?什么是对象?

1.2 Python 中的类定义与对象创建

2. 实例化:从类到对象的过程

3. 面向对象编程(OOP)四大特性概述

4. 封装 —— 数据隐藏与接口

4.1 Python 中的访问控制:约定与名称改编

4.2 使用 @property 实现优雅的访问控制

5. 变量作用域:实例变量、类变量、局部变量与全局变量

5.1 实例变量 vs 类变量

5.2 方法中的局部变量

5.3 self 的作用与 C++ 的 this 指针

5.4 全局变量与函数作用域

6. 综合实战:封装一个简单的银行账户系统

7. 常见陷阱与最佳实践


理解对象与类的本质,掌握封装与作用域,写出更健壮的面向对象代码

面向对象编程(OOP)是一种组织代码的方式,它将数据(属性)和行为(方法)封装在一起,形成“对象”。Python 从设计之初就是一门面向对象的语言,而 C++ 则是混合范式语言。

1. 对象与类:蓝图与实例

1.1 什么是类?什么是对象?

  • 类(Class):是创建对象的蓝图或模板。它定义了对象应该具有的属性(数据)和方法(行为)。

  • 对象(Object):是根据类创建的具体实例。每个对象都有自己的状态(属性值),并可以执行类中定义的行为。

生活类比:类就像一张“汽车设计图纸”,定义了汽车的品牌、颜色、最高时速、启动方法等。对象就是根据这张图纸生产出来的具体汽车,每辆车有自己的颜色、车牌号,但都遵循图纸的规范。

1.2 Python 中的类定义与对象创建

class Car: """汽车类""" # 类属性(所有实例共享) wheels = 4 # 实例初始化方法(构造器) def __init__(self, brand, color): self.brand = brand # 实例属性 self.color = color # 实例方法 def drive(self): return f"{self.color} {self.brand} is driving." # 实例化:创建对象 my_car = Car("Tesla", "red") your_car = Car("BMW", "blue") print(my_car.wheels) # 4(类属性) print(my_car.drive()) # red Tesla is driving. print(your_car.drive()) # blue BMW is driving. Car.wheels = 3 # 修改类属性 print(my_car.wheels) # 3(所有实例受影响) print(Car.wheels) # 3

逐行解析

  • class Car::定义名为Car的类。

  • wheels = 4:类属性,属于类本身,所有实例共享。

  • def __init__(self, brand, color)::初始化方法,当创建Car实例时自动调用。self代表当前实例。

  • self.brand = brand:创建实例属性brand并赋值。

  • self.color = color:创建实例属性color

  • def drive(self)::实例方法,第一个参数必须是self,用于访问实例属性。

  • my_car = Car("Tesla", "red"):实例化,调用__init__,返回实例对象赋值给my_car

  • my_car.wheels:访问类属性,如果实例没有同名属性,则会向上查找类属性。

  • Car.wheels = 3:通过类名修改类属性,所有实例的wheels访问都会改变(除非实例自己覆盖了该属性)。

C++ 联动

  • C++ 中类与对象的概念类似,但语法不同:

class Car { public: static int wheels; // 类属性(静态成员) std::string brand; std::string color; Car(std::string b, std::string c) : brand(b), color(c) {} std::string drive() { return color + " " + brand + " is driving."; } }; int Car::wheels = 4; // 静态成员定义 Car my_car("Tesla", "red");
  • 区别:C++ 中类属性(静态成员)需要在类外单独定义;Python 的类属性直接定义在类体内。C++ 有明确的public/private访问控制,而 Python 主要依赖命名约定。


2. 实例化:从类到对象的过程

实例化就是调用类创建对象的过程。Python 中,类的实例化分为两步:

  1. 调用__new__方法创建空对象(很少重写)。

  2. 调用__init__方法初始化对象。

class Dog: def __init__(self, name): self.name = name # 实例化 d = Dog("Buddy") print(d.name) # Buddy

解析

  • Dog("Buddy")触发了实例化。

  • 实际上__new__返回一个Dog实例(空壳),然后__init__填充属性name

  • 最终对象赋值给d

C++ 联动

  • C++ 中实例化对象有两种方式:栈上Dog d("Buddy");或堆上Dog* d = new Dog("Buddy");。构造函数直接完成内存分配和初始化(堆上需要new)。

  • Python 的所有对象都在堆上分配,由垃圾回收管理,不需要手动释放。


3. 面向对象编程(OOP)四大特性概述

OOP 的四大特性是:封装、继承、多态、抽象。本文重点讲解封装变量作用域(继承与多态将在后续文章中深入)。

  • 封装:将数据(属性)和操作数据的方法捆绑在一起,并隐藏内部实现细节,对外只暴露必要的接口。

  • 继承:子类复用父类的代码。

  • 多态:同一接口,不同实现。

  • 抽象:提取共性,定义接口规范。


4. 封装 —— 数据隐藏与接口

封装有两个层面的含义:

  1. 数据与操作的捆绑:将相关属性和方法放在同一个类中。

  2. 访问控制:限制外部直接访问内部数据,通过公开的方法(getter/setter)来读写。

4.1 Python 中的访问控制:约定与名称改编

Python没有像 C++ 那样的privateprotected关键字,而是采用命名约定:

命名模式含义外部访问
name公有(public)可以直接访问
_name受保护(protected)约定外部不应直接访问
__name私有(private)名称改编,但仍可访问
class BankAccount: def __init__(self, owner, balance): self.owner = owner # 公有 self._password = "1234" # 受保护(约定) self.__balance = balance # 私有(名称改编) def get_balance(self): return self.__balance def deposit(self, amount): if amount > 0: self.__balance += amount acc = BankAccount("Alice", 1000) print(acc.owner) # Alice(直接访问公有) print(acc._password) # 1234(可以访问,但不推荐) # print(acc.__balance) # AttributeError print(acc.get_balance()) # 1000(通过公有方法访问) print(acc._BankAccount__balance) # 1000(名称改编后仍然可以访问,但不应这样做)

逐行解析

  • self.owner = owner:公有属性,任何地方都可直接读写。

  • self._password = "1234":单下划线开头,约定为“受保护”,外部可以访问但应视为内部实现细节,不建议使用。IDE 和工具会提示警告。

  • self.__balance = balance:双下划线开头,Python 会进行名称改编(name mangling),实际属性名变为_BankAccount__balance。这样做的目的是防止子类意外覆盖,但并不能真正阻止外部访问。

  • get_balance():公有方法,提供对私有属性的受控访问。

  • 外部代码可以通过_BankAccount__balance访问,但强烈不建议依赖这种实现细节。

4.2 使用@property实现优雅的访问控制

class Temperature: def __init__(self, celsius): self._celsius = celsius # 内部存储 @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError("Temperature cannot be below absolute zero") self._celsius = value @property def fahrenheit(self): return self._celsius * 9/5 + 32 t = Temperature(25) print(t.celsius) # 25(通过 getter) t.celsius = 30 # 通过 setter 验证 print(t.fahrenheit) # 86.0(只读计算属性) # t.fahrenheit = 100 # AttributeError: can't set attribute

解析

  • @property将方法转为只读属性,访问t.celsius实际调用celsius方法。

  • @celsius.setter允许赋值操作t.celsius = 30时执行自定义逻辑(验证)。

  • fahrenheit只有 getter,没有 setter,因此是只读的计算属性。

C++ 联动

  • C++ 中实现封装通常需要手写 getter/setter 函数,例如:

class Temperature { private: double celsius; public: double getCelsius() const { return celsius; } void setCelsius(double value) { if (value < -273.15) throw std::invalid_argument("..."); celsius = value; } double getFahrenheit() const { return celsius * 9/5 + 32; } };
  • Python 的@property使得 getter/setter 的调用看起来像普通属性,更加简洁自然。C++ 中无法直接实现这种语法糖(除非重载operator.,但 C++ 不允许)。


5. 变量作用域:实例变量、类变量、局部变量与全局变量

5.1 实例变量 vs 类变量

  • 实例变量:属于每个实例,在__init__或实例方法中用self.变量定义,不同实例的值相互独立。

  • 类变量:属于类本身,在类体内直接定义,所有实例共享。

class Employee: company = "TechCorp" # 类变量 def __init__(self, name): self.name = name # 实例变量 e1 = Employee("Alice") e2 = Employee("Bob") print(e1.company, e2.company) # TechCorp TechCorp e1.company = "NewCorp" # 实际上是创建了实例变量 company,隐藏了类变量 print(e1.company, e2.company) # NewCorp TechCorp Employee.company = "GlobalCorp" # 修改类变量 print(e1.company, e2.company) # NewCorp GlobalCorp(e1 有自己的实例变量,不受影响)

解析

  • 访问e1.company时,先查找实例是否有company属性,没有则查找类属性。

  • 赋值e1.company = "NewCorp"会在实例上创建新属性,不再共享类变量。

  • 修改类变量通过Employee.company影响所有未覆盖的实例。

5.2 方法中的局部变量

在实例方法内部定义的变量是局部变量,只在方法执行期间存在。

class Counter: def increment(self, x): temp = x + 1 # 局部变量,方法结束后销毁 return temp

5.3self的作用与 C++ 的this指针

  • self是实例方法的第一个参数,指向当前调用该方法的对象。它是显式传递的。

  • this在 C++ 中是隐式的指针,在成员函数内部可以直接使用。

class Point: def __init__(self, x, y): self.x = x self.y = y def distance(self, other): return ((self.x - other.x)**2 + (self.y - other.y)**2)**0.5

C++ 对应

class Point { public: double x, y; Point(double x, double y) : x(x), y(y) {} double distance(const Point& other) const { return sqrt((this->x - other.x)*(this->x - other.x) + ...); } };

区别

  • Python 的self是显式的,必须作为第一个参数;C++ 的this是隐式的关键字。

  • Python 中的实例属性需要显式通过self访问;C++ 中可以直接访问成员变量(x等价于this->x)。

5.4 全局变量与函数作用域

在类或方法中,可以通过global关键字访问和修改全局变量。

global_count = 0 class Tracker: def increment(self): global global_count global_count += 1 t = Tracker() t.increment() print(global_count) # 1

建议:尽量避免在类中使用全局变量,会破坏封装性。

C++ 联动

  • C++ 中全局变量可以直接访问,没有类似 Python 的global声明,因为 C++ 的作用域规则是编译期静态的。


6. 综合实战:封装一个简单的银行账户系统

class Account: """银行账户类,演示封装和作用域""" _bank_name = "Python Bank" # 类变量(约定受保护) def __init__(self, owner, initial_balance=0): self.owner = owner # 公有 self.__balance = initial_balance # 私有 self._transaction_log = [] # 受保护 @property def balance(self): return self.__balance def deposit(self, amount): if amount <= 0: raise ValueError("Amount must be positive") self.__balance += amount self._log(f"Deposited {amount}") def withdraw(self, amount): if amount <= 0: raise ValueError("Amount must be positive") if amount > self.__balance: raise ValueError("Insufficient funds") self.__balance -= amount self._log(f"Withdrew {amount}") def _log(self, msg): self._transaction_log.append(msg) def get_log(self): return self._transaction_log.copy() @classmethod def get_bank_name(cls): return cls._bank_name @staticmethod def is_valid_owner(name): return len(name) > 1 and name.isalpha() # 使用 acc = Account("Alice", 100) acc.deposit(50) acc.withdraw(30) print(acc.balance) # 120 print(acc.get_log()) # ['Deposited 50', 'Withdrew 30'] print(Account.get_bank_name())# Python Bank print(Account.is_valid_owner("Bob")) # True

解析

  • _bank_name是类变量(约定受保护),通过类方法get_bank_name访问。

  • __balance是私有实例属性,通过@property提供只读访问。

  • _log是受保护方法,仅在内部使用。

  • 类方法get_bank_name通过cls访问类属性。

  • 静态方法is_valid_owner与类或实例无关,仅作为工具函数。


7. 常见陷阱与最佳实践

陷阱说明解决方案
在类体内直接调用实例方法类体是定义阶段,还没有实例,不能调用实例方法__init__或其他实例方法中调用
忘记self参数导致TypeError定义实例方法时必须包含self,调用时自动传递确保每个实例方法的第一个参数是self
错误地使用类变量修改实例属性obj.class_var = value会创建实例属性,而不是修改类变量使用ClassName.class_var = value修改类变量
过度使用global或类变量代替实例变量破坏封装,导致状态混乱优先使用实例变量,必要时提供访问方法
依赖名称改编实现私有,却仍从外部访问__name只是改名,并非真正的私有,不能实现严格封装遵守命名约定,不要访问_ClassName__name
__init__中返回非None的值__init__必须返回None,否则触发TypeError只在__init__中初始化,不要return非空

C++ 中的类似陷阱

  • C++ 中忘记public访问限定符导致所有成员默认私有。

  • 在构造函数初始化列表中遗漏成员,导致未初始化。

  • 静态成员需要在类外定义。

感谢你的观看,期待我们下次再见!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/2 1:08:04

告别复制粘贴!用Postman Tests脚本实现API自动化测试的5个实战场景

告别复制粘贴&#xff01;用Postman Tests脚本实现API自动化测试的5个实战场景在API开发和测试领域&#xff0c;手动验证每个接口的返回结果不仅耗时耗力&#xff0c;还容易出错。Postman作为一款强大的API测试工具&#xff0c;其Tests脚本功能能够将我们从重复劳动中解放出来&…

作者头像 李华
网站建设 2026/6/2 1:03:07

新手避坑指南:用Python模拟SAR信号混叠,5分钟搞懂采样定理

新手避坑指南&#xff1a;用Python模拟SAR信号混叠&#xff0c;5分钟搞懂采样定理第一次接触雷达信号处理时&#xff0c;看到"奈奎斯特频率"、"频谱混叠"这些术语总让人头疼。教科书上的公式推导虽然严谨&#xff0c;但缺乏直观感受。直到我在实验室用Pyth…

作者头像 李华
网站建设 2026/6/2 0:56:47

《流畅的Python》读书笔记19(补充01): 使用 yield from - 再谈PE380

PEP 380: Syntax for Delegating to a Subgenerator 是 Python 发展史上一个里程碑式的提案&#xff0c;它在 Python 3.3 中正式引入。 核心语法是&#xff1a;yield from <iterable> 以下从背景痛点、架构原理、使用方法和注意事项四个维度详细解析。1. 为什么需要引入&…

作者头像 李华
网站建设 2026/6/2 0:56:18

保姆级攻略:用Python和MATLAB搞定2024深圳杯数学建模C题(编译器识别)

从二进制到智能分类&#xff1a;编译器版本识别的全流程实战解析当你面对一堆由不同版本GCC编译器生成的二进制文件时&#xff0c;是否曾好奇这些看似相同的机器码背后隐藏着怎样的版本指纹&#xff1f;在2024年数学建模竞赛的实战场景中&#xff0c;我们将揭开编译器识别的神秘…

作者头像 李华