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 中类的实例化分为两步调用__new__方法创建空对象很少重写。调用__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. 封装 —— 数据隐藏与接口封装有两个层面的含义数据与操作的捆绑将相关属性和方法放在同一个类中。访问控制限制外部直接访问内部数据通过公开的方法getter/setter来读写。4.1 Python 中的访问控制约定与名称改编Python没有像 C 那样的private、protected关键字而是采用命名约定命名模式含义外部访问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: cant 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 GlobalCorpe1 有自己的实例变量不受影响解析访问e1.company时先查找实例是否有company属性没有则查找类属性。赋值e1.company NewCorp会在实例上创建新属性不再共享类变量。修改类变量通过Employee.company影响所有未覆盖的实例。5.2 方法中的局部变量在实例方法内部定义的变量是局部变量只在方法执行期间存在。class Counter: def increment(self, x): temp x 1 # 局部变量方法结束后销毁 return temp5.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.5C 对应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_balance0): 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(fDeposited {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(fWithdrew {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访问限定符导致所有成员默认私有。在构造函数初始化列表中遗漏成员导致未初始化。静态成员需要在类外定义。感谢你的观看期待我们下次再见