一、opp的结构
对于一个来说,他有的结构是:
属性:类属性和实习属性
方法:实例方法、类方法、静态方法、特殊方法
class man:
# 类属性
# 类属性是所有实例共享的属性,定义在类中,方法外
# 类属性可以通过类名或实例名访问
# 类属性可以在类中直接访问,也可以在方法中通过self访问
# 类属性可以在类中直接修改,也可以在方法中通过self修改
# 类属性可以在类中直接删除,也可以在方法中通过self删除
# 类属性可以在类中直接查看,也可以在方法中通过self查看
# 类属性可以在类中直接赋值,也可以在方法中通过self赋值
# 类属性可以在类中直接调用,也可以在方法中通过self调用
# 类属性可以在类中直接定义,也可以在方法中通过self定义
# 类属性可以在类中直接初始化,也可以在方法中通过self初始化
# 类属性可以在类中直接访问,也可以在方法中通过self访问
# 类属性可以在类中直接修改,也可以在方法中通过self修改
sex="男"
id=0
#创建类对象时,自动调用__init__方法(需要上传name,age值)(实例属性)
def __init__(self,name,age):
self.name=name
self.age=age
#实例方法
# 实例方法是定义在类中的方法,第一个参数必须为 self
# 实例方法可以通过实例名调用,也可以通过类名调用
# 实例方法可以访问类属性,也可以访问实例属性
# 实例方法可以修改类属性,也可以修改实例属性
def change_name(self,a,b):
self.name=a+b
return self.name
# 特殊方法
# 特殊的方法,打印后会输出它(魔术方法)
def __str__(self):
return f"姓名:{self.name},年龄:{self.age},性别:{self.sex},ID:{self.id}"
#类方法
# 类方法是定义在类中的方法,第一个参数必须为 cls
# 类方法可以通过类名调用,也可以通过实例名调用
# 类方法可以访问类属性,也可以访问实例属性
# 类方法可以修改类属性,也可以修改实例属性
@classmethod
#classmethod
def change_sex(cls):
return cls.sex
# 类方法可以修改类属性,也可以修改实例属性
@classmethod
def change_id(cls,new_id):
cls.id=new_id
return cls.id
#静态方法
# 静态方法是定义在类中的方法,第一个参数必须为 cls
# 静态方法可以通过类名调用,也可以通过实例名调用
# 静态方法不能访问类属性,也不能访问实例属性
# 静态方法不能修改类属性,也不能修改实例属性
@staticmethod
def change_name(a,b):
return a+b
#私有方法
# 私有方法是定义在类中的方法,方法名以双下划线开头
# 私有方法只能在类中直接调用,不能在类外直接调用
# 私有方法可以访问类属性,也可以访问实例属性
# 私有方法可以修改类属性,也可以修改实例属性
def __change_name(self,a,b):
return a+b
zs=man("张三",18)
print(zs)
#输出姓名:张三,年龄:18,性别:男,ID:0
#这里起作用的是__str__方法
二、公私接口
1.概念总览
| 概念 | 定义方式 | 作用 |
| 公共接口 | 无下划线开头的方法/属性 | 对外提供的功能接口 |
| 私有接口 | 双下划线 __ 开头的方法/属性 | 类内部使用,不对外暴露 |
2.定义与命名约定
Python通过 命名约定 区分接口的访问权限:
| 命名形式 | 类型 | 访问权限 | 示例 |
| name | 公共属性/方法 | 类内外均可直接访问 | person.name |
| _name | 受保护属性/方法 | 建议仅类内部使用(可外部访问) | person._name (不推荐) |
| __name | 私有属性/方法 | 仅类内部可直接访问 | person.__name (外部报错) |
| __name__ | 特殊方法/属性 | Python内置,类内外均可访问 | person.__str__() |
3.私有接口的实现原理
Python对双下划线开头的名称进行 名称修饰 (name mangling):
– 实际存储为: _类名__属性名
– 目的:避免子类与父类的私有属性命名冲突
class Person:
def __init__(self):
self.__age = 25 # 私有属性
# 内部实际存储为:_Person__age
p = Person()
# print(p.__age) # AttributeError
print(p._Person__age) # 输出:25(不推荐这样访问)
三、Getter方法的两种实现方式
1. 传统Getter方法(手动定义)
手动定义获取属性的方法,通常以 get_ 开头:
class Person:
def __init__(self, name, age):
self.__name = name # 私有属性
self.__age = age # 私有属性
# 公共接口:传统getter方法
def get_name(self):
"""获取姓名"""
return self.__name
def get_age(self):
"""获取年龄"""
return self.__age
# 公共接口:传统setter方法
def set_age(self, age):
"""设置年龄(带验证)"""
if age < 0 or age > 150:
#异常处理,如果年龄小于0大于150返回下放错误提示
raise ValueError("年龄必须在0-150之间")
self.__age = age
# 使用
p = Person("张三", 25)
print(p.get_name()) # 输出:张三
p.set_age(26)
print(p.get_age()) # 输出:26
2. 使用@property装饰器(推荐)
将方法转换为 属性访问方式 (无需括号),更简洁直观:
class Person:
def __init__(self, name, age):
self._name = name # 受保护属性
self._age = age # 受保护属性
# 公共接口:property装饰器实现getter方法
@property
def name(self):
"""姓名属性(只读)"""
print("[GET] 获取姓名")
return self._name
@property
def age(self):
"""年龄属性(可读写)"""
print("[GET] 获取年龄")
return self._age
# 配套的setter方法(可选)
@age.setter
def age(self, value):
print("[SET] 设置年龄")
if value < 0 or value > 150:
raise ValueError("年龄必须在0-150之间")
self._age = value
# 使用
p = Person("张三", 25)
print(p.name) # 输出:[GET] 获取姓名 \\n 张三(无需括号)
p.age = 26 # 输出:[SET] 设置年龄
print(p.age) # 输出:[GET] 获取年龄 \\n 26
3.两者区别
| 特性 | 传统getter方法 | @property 装饰器 |
| 调用方式 | obj.get_name() | obj.name (属性访问) |
| 代码简洁性 | 较繁琐 | 更简洁直观 |
| 向后兼容性 | 需要修改调用代码 | 无需修改已有代码 |
| 只读属性实现 | 不定义setter方法 | 仅定义 @property ,不定义setter |
| 类型提示友好度 | 一般 | 优秀(IDE可识别为属性) |
作者有话说:首先我发现的不同,传统的与新的对比:


