【Python】面向对象(一)
目录
- 面向对象
- 面向对象概念
- 类和对象
- 类属性
- 类方法
- 静态方法
- 构造函数
- 访问修饰符
面向对象
面向对象概念
什么是 OOP?
OOP(Object-Oriented Programming,面向对象编程)是一种编程范式,它用 对象(Object) 来模拟现实世界的实体。
- 对象(Object) = 属性(数据) + 方法(行为)
- 举例:
- 学生(Student):属性 = 姓名、学号、成绩;方法 = 学习、考试、计算平均分
- 汽车(Car):属性 = 品牌、马力、速度;方法 = 启动、加速、刹车
所以,对象是数据和行为的结合体。
过程式 vs 面向对象
-
过程式编程(Procedural Programming)
-
重点是 函数 和 流程控制,按步骤执行。
-
问题:
- 太多全局变量 → 维护困难
- 数据和函数分离 → 不符合现实世界的建模习惯
-
-
面向对象编程(OOP)
-
把 数据 和 函数(方法) 封装进一个整体(类和对象)。
-
优点:
- 更符合现实思维方式
- 易扩展、易维护
- 提供了继承、多态、封装等机制
-
OOP 的四大基本特性:
- 类(Class)和对象(Object)
- 封装(Encapsulation)
- 继承(Inheritance)
- 多态(Polymorphism)
类(Class)与对象(Object)
类(Class)
- 类是对象的 蓝图 或 模板。
- 类里定义了对象 有什么属性 和 能做什么事。
对象(Object)
- 对象是类的 实例(instance)。
- 一个类可以创建多个对象。
代码示例:
# 定义类
class Smartphone:def __init__(self, device, brand): # 构造方法self.device = deviceself.brand = branddef description(self):return f"{self.device} of {self.brand} supports Android 14"# 创建对象
phoneObj = Smartphone("Smartphone", "Samsung")
print(phoneObj.description())
输出:
Smartphone of Samsung supports Android 14
封装(Encapsulation)
- 封装就是 隐藏对象的内部实现,只暴露必要的接口给外部使用。
- 在 Python 里:
- 普通属性 → 可以直接访问
- 私有属性(前缀
__
)→ 不能直接访问,只能通过方法(getter/setter)来修改
代码示例:
class Desktop:def __init__(self):self.__max_price = 25000 # 私有属性def sell(self):return f"Selling Price: {self.__max_price}"def set_max_price(self, price):if price > self.__max_price:self.__max_price = pricedesktopObj = Desktop()
print(desktopObj.sell())# 直接改属性(失败)
desktopObj.__max_price = 35000
print(desktopObj.sell())# 用 setter 改属性(成功)
desktopObj.set_max_price(35000)
print(desktopObj.sell())
输出:
Selling Price: 25000
Selling Price: 25000
Selling Price: 35000
继承(Inheritance)
- 子类继承父类,可以复用父类的属性和方法。
- 子类还可以:
- 新增自己的方法
- 重写(override)父类的方法
代码示例:
class Parent:parentAttr = 100def __init__(self):print("Calling parent constructor")def parentMethod(self):print("Calling parent method")def setAttr(self, attr):Parent.parentAttr = attrdef getAttr(self):print("Parent attribute:", Parent.parentAttr)class Child(Parent):def __init__(self):print("Calling child constructor")def childMethod(self):print("Calling child method")# 创建子类对象
c = Child()
c.childMethod()
c.parentMethod()
c.setAttr(200)
c.getAttr()
输出:
Calling child constructor
Calling child method
Calling parent method
Parent attribute: 200
Python 还支持 多重继承,可用 issubclass()
、isinstance()
检查关系。
多态(Polymorphism)
- 多态 = 多种形态
- 不同的类可以实现相同的方法接口,但表现不同。
- 方法重写(Override) 是多态的核心。
代码示例:
class Parent:def myMethod(self):print("Calling parent method")class Child(Parent):def myMethod(self):print("Calling child method")c = Child()
c.myMethod() # 子类重写父类方法
输出:
Calling child method
类和对象
类与对象的基本概念
- 类(Class)
类是用户自定义的数据类型,定义了对象的属性(数据)**和**行为(方法)。它就像一个模板或蓝图。
例如:我们可以写一个Smartphone
类,属性包括RAM
、ROM
、屏幕大小
,行为包括打电话
、发短信
。 - 对象(Object)
对象是类的一个实例化结果。类只描述结构,不占内存;当创建对象时,才会为对象分配内存。
举例:类是“人类”,对象是“张三”“李四”。 - Python 是面向对象语言:整数、字符串、列表、字典,甚至函数,本质上都是对象,属于内置类的实例。
类的定义
在 Python 中使用 class
关键字:
class ClassName:"可选的类文档字符串"# 类体:定义属性、方法
例如:
class Employee:"Common base class for all employees"empCount = 0 # 类变量def __init__(self, name, salary): # 构造方法self.name = name # 实例变量self.salary = salaryEmployee.empCount += 1def displayCount(self): # 方法print("Total Employee %d" % Employee.empCount)def displayEmployee(self):print("Name:", self.name, ", Salary:", self.salary)
说明:
__init__
是构造函数,创建对象时自动执行。self
表示对象本身(类似 Java 的this
)。- 类变量
empCount
在所有实例之间共享。 - 实例变量
self.name
、self.salary
属于各自对象。
对象的创建与使用
对象的创建就是 调用类名,传入构造函数参数:
emp1 = Employee("Zara", 2000) # 第一个对象
emp2 = Employee("Manni", 5000) # 第二个对象emp1.displayEmployee()
emp2.displayEmployee()
print("Total Employee %d" % Employee.empCount)
输出:
Name: Zara , Salary: 2000
Name: Manni , Salary: 5000
Total Employee 2
类变量和实例变量
- 类变量:所有对象共享(如
Employee.empCount
)。 - 实例变量:每个对象独立(如
self.name
)。
我们也可以动态地添加/修改/删除属性:
emp1.age = 7 # 添加属性
emp1.age = 8 # 修改属性
del emp1.age # 删除属性
对象属性的操作函数
除了点运算符,还可以用内置函数:
hasattr(emp1, 'age') # 判断是否存在属性
getattr(emp1, 'name') # 获取属性值
setattr(emp1, 'age', 10) # 设置属性值(如果没有则创建)
delattr(emp1, 'age') # 删除属性
Python 内置类属性
每个类都有一些内置属性,可以用 __dict__
查看:
print("Employee.__doc__:", Employee.__doc__) # 文档字符串
print("Employee.__name__:", Employee.__name__) # 类名
print("Employee.__module__:", Employee.__module__) # 定义所在模块
print("Employee.__bases__:", Employee.__bases__) # 基类
print("Employee.__dict__:", Employee.__dict__) # 类的命名空间字典
输出类似:
Employee.__doc__: Common base class for all employees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: (<class 'object'>,)
Employee.__dict__: {'__module__': '__main__', '__doc__': 'Common base class for all employees', 'empCount': 0, '__init__': <function Employee.__init__ at 0x7fdaa2e09300>, 'displayCount': <function Employee.displayCount at 0x7fdaa2e6ce00>, 'displayEmployee': <function Employee.displayEmployee at 0x7fdaa2e6cea0>, '__dict__': <attribute '__dict__' of 'Employee' objects>, '__weakref__': <attribute '__weakref__' of 'Employee' objects>}
内置数据类型其实都是类对象
Python 内置的数字、字符串、列表、字典,本质上也是类的对象:
num = 20
print(type(num)) # <class 'int'>
s = "Hello"
print(type(s)) # <class 'str'>
dct = {'a':1}
print(type(dct)) # <class 'dict'>def SayHello(): pass
print(type(SayHello)) # <class 'function'>
垃圾回收与对象销毁
Python 使用引用计数来管理内存:
- 当变量引用对象时,引用计数+1;
- 删除引用时,计数-1;
- 当计数为 0,对象会被垃圾回收。
示例:
a = 40 # 对象 <40> 引用+1
b = a # 引用+1
c = [b] # 引用+1
del a # 引用-1
b = 100 # 引用-1
c[0] = -1 # 引用-1,引用计数为0,对象销毁
我们也可以定义 析构方法 __del__
:
class Employee:"Common base class for all employees"empCount = 0 # 类变量def __init__(self, name, salary): # 构造方法self.name = name # 实例变量self.salary = salaryEmployee.empCount += 1print("Point created")def __del__(self):Employee.empCount -= 1print("Point destroyed")def displayCount(self): # 方法print("Total Employee %d" % Employee.empCount)def displayEmployee(self):print("Name:", self.name, ", Salary:", self.salary)emp1 = Employee("Zara", 2000) # 第一个对象
emp2 = Employee("Manni", 5000) # 第二个对象emp1.displayEmployee()
emp2.displayEmployee()
print("Total Employee %d" % Employee.empCount)del emp1
del emp2
print("Total Employee %d" % Employee.empCount)
输出:
Point created
Point created
Name: Zara , Salary: 2000
Name: Manni , Salary: 5000
Total Employee 2
Point destroyed
Point destroyed
Total Employee 0
数据隐藏(封装)
Python 并没有真正的私有属性,但可以通过 双下划线前缀 来“伪私有”:
class JustCounter:__secretCount = 0 # 私有变量def count(self):self.__secretCount += 1print(self.__secretCount)counter = JustCounter()
counter.count()
counter.count()print(counter.__secretCount) # 报错
输出:
1
2
AttributeError: 'JustCounter' object has no attribute '__secretCount'
但其实仍然可以通过以下方式访问(名字改写):
print(counter._JustCounter__secretCount) # 2
类属性
在 Python 中,类属性就是在类里面(class
代码块中),但不在构造方法 __init__()
中定义的变量。
- 类属性 → 属于整个类,所有对象(实例)共享同一份数据。
- 实例属性 → 属于对象,每个对象都有独立的数据。
类属性一般用于表示 所有对象都应共享的值,例如:员工总数、学校名称、公司名称等。
定义类属性与实例属性
class Employee:# 类属性(所有对象共享)company = "OpenAI" def __init__(self, name, age):# 实例属性(每个对象独有)self.name = nameself.age = age
访问类属性
类属性既可以通过 类名 访问,也可以通过 对象 访问:
emp1 = Employee("Alice", 25)
emp2 = Employee("Bob", 30)# 通过类名访问
print(Employee.company) # OpenAI# 通过对象访问
print(emp1.company) # OpenAI
print(emp2.company) # OpenAI
输出:
OpenAI
OpenAI
OpenAI
无论多少个对象,类属性的值相同。
修改类属性
修改类属性时,必须用类名修改,否则就会变成实例属性。
class Employee:empCount = 0 # 类属性def __init__(self, name, age):self.name = nameself.age = ageEmployee.empCount += 1 # 修改类属性print("Name:", self.name, ", Age:", self.age)print("Employee Count:", Employee.empCount)# 创建对象时,empCount 自动 +1
e1 = Employee("Bhavana", 24)
print()
e2 = Employee("Rajesh", 26)
输出:
Name: Bhavana , Age: 24
Employee Count: 1Name: Rajesh , Age: 26
Employee Count: 2
这里 empCount
是所有对象共享的,如果新建一个对象,计数会继续递增。
类属性的意义
- 表示所有对象共享的数据
比如“公司名称”“学校名称”“员工总数”等。 - 设置默认值
如果很多对象需要一个相同的初始值,可以放在类属性里。 - 实现单例模式(Singleton)
利用类属性来控制对象的唯一性(比如只允许创建一个对象)。
Python 内置类属性
Python 中每个类都有一些内置的类属性,可以用来获取类的元信息:
__dict__
:类的命名空间(属性字典)__doc__
:类的文档字符串__name__
:类名__module__
:类所在的模块__bases__
:基类(父类)
示例:
class Employee:"""员工类"""def __init__(self, name="Bhavana", age=24):self.name = nameself.age = agedef displayEmployee(self):print("Name:", self.name, ", Age:", self.age)print("Employee.__doc__:", Employee.__doc__)
print("Employee.__name__:", Employee.__name__)
print("Employee.__module__:", Employee.__module__)
print("Employee.__bases__:", Employee.__bases__)
print("Employee.__dict__:", Employee.__dict__)
输出:
Employee.__doc__: 员工类
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: (<class 'object'>,)
Employee.__dict__: {'__module__': '__main__', '__doc__': '员工类', '__init__': <function Employee.__init__ at 0x7fd054119300>, 'displayEmployee': <function Employee.displayEmployee at 0x7fd05417ce00>, '__dict__': <attribute '__dict__' of 'Employee' objects>, '__weakref__': <attribute '__weakref__' of 'Employee' objects>}
实例属性
与类属性不同,实例属性是定义在 __init__
中的,每个对象都有自己独立的一份。
class Student:def __init__(self, name, grade):self.name = name # 实例属性self.grade = grade # 实例属性print("Name:", self.name, ", Grade:", self.grade)student1 = Student("Ram", "B")
student2 = Student("Shyam", "C")
输出:
Name: Ram , Grade: B
Name: Shyam , Grade: C
student1
和 student2
的 name
、grade
各不相同,互不影响。
类属性 vs 实例属性
实例属性 | 类属性 |
---|---|
定义在 __init__() 中 | 定义在类中但不在方法里 |
通过对象访问 | 可通过类名或对象访问 |
每个对象独立,互不共享 | 所有对象共享一份 |
修改只影响当前对象 | 修改会影响所有对象 |
高级用法
-
动态添加类属性
class Car:passCar.brand = "Toyota" # 运行时动态添加 print(Car.brand)
-
动态修改/删除类属性
Car.brand = "Tesla" print(Car.brand) # Tesladel Car.brand print(hasattr(Car, "brand")) # False
-
类属性和实例属性“遮蔽”现象
class Demo:x = 10 # 类属性d = Demo() print(d.x) # 10 (访问类属性)d.x = 20 # 创建实例属性,遮蔽类属性 print(d.x) # 20 print(Demo.x) # 10 (类属性未变)
如果实例对象创建了同名属性,会“屏蔽”类属性。
类方法
什么是方法(Method)
在 Python 的类中,方法就是定义在类里面的函数,用来对对象或类执行某些操作。
方法分为三类:
- 实例方法(Instance Method)
- 默认的普通方法,参数第一个是
self
(对象本身),可以访问和修改 实例属性,也能访问类属性。
- 默认的普通方法,参数第一个是
- 类方法(Class Method)
- 参数第一个是
cls
(类本身),绑定在类上,不能访问实例属性,但可以访问和修改类属性。
- 参数第一个是
- 静态方法(Static Method)
- 没有
self
或cls
,与类只是“放在一起”,不能访问实例或类的状态,逻辑上属于类。
- 没有
什么是 Class Method(类方法)
- 类方法是绑定在类上的方法,而不是绑定在实例上的方法。
- 调用时可以直接通过 类名 调用,而不必先创建对象。
- 参数第一个是
cls
,代表类本身,可以用来访问和修改 类属性。
类方法常用于:
- 访问/修改类属性
- 定义“备用构造函数”(Alternate Constructor)
- 工厂模式、单例模式等
创建类方法的两种方式
方式一:用 classmethod()
函数
class Employee:empCount = 0 # 类属性def __init__(self, name, age):self.name = nameself.age = ageEmployee.empCount += 1def showcount(self): # 实例方法print(self.empCount)# 把实例方法转成类方法counter = classmethod(showcount)e1 = Employee("Bhavana", 24)
e2 = Employee("Rajesh", 26)
e3 = Employee("John", 27)e1.showcount() # 用对象调用
Employee.counter() # 用类调用
输出:
3
3
方式二:用 @classmethod
装饰器(推荐)
class Employee:empCount = 0 # 类属性def __init__(self, name, age):self.name = nameself.age = ageEmployee.empCount += 1@classmethoddef showcount(cls):print(cls.empCount)@classmethoddef newemployee(cls, name, age): # 备用构造函数return cls(name, age)e1 = Employee("Bhavana", 24)
e2 = Employee("Rajesh", 26)
e3 = Employee("John", 27)
e4 = Employee.newemployee("Anil", 21) # 通过类方法创建对象Employee.showcount()
输出:
4
推荐使用 装饰器写法,比 classmethod()
简洁直观。
在类方法中访问类属性
类方法可以通过 cls.属性名
访问或修改类属性:
class Cloth:price = 4000 # 类属性@classmethoddef showPrice(cls):return cls.priceprint(Cloth.showPrice())
输出:
4000
动态操作类方法
Python 允许我们 动态添加或删除 类方法。
-
动态添加类方法
class Cloth:pass@classmethod def brandName(cls):print("Name of the brand is Raymond")# 动态添加类方法 setattr(Cloth, "brand_name", brandName)c = Cloth() c.brand_name()
输出:
Name of the brand is Raymond
-
动态删除类方法
class Cloth:@classmethoddef brandName(cls):print("Name of the brand is Raymond")# 删除类方法 del Cloth.brandName print("Method deleted")
输出:
Method deleted
删除后,如果再调用
Cloth.brandName()
会报错AttributeError
。
类方法 vs 实例方法 vs 静态方法
特性 | 实例方法 | 类方法 | 静态方法 |
---|---|---|---|
绑定对象 | 实例 | 类 | 无绑定 |
第一个参数 | self (实例对象) | cls (类对象) | 无 |
可访问 | 实例属性、类属性 | 类属性(不能直接访问实例属性) | 都不能 |
调用方式 | obj.method() | Class.method() 或 obj.method() | Class.method() 或 obj.method() |
常见用途 | 操作对象数据 | 操作类数据、备用构造函数 | 工具函数 |
高级用法
-
备用构造函数
class Date:def __init__(self, year, month, day):self.year = yearself.month = monthself.day = day@classmethoddef from_string(cls, date_str):y, m, d = map(int, date_str.split("-"))return cls(y, m, d)d = Date.from_string("2025-09-21") print(d.year, d.month, d.day)
输出:
2025 9 21
类方法能创建对象的“另一种构造方式”。
-
工厂模式
class Shape:def __init__(self, name):self.name = name@classmethoddef circle(cls):return cls("Circle")@classmethoddef square(cls):return cls("Square")s1 = Shape.circle() s2 = Shape.square() print(s1.name, s2.name)
输出:
Circle Square
静态方法
静态方法的定义(是什么?)
在 Python 中,静态方法(static method) 是一种属于类的方法,但它 既不需要实例(self),也不需要类引用(cls) 作为第一个参数。
- 实例方法:默认第一个参数是
self
,能操作对象的实例属性。 - 类方法:默认第一个参数是
cls
,能操作类属性。 - 静态方法:没有
self
、也没有cls
,就是一个“普通函数”,但被放在类里面,为了逻辑上的归类和组织。
直白理解:
- 静态方法和普通函数几乎没区别,只是 放在类里面,便于和类产生关联。
- 静态方法更像是“工具方法”,常用于一些通用逻辑,不依赖于对象或类的状态。
静态方法的创建方式
Python 提供两种方式:
-
使用
staticmethod()
内置函数这是最原始的写法,把一个普通函数“转化”为静态方法。
class Employee:empCount = 0def __init__(self, name, age):self.__name = nameself.__age = ageEmployee.empCount += 1# 定义普通方法def showcount():print(Employee.empCount)# 把方法转化为静态方法counter = staticmethod(showcount)# 创建对象 e1 = Employee("Bhavana", 24) e2 = Employee("Rajesh", 26) e3 = Employee("John", 27)e1.counter() # 对象调用 Employee.counter() # 类调用
输出:
3 3
-
使用
@staticmethod
装饰器(推荐方式)这是更简洁的写法,也是实际开发中的 标准写法。
class Student:stdCount = 0def __init__(self, name, age):self.__name = nameself.__age = ageStudent.stdCount += 1@staticmethoddef showcount():print(Student.stdCount)# 创建对象 s1 = Student("Bhavana", 24) s2 = Student("Rajesh", 26) s3 = Student("John", 27)print("Number of Students:") Student.showcount()
输出:
Number of Students: 3
说明:
showcount
里面既没有self
也没有cls
。- 它只能直接通过 类名访问类变量(例如
Student.stdCount
)。
静态方法和类方法的区别
特点 | 实例方法 | 类方法 | 静态方法 |
---|---|---|---|
第一个参数 | self (对象本身) | cls (类本身) | 无 |
是否能访问实例属性 | ✅ | ❌ | ❌ |
是否能访问类属性 | ✅ | ✅ | ❌(只能硬编码访问类名) |
绑定对象 | 实例 | 类 | 无绑定 |
调用方式 | 实例调用 | 类/实例调用 | 类/实例调用 |
常见用途 | 操作实例属性 | 工厂方法、操作类属性 | 工具函数 |
总结一句话:
- 实例方法:和对象打交道
- 类方法:和类打交道
- 静态方法:和类没啥关系,只是放在类里“逻辑归类”
静态方法的使用场景
静态方法虽然看起来“鸡肋”,但在实际开发里有几个经典用法:
-
工具函数(通用逻辑)
比如写个判断逻辑,放在类里面,和类语义相关,但不需要访问类的属性。
class MathUtils:@staticmethoddef add(a, b):return a + b@staticmethoddef is_even(num):return num % 2 == 0print(MathUtils.add(5, 3)) # 8 print(MathUtils.is_even(10)) # True
-
数据验证
class Validator:@staticmethoddef is_valid_email(email):return "@" in email and "." in emailprint(Validator.is_valid_email("abc@example.com")) # True print(Validator.is_valid_email("not_email")) # False
-
保证方法不依赖类状态
有些方法你就是不希望它访问或修改类属性,这时候用静态方法能“强制隔离”。
class Config:setting = "DEBUG"@staticmethoddef greet():print("Hello, world!")Config.greet() # 不依赖 setting
静态方法的优点
- 逻辑组织性:和类语义相关的方法放在类里,而不是全局函数。
- 更易维护:不用在全局找函数,类名就能找到对应工具。
- 调用灵活:类调用、对象调用都行。
- 行为可预测:不会受对象或类属性影响,输出固定。
- 防止误用:用静态方法明确告诉使用者:别想在这里改类或对象状态。
动态添加静态方法
class Tool:passdef say_hello():print("Hello!")# 动态设置为静态方法
Tool.hello = staticmethod(say_hello)Tool.hello()
输出:
Hello!
静态方法 vs 普通函数
其实静态方法和普通函数几乎一样,区别是:
- 普通函数在全局命名空间;
- 静态方法在类命名空间,调用时需要类名。
所以更多是 代码组织层面的意义。
构造函数
构造函数是什么?
在 Python 中,构造函数(Constructor) 是类里的一个特殊方法:
- 它的名字固定为
__init__()
; - 当你创建类的对象时,会自动被调用;
- 作用:用来 初始化对象的属性,确保对象一创建出来就有合适的初始值。
构造函数其实就是对象的“出生证明”。
语法与基本用法
class ClassName:def __init__(self, parameters):# 初始化对象属性self.attr = parameters
- 第一个参数通常是
self
,代表实例对象本身(你也可以改名字,但强烈推荐用self
)。 - 其他参数可以是你希望在创建对象时传入的初始化值。
构造函数的类型
-
默认构造函数(Default Constructor)
没有额外参数,只接受
self
。class Employee:def __init__(self):self.name = "Bhavana"self.age = 24e1 = Employee() print("Name:", e1.name) print("Age:", e1.age)
输出:
Name: Bhavana Age: 24
特点:
- 每个对象的初始值都相同。
-
参数化构造函数(Parameterized Constructor)
构造函数接受多个参数,创建对象时传入不同的值。
class Employee:def __init__(self, name, age):self.name = nameself.age = agee1 = Employee("Bhavana", 24) e2 = Employee("Bharat", 25)print("Name:", e1.name, "Age:", e1.age) print("Name:", e2.name, "Age:", e2.age)
输出:
Name: Bhavana Age: 24 Name: Bharat Age: 25
-
带默认参数的构造函数
Python 支持给构造函数参数设置 默认值,这样可以在传参或不传参时都能创建对象。
class Employee:def __init__(self, name="Bhavana", age=24):self.name = nameself.age = agee1 = Employee() # 用默认值 e2 = Employee("Bharat", 25) # 用传入值print("Name:", e1.name, "Age:", e1.age) print("Name:", e2.name, "Age:", e2.age)
输出:
Name: Bhavana Age: 24 Name: Bharat Age: 25
构造函数 + 实例方法
构造函数通常配合 实例方法(Instance Methods) 使用。
实例方法的第一个参数是 self
,它能访问对象的属性。
class Employee:def __init__(self, name="Bhavana", age=24):self.name = nameself.age = agedef displayEmployee(self):print("Name:", self.name, ", Age:", self.age)e1 = Employee()
e2 = Employee("Bharat", 25)e1.displayEmployee()
e2.displayEmployee()
输出:
Name: Bhavana , Age: 24
Name: Bharat , Age: 25
对象属性的动态操作
Python 是动态语言,类和对象的属性可以随时 增加 / 修改 / 删除。
e1 = Employee("Bhavana", 24)# 增加属性
e1.salary = 7000
print(e1.salary) # 7000# 修改属性
e1.name = "XYZ"
print(e1.name) # XYZ# 删除属性
del e1.salary
还可以用内置函数操作:
getattr(obj, name[, default])
→ 获取属性hasattr(obj, name)
→ 检查属性是否存在setattr(obj, name, value)
→ 设置属性(不存在就创建)delattr(obj, name)
→ 删除属性
print(hasattr(e1, "salary")) # False
print(getattr(e1, "name")) # Bhavana
setattr(e1, "salary", 8000) # 动态增加
print(e1.salary) # 8000
delattr(e1, "age") # 删除 age
Python 中的多构造函数问题
在 Java / C++ 里,可以重载多个构造函数,但 Python 不支持 多个 __init__
并存:
- 如果你写了多个
__init__
,只有最后一个会生效,前面的会被覆盖。
Python 可以用 可变参数(*args
, **kwargs
)来模拟多个构造函数。
class Student:def __init__(self, *args):if len(args) == 1:self.name = args[0]elif len(args) == 2:self.name = args[0]self.age = args[1]elif len(args) == 3:self.name = args[0]self.age = args[1]self.gender = args[2]st1 = Student("Shrey")
print("Name:", st1.name)st2 = Student("Ram", 25)
print("Name:", st2.name, "Age:", st2.age)st3 = Student("Shyam", 26, "M")
print("Name:", st3.name, "Age:", st3.age, "Gender:", st3.gender)
输出:
Name: Shrey
Name: Ram Age: 25
Name: Shyam Age: 26 Gender: M
这样就实现了类似 构造函数重载 的效果。
访问修饰符
什么是访问修饰符?
在面向对象编程中,访问修饰符(Access Modifiers) 是用来限制类成员(变量、方法)的可访问范围的。
它体现了 封装(Encapsulation) 的思想:
- 对象的内部实现细节不应该被随意访问或修改
- 外部只能通过公开接口(方法)与对象交互
在 C++/Java 中,有明确的 public
、protected
、private
关键字。
但是在 Python 中,没有强制性的访问控制关键字,而是通过 命名约定 + 解释器机制 来实现。
Python中的三种访问控制
Python 提供了 三种访问修饰符(通过命名约定实现):
类型 | 语法表示 | 含义 | 可访问范围 |
---|---|---|---|
Public | 普通名称(如 name ) | 默认情况,任何地方都能访问 | 类内、子类、类外 |
Protected | 单下划线 _var | 一般约定为“受保护”,不要在类外直接使用 | 类内、子类 |
Private | 双下划线 __var | 私有,不能直接在类外访问,会触发 Name Mangling | 类内 |
Python的语法规则(命名约定)
-
Public(公开)
self.name = "公开"
没有下划线前缀 → 任何地方都能访问。
-
Protected(受保护)
self._salary = 10000
单下划线
_
→ 只是一种约定,表示“内部使用”,类外访问虽然能用,但不建议。 -
Private(私有)
self.__age = 24
双下划线
__
→ Python会进行 名字改编(Name Mangling),变成_ClassName__变量名
,因此类外不能直接访问。
示例:Public / Protected / Private
class Employee:def __init__(self, name, age, salary):self.name = name # publicself._salary = salary # protectedself.__age = age # privatedef display(self):print(f"姓名: {self.name}, 年龄: {self.__age}, 薪资: {self._salary}")e = Employee("张三", 25, 10000)# 公开成员:类外可直接访问
print(e.name) # ✅ 正常访问# 受保护成员:能访问,但不建议
print(e._salary) # ⚠️ 虽然能用,但按约定不要在类外用# 私有成员:直接访问会报错
print(e.__age) # ❌ AttributeError
输出:
张三
10000Warnings/Errors:
Traceback (most recent call last):File "/home/cg/root/523f7e26/main.py", line 20, in <module>print(e.__age) # ❌ AttributeError^^^^^^^
AttributeError: 'Employee' object has no attribute '__age'
Name Mangling(名字改编)
虽然 __age
是私有的,但 Python只是改名了,仍然可以通过 _类名__变量名
访问:
print(e._Employee__age) # ✅ 访问到 25
这说明 Python 并不是“强制私有”,只是依靠开发者自觉遵守约定。
property() 实现封装
由于私有属性不能直接访问,我们通常用 property() 提供统一接口:
class Employee:def __init__(self, name, age):self.__name = nameself.__age = age# getter 方法def get_name(self):return self.__namedef get_age(self):return self.__age# setter 方法def set_name(self, name):self.__name = namedef set_age(self, age):self.__age = age# 定义属性name = property(get_name, set_name, doc="员工姓名")age = property(get_age, set_age, doc="员工年龄")e = Employee("张三", 25)print(e.name) # ✅ 自动调用 get_name()
e.name = "李四" # ✅ 自动调用 set_name()
print(e.name)
输出:
张三
李四
使用 @property 装饰器(更优雅)
现代写法一般用 @property
和 @属性.setter
:
class Employee:def __init__(self, name, age):self.__name = nameself.__age = age@propertydef name(self):return self.__name@name.setterdef name(self, value):self.__name = value@propertydef age(self):return self.__age@age.setterdef age(self, value):if value < 18:raise ValueError("年龄不能小于18")self.__age = valuee = Employee("张三", 25)
print(e.name, e.age)e.age = 30
print(e.age)# e.age = 10 # ❌ ValueError: 年龄不能小于18
优点:
- 访问方式像属性(
e.age
) - 内部依然受控(加校验逻辑)