0. 引言
在Python的类中,默认的定义为公开变量/方法,当然也可以定义私有变量和私有方法,与其他语言不同的是,Python中使用__
进行声明而非关键字进行声明。
1. 私有属性和私有方法
1.1 定义
- 私有属性是指在 Python 的面向对象开发过程中,对象的某些属性只想在对象的内部被使用,但不想在外部被访问到这些属性。即,私有属性是对象不愿意公开的属性。
- 私有方法是指在 Python 的面向对象开发过程中,对象的某些方法或者称为函数只想在对象的内部被使用,但不想在外部被访问到这些方法或函数。即:私有方法是对象不愿意公开的方法或函数。
1.2 语法
二者均使用开头__
(两个下划线)进行定义:
- 定义私有属性:
self.__属性名 = xxx
- 定义私有方法:
def __方法名(参数)
例子:
class Demo:
def __init__(self, param_1, param_2):
self.param_1 = param_1 # 定义公有属性
self.__param_2 = param_2 # 定义私有属性
# 定义私有方法
def __secret_function(self):
print(f"[公有属性] self.param_1: {
self.param_1}")
print(f"[私有属性] self.__param_2: {
self.__param_2}")
1.3 调用分析
1.3.1 私有属性
在__init__
的对象初始化方法中,以两个下划线开头定义的__param_1
属性就是私有属性。现在在对象的外部来调用一下__param_1
属性,看是否能正常访问该私有属性。
class Demo:
def __init__(self, param_1, param_2):
self.param_1 = param_1 # 定义公有属性
self.__param_2 = param_2 # 定义私有属性
# 定义私有方法
def __secret_function(self):
print(f"[公有属性] self.param_1: {
self.param_1}")
print(f"[私有属性] self.__param_2: {
self.__param_2}")
ins_obj = Demo(param_1="公有属性", param_2="私有属性")
print(ins_obj.param_1)
print(ins_obj.__param_2)
"""
公有属性
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Input In [20], in <cell line: 14>()
12 ins_obj = Demo(param_1="公有属性", param_2="私有属性")
13 print(ins_obj.param_1)
---> 14 print(ins_obj.__param_2)
AttributeError: 'Demo' object has no attribute '__param_2'
"""
从上图运行结果可以看出,第14行,即在对象外部访问对象的私有属性 __param_2
时,提示 AttributeError 错误,Demo
对象 ins_obj
没有__param_2
属性。
为了证明ins_obj
类对象确实是有 self.__param_2
这个实例属性的,只是因为在对象外部不能访问私有属性。我把 self.__param_2
修改为self.param_2
,结果如下:
class Demo:
def __init__(self, param_1, param_2):
self.param_1 = param_1 # 定义公有属性
# self.__param_2 = param_2 # 定义私有属性
self.param_2 = param_2 # 将私有属性改为公有属性
# 定义私有方法
def __secret_function(self):
print(f"[公有属性] self.param_1: {
self.param_1}")
print(f"[私有属性] self.__param_2: {
self.param_2}")
ins_obj = Demo(param_1="公有属性", param_2="私有方法 -> 公有属性")
print(ins_obj.param_1)
print(ins_obj.param_2)
"""
公有属性
私有方法 -> 公有属性
"""
可以从运行结果看出,这种公有属性在外部的调用是正常的,没有提示 AttributeError 错误。
1.3.2 私有方法
在__secret_function(self)
实例方法中,以两个下划线开头定义的__secret_function(self)
方法就是私有方法。和上面测试流程一样,先在对象的外部来调用私有方法__secret_function(self)
,看是否能正常调用该私有方法。
class Demo:
def __init__(self, param_1, param_2):
self.param_1 = param_1 # 定义公有属性
self.__param_2 = param_2 # 定义私有属性
# 定义私有方法
def __secret_function(self):
print(f"[公有属性] self.param_1: {
self.param_1}")
print(f"[私有属性] self.__param_2: {
self.__param_2}")
ins_obj = Demo(param_1="公有属性", param_2="私有属性")
print(ins_obj.param_1)
try:
print(ins_obj.__param_2)
except Exception as e:
print(f"在调用私有属性时发生错误: {
e}")
# 调用私有方法
ins_obj.__secret_function()
"""
公有属性
在调用私有属性时发生错误: 'Demo' object has no attribute '__param_2'
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Input In [22], in <cell line: 20>()
17 print(f"在调用私有属性时发生错误: {e}")
19 # 调用私有方法
---> 20 ins_obj.__secret_function()
AttributeError: 'Demo' object has no attribute '__secret_function'
"""
从上图运行结果可以看出,第20行,即在对象外部访问对象的私有方法__secret_function(self)
时,提示 AttributeError 错误,Demo
对象 ins_obj
没有 __secret_function(self)
方法。
为了证明 Demo
类对象是有 __secret_function(self)
这个实例方法的,只是因为在对象外部不能访问私有方法。把__secret_function(self)
方法修改为:secret_function(self)
,其他代码不变,看如下的运行结果。
class Demo:
def __init__(self, param_1, param_2):
self.param_1 = param_1 # 定义公有属性
self.__param_2 = param_2 # 定义私有属性
# 定义私有方法
def secret_function(self): # 将私有方法改为公有方法
print(f"[公有属性——类内部调用] self.param_1: {
self.param_1}")
print(f"[私有属性——类内部调用] self.__param_2: {
self.__param_2}")
ins_obj = Demo(param_1="公有属性", param_2="私有属性")
print(f"[类外部调用] ins_obj.param_1: {
ins_obj.param_1}")
try:
print(f"[类外部调用] ins_obj.__param_2: {
ins_obj.__param_2}")
except Exception as e:
print(f"在调用私有属性时发生错误: {
e}")
# 调用公有方法
ins_obj.secret_function()
"""
[类外部调用] ins_obj.param_1: 公有属性
在调用私有属性时发生错误: 'Demo' object has no attribute '__param_2'
[公有属性——类内部调用] self.param_1: 公有属性
[私有属性——类内部调用] self.__param_2: 私有属性
"""
可以从运行结果看出,这种非私有方法在外部的调用是正常的,没有提示 AttributeError 错误。
1.3.3 类内部调用私有属性/方法
在对象内部私有方法与私有属性是可以被调用的,证明如下:
class Demo:
def __init__(self, param_1, param_2):
self.param_1 = param_1 # 定义公有属性
self.__param_2 = param_2 # 定义私有属性
# 定义私有方法
def __secret_function(self): # 私有方法
print(f"[公有属性——类内部调用] self.param_1: {
self.param_1}")
print(f"[私有属性——类内部调用] self.__param_2: {
self.__param_2}")
# 定义公有方法并在公有方法中调用私有方法和私有属性
def public_function(self): # 公有方法
# 分别调用公有属性和私有属性
print(f"[public_function内部调用] self.param_1: {
self.param_1}")
print(f"[public_function内部调用] self.__param_2: {
self.__param_2}")
# 调用私有方法
self.__secret_function()
ins_obj = Demo(param_1="公有属性", param_2="私有属性")
print(f"[类外部调用] ins_obj.param_1: {
ins_obj.param_1}")
try:
print(f"[类外部调用] ins_obj.__param_2: {
ins_obj.__param_2}")
except Exception as e:
print(f"在调用私有属性时发生错误: {
e}")
# 调用类的私有方法
try:
ins_obj.__secret_function()
except Exception as e:
print(f"在调用私有方法时发生错误: {
e}")
# 调用公有方法
ins_obj.public_function()
"""
[类外部调用] ins_obj.param_1: 公有属性
在调用私有属性时发生错误: 'Demo' object has no attribute '__param_2'
在调用私有方法时发生错误: 'Demo' object has no attribute '__secret_function'
[public_function内部调用] self.param_1: 公有属性
[public_function内部调用] self.__param_2: 私有属性
[公有属性——类内部调用] self.param_1: 公有属性
[私有属性——类内部调用] self.__param_2: 私有属性
"""
上述代码中,public_function
公有方法调用了私有属性self.__param_2
和私有方法__secret_function(self)
。说明在对象外部可以通过调用对象的公有方法去访问私有属性和私有方法(前提是得在类的内部定义好相关的代码)。
在类的内部,私有属性和私有方法是可以随意被调用的,一旦到了类的外部,那就不能调用了!
1.3.4 子类 内部/外部 调用私有属性/方法
class Demo:
def __init__(self, param_1, param_2):
self.param_1 = param_1 # 定义公有属性
self.__param_2 = param_2 # 定义私有属性
# 定义私有方法
def __secret_function(self): # 私有方法
print(f"[公有属性——类内部调用] self.param_1: {
self.param_1}")
print(f"[私有属性——类内部调用] self.__param_2: {
self.__param_2}")
# 定义公有方法并在公有方法中调用私有方法和私有属性
def public_function(self): # 公有方法
# 分别调用公有属性和私有属性
print(f"[public_function内部调用] self.param_1: {
self.param_1}")
print(f"[public_function内部调用] self.__param_2: {
self.__param_2}")
# 调用私有方法
self.__secret_function()
class Demo_child(Demo):
# 继承Demo的一切
# 定义公有方法访问父类的私有属性和方法
def public_child(self):
# 分别调用公有属性和私有属性
print(f"[public_child内部调用] self.param_1: {
self.param_1}")
print(f"[public_child内部调用] self.__param_2: {
self.__param_2}")
# 调用私有方法
self.__secret_function()
child_obj = Demo_child(param_1="Re-公有属性", param_2="Re-私有属性")
print(f"[成功] [类外部调用-子类调用父类公有属性] child_obj.param_1: {
child_obj.param_1}")
try:
print(f"[成功] [类外部调用-子类调用父类私有属性] child_obj.__param_2: {
child_obj.__param_2}")
except Exception as e:
print(f"[失败] 子类调用父类私有属性时发生错误: {
e}")
# 子类调用父类的私有方法
try:
print("......................[开始] 子类调用父类的私有方法......................")
child_obj.__secret_function()
print("[成功] 子类调用父类的私有方法")
except Exception as e:
print(f"[失败] 子类调用父类的私有方法时发生错误: {
e}")
# 子类调用父类的公有方法
try:
print("......................[开始] 子类调用父类的公有方法......................")
child_obj.public_function()
print("[成功] 子类调用父类的公有方法")
except Exception as e:
print(f"[失败]子类调用父类的公有方法时发生错误: {
e}")
# 子类调用自己的公有方法
try:
print("......................[开始] 子类调用自己的公有方法......................")
child_obj.public_child()
print("[成功] 子类调用自己的公有方法")
except Exception as e:
print(f"[失败] 子类调用自己的公有方法时发生错误: {
e}")
"""
[成功] [类外部调用-子类调用父类公有属性] child_obj.param_1: Re-公有属性
[失败] 子类调用父类私有属性时发生错误: 'Demo_child' object has no attribute '__param_2'
......................[开始] 子类调用父类的私有方法......................
[失败] 子类调用父类的私有方法时发生错误: 'Demo_child' object has no attribute '__secret_function'
......................[开始] 子类调用父类的公有方法......................
[public_function内部调用] self.param_1: Re-公有属性
[public_function内部调用] self.__param_2: Re-私有属性
[公有属性——类内部调用] self.param_1: Re-公有属性
[私有属性——类内部调用] self.__param_2: Re-私有属性
[成功] 子类调用父类的公有方法
......................[开始] 子类调用自己的公有方法......................
[public_child内部调用] self.param_1: Re-公有属性
[失败] 子类调用自己的公有方法时发生错误: 'Demo_child' object has no attribute '_Demo_child__param_2'
"""
可以看到,子类可以调用:
- 父类的公有属性
- 父类的公有方法(无论该公有方法是否调用父类的私有方法/属性)
- 子类的公有方法(里面不能直接调用父类的私有方法!)
子类不能直接调用父类的私有方法和私有属性,但可以通过调用父类的公有方法间接访问父类的私有方法(即该父类的公有方法调用了父类的私有方法)。
1.4 Python 伪·私有属性和私有方法
在 Python 中,并没有真正意义上的私有,之所以上面的例子中我们无法访问在外部访问私有方法和私有属性,是因为 Python 内部在给属性、方法命名时,对名称做了一些特殊处理,使得外界无法访问到对应的属性和方法。
以私有属性和私有方法为例,Python内部处理方式为:
- 属性:
__param_1
,经过处理后的属性名为:_Demo__param_1
,即_类名__属性名
- 方法:
__secret_function
,经过处理后的方法名为:_Demo__secret_function
,即_类名__方法名
知道了 Python 内部对于私有属性和私有方法的处理,现在使用这种处理后的命名方式来在对象外部访问私有属性和私有方法,看是否能访问正常。
class Demo:
def __init__(self, param_1, param_2):
self.param_1 = param_1 # 定义公有属性
self.__param_2 = param_2 # 定义私有属性
# 定义私有方法
def __secret_function(self): # 私有方法
print(f"[公有属性——类内部调用] self.param_1: {
self.param_1}")
print(f"[私有属性——类内部调用] self.__param_2: {
self.__param_2}")
# 定义公有方法并在公有方法中调用私有方法和私有属性
def public_function(self): # 公有方法
# 分别调用公有属性和私有属性
print(f"[public_function内部调用] self.param_1: {
self.param_1}")
print(f"[public_function内部调用] self.__param_2: {
self.__param_2}")
# 调用私有方法
self.__secret_function()
ins_obj = Demo(param_1="公有属性", param_2="私有属性")
print(f"[成功] [类外部调用公有属性] ins_obj.param_1: {
ins_obj.param_1}")
try:
print(f"[成功] [类外部调用私有属性] ins_obj.__param_2: {
ins_obj._Demo__param_2}")
except Exception as e:
print(f"[失败] 类外部调用私有属性时发生错误: {
e}")
# 调用私有方法
try:
ins_obj._Demo__secret_function()
print("[成功] 类外部调用私有方法")
except Exception as e:
print(f"[失败] 类外部调用私有方法时发生错误: {
e}")
"""
[成功] [类外部调用公有属性] ins_obj.param_1: 公有属性
[成功] [类外部调用私有属性] ins_obj.__param_2: 私有属性
[公有属性——类内部调用] self.param_1: 公有属性
[私有属性——类内部调用] self.__param_2: 私有属性
[成功] 类外部调用私有方法
"""
控制台没有抛任何的异常,之前的提示 AttributeError 错误也没有了。
这个例子证明了 Python 是没有真正意义上的私有的,当知道了其内部处理方式后,依然可以使用实例对象._类名__属性名(方法名)
的方法来在对象外部访问到对象内部定义的私有属性和私有方法。
但这种方式在日常工作中是不推荐使用的,既然在对象内部定义属性和方法时,就声明了其为私有的,调用方就需要遵守其规则。
2. 实例方法、静态方法和类方法
2.1 实例方法/对象方法
实例方法或者叫对象方法,指的是我们在类中定义的普通方法。只有实例化对象之后才可以使用的方法,该方法的第一个形参接收的一定是对象本身self
。
说白了实例化方法就是我们最常用的方法,例子如下:
class Demo:
# 定义一个实例方法(对象方法)
def ins_func(self): # 对于实例方法,self不能省略
print("这是一个[实例方法]")
# [实例化方法] 方法1:实例化对象 -> 变量接收 -> 变量.调用实例方法
ins_obj = Demo()
ins_obj.ins_func()
# [实例化方法] 方法2:实例化对象.调用实例方法(没有实例化对象的接收变量)
Demo().ins_func() # Demo的()就说明实例化对象了!
"""
这是一个[实例方法]
这是一个[实例方法]
"""
2.2 静态方法
- 格式:在方法上面添加
@staticmethod
- 参数:静态方法可以有参数也可以无参数
- 应用场景:一般用于和类对象以及实例对象无关的代码
- 使用方式:
类名.类方法名
(或者对象名.类方法名
)
例子:
class Demo:
# 定义一个实例方法(对象方法)
def ins_func(self): # 对于实例方法,self不能省略
print("这是一个[实例方法]")
# 定义一个静态方法
@staticmethod
def static_method(): # 参数可有可无
print("这是一个[静态方法]")
# [实例化方法] 方法1:实例化对象 -> 变量接收 -> 变量.调用实例方法
ins_obj = Demo()
ins_obj.ins_func()
# [实例化方法] 方法2:实例化对象.调用实例方法(没有实例化对象的接收变量)
Demo().ins_func() # Demo的()就说明实例化对象了!
# [静态方法] 不用实例化对象,直接调用静态方法
Demo.static_method()
# Demo().static_method() # 也不报错,但一般不这么用(因为会实例化对象)
"""
这是一个[实例方法]
这是一个[实例方法]
这是一个[静态方法]
"""
2.3 类方法
类方法无需实例化,可以通过类直接调用的方法,但是方法的第一个参数接收的一定是类本身cls
。
- 在方法上面添加
@classmethod
- 方法的参数为
cls
也可以是其他名称,但是一般默认为cls
cls
指向类对象
- 应用场景:当一个方法中只涉及到静态属性的时候可以使用类方法(类方法用来修改类属性)
- 使用方法(两种均可):
对象名.类方法名
类名.类方法名
例子:
class Demo:
# 0. 定义一个类属性
__private_attr = 1
# 1. 定义一个实例方法(对象方法)
def ins_func(self, param_ins): # 对于实例方法,self不能省略
self.param_ins = param_ins
print(f"这是一个[实例方法]: {
self.param_ins}")
# 2. 定义一个静态方法
@staticmethod
def static_method(param_static): # 参数可有可无
print(f"这是一个[静态方法]: {
param_static}")
# 3. 定义一个类方法
@classmethod
def class_method(cls, param_cls):
print(f"这是一个[类方法]: {
param_cls}")
print(f"[修改前]类属性为: {
cls.__private_attr}")
cls.__private_attr = param_cls
print(f"[修改后]类属性为: {
cls.__private_attr}\n")
try:
print(f"[修改前]类属性self.param_ins为: {
cls.param_ins}")
cls.param_ins = param_cls
print(f"[修改后]类属性self.param_ins为: {
cls.param_ins}")
except Exception as e:
print("类方法只能修改类属性,不能修改实例方法的类属性!")
# [实例化方法] 方法1:实例化对象 -> 变量接收 -> 变量.调用实例方法
ins_obj = Demo()
ins_obj.ins_func(param_ins = "实例方法ins_func传入的参数")
# [实例化方法] 方法2:实例化对象.调用实例方法(没有实例化对象的接收变量)
Demo().ins_func(param_ins = "实例方法ins_func传入的参数") # Demo的()就说明实例化对象了!
# [静态方法] 不用实例化对象,直接调用静态方法
Demo.static_method(param_static="静态方法static_method传入的参数")
# Demo().static_method(param_static="静态方法static_method传入的参数") # 也不报错,但一般不这么用(因为会实例化对象)
# [类方法] 方法1:对象名.类方法名
ins_obj.class_method(100)
# [类方法] 方法2:类名.类方法名
Demo.class_method(100)
"""
这是一个[实例方法]: 实例方法ins_func传入的参数
这是一个[实例方法]: 实例方法ins_func传入的参数
这是一个[静态方法]: 静态方法static_method传入的参数
这是一个[类方法]: 100
[修改前]类属性为: 1
[修改后]类属性为: 100
类方法只能修改类属性,不能修改实例方法的类属性!
这是一个[类方法]: 100
[修改前]类属性为: 100
[修改后]类属性为: 100
类方法只能修改类属性,不能修改实例方法的类属性!
"""
参考
- https://www.jb51.net/article/239280.htm
- https://blog.csdn.net/weixin_39898248/article/details/109935893