命名规则不同,新的接口更加明显,命名更加简单
四、实战案例:用户管理系统
1. 需求分析
设计一个用户类,需要:
– 存储用户的姓名、邮箱、密码(加密)
– 提供安全的属性访问方式
– 实现密码加密存储
– 验证邮箱格式
2. 完整实现代码
import hashlib
import re
class User:
def __init__(self, username, email, password):
self._username = username # 受保护属性
self._email = email # 受保护属性
self._password = self._encrypt_password(password) # 私有属性,存储加密密码
# 私有方法:密码加密(内部使用)
def _encrypt_password(self, password):
"""对密码进行MD5加密"""
return hashlib.md5(password.encode()).hexdigest()
# 私有方法:邮箱验证(内部使用)
def _validate_email(self, email):
"""验证邮箱格式是否正确"""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
# 公共接口:用户名(只读)
@property
def username(self):
return self._username
# 公共接口:邮箱(可读写,带验证)
@property
def email(self):
return self._email
@email.setter
def email(self, value):
if not self._validate_email(value):
raise ValueError("邮箱格式不正确")
self._email = value
# 公共接口:密码(只写,带加密)
@property
def password(self):
"""密码属性(只写,不允许读取)"""
raise AttributeError("密码不允许直接读取")
@password.setter
def password(self, value):
self._password = self._encrypt_password(value)
# 公共方法:验证密码
def verify_password(self, password):
"""验证输入的密码是否正确"""
encrypted = self._encrypt_password(password)
return encrypted == self._password
# 字符串表示
def __str__(self):
return f"User(username='{self._username}', email='{self._email}')"
五、总结
1. 何时使用哪种方式?
– 简单属性 :直接使用公共属性(如 name )
– 需要控制访问的属性 :使用 @property 装饰器
– 内部辅助功能 :使用私有方法(如 _encrypt_password )
– 只读属性 :仅定义 @property ,不定义setter方法
– 需要验证的属性 :同时定义 @property 和 @xxx.setter
2. 注意事项
– 不要过度封装 :简单的属性无需使用 @property
– 遵循命名约定 :私有属性使用双下划线,受保护属性使用单下划线
– 提供清晰的文档字符串 :为 @property 装饰的方法添加文档,说明属性的用途和访问权限
– 避免在getter/setter中做耗时操作 :这些方法可能被频繁调用,会影响性能
3. 常见误区
– 误解 __name 的安全性 :它只是一种命名约定,不是真正的私有(仍可通过 _类名__name 访问)
– 滥用 @property :简单属性无需使用,会增加代码复杂度
– 忘记验证输入 :在setter方法中一定要验证输入数据的合法性





