Python基础(二十二): 属性的访问权限

3,223 阅读5分钟
  1. 属性的访问权限: 公有属性、受保护的属性、私有属性
  2. Python中并没有真正的私有化支持, 但是可以使用下划线, 完成伪私有的效果

一、公有属性

  • 公有属性
* 类内部访问
* 子类内部访问
* 模块内其他位置: 父类, 派生类, 父类实例, 派生类实例
* 跨模块访问: import, from 模块 import *

1、类内部访问

class Animal:
    name = "旺财"
    def call(self):
        print(Animal.name)
        print(self.name)
a = Animal()
a.call()
# 打印结果:
旺财
旺财

2、子类中访问

class Animal:
    name = "旺财"
    
class Dog(Animal):
    def call(self):
        print(Dog.name)
        print(self.name)
d = Dog()
d.call()
# 打印结果:
旺财
旺财

3、模块内其他位置访问

  • 父类, 派生类
class Animal:
    name = "旺财"
    
class Dog(Animal):
    pass
    
print(Animal.name)
print(Dog.name)
# 打印结果:
旺财
旺财
  • 父类实例, 派生类实例
class Animal:
    name = "旺财"
    
class Dog(Animal):
    pass

a = Animal()
d = Dog()
print(a.name)
print(d.name)
# 打印结果:
旺财
旺财

4、跨模块访问类的属性

  • 在其他模块中导入使用, 假设本模块名字为Module

  • import 模块名

import Module
pirnt(Module.Animal.name)       # 打印: 旺财
  • from 模块名 import *
from Python import *
print(Animal.name)              # 打印: 旺财

5、跨模块访问变量

  • 在模块Module内定义一个变量
a = 10
  • import 模块名: 跨模块访问Module中的变量a
import Module
pirnt(Module.a)       # 打印: 10
  • from 模块名 import *: 跨模块访问Module中的变量a
from Python import *
print(a)              # 打印: 10

二、受保护的属性

  • 受保护的属性: 在定义属性时, 在名称前面加上一个下划线_
* 类内部访问
* 子类内部访问
* 模块内其他位置: 父类, 派生类, 父类实例, 派生类实例, 但是会有警告
* 跨模块访问: import 模块: 可以访问, 但是会有警告
            from 模块 import *: 可以访问, 但是会有警告

1、类内部访问

  • 正常访问, 没有警告
class Animal:
    _name = "富贵"
    def call(self):
        print(Animal._name)
        print(self._name)
a = Animal()
a.call()
# 打印结果:
富贵
富贵

2、子类内部访问

  • 正常访问, 没有警告
class Animal:
    _name = "富贵"
class Dog(Animal):
    def call(self):
        print(Dog._name)
        print(self._name)
d = Dog()
d.call()
# 打印结果:
富贵
富贵

3、模块内其他位置

  • 父类、派生类访问, 会有警告
class Animal:
    _name = "富贵"
    
class Dog(Animal):
    pass;
    
print(Animal._name)
print(Dog._name)
# 打印结果:
富贵
富贵

# 警告: Access to a protected member _name of a class

正常打印, 但是会发出警告: Access to a protected member _name of a class

  • 父类实例、派生类实例访问, 会有警告
class Animal:
    _name = "富贵"
    
class Dog(Animal):
    pass;
    
a = Animal()
d = Dog()

print(a._name)
print(d._name)
# 打印结果:
富贵
富贵

# 警告: Access to a protected member _name of a class

正常打印, 但是会发出警告: Access to a protected member _name of a class

4、跨模块访问类的属性

  • 在其他模块中导入使用, 假设本模块名字为Module

  • import 模块名: 可以访问, 但是会有警告

import Module
print(Module.Animal._name)        # 打印: 10
# 警告: Access to a protected member _name of a class
  • from 模块名 import *: 可以访问, 但是会有警告
from Module import *
print(Animal._name)
# 警告: Access to a protected member _name of a class

5、跨模块访问变量

  • 假设模块Module中有一个变量
_a = 10
  • import 模块名: 可以访问, 但是会有警告
import Module
print(Module._a)        # 打印: 10
# 警告: Access to a protected member _a of a module
  • from 模块名 import *: 不可以访问, 报错
from Module import  *
print(_a)               # 报错: NameError: name '_a' is not defined

5、__all__

  • __all__: 表示其他模块使用本模块式时, 可以导入的变量, 在默认情况下, 如果变量前有一个下划线_, 例如:_a, 就不会存放在__all__
  • 我们在__all__中加上_a, 就可以在其他模块中使用_a
# Module模块
__all__ = ["_a"]
_a = 10
# 其他模块
from Module import  *
print(_a)               # 打印: 10

三、私有属性

  • 私有属性: 只能在类的内部访问
* 类内部访问
* 子类不能访问
* 模块内其他位置不能访问
* 跨模块不能访问

1、类内部访问

class Animal:
    __name = "狗蛋"
    def call(self):
        print(Animal.__name)
        print(self.__name)
a = Animal()
a.call()
# 打印效果:
狗蛋
狗蛋

2、私有属性的实现机制

  • 私有属性的实现机制 -- 名字重整(Name Mangling)

  • 解释器会将带有两个下划线__的属性的名字修改, 例如_类名__x

class Aminal:
    __name = "狗蛋"
  • 可以通过类的__dict__属性, 查看类中的属性
print(Animal.__dict__)
# 打印: {'__module__': '__main__', '_Animal__name': '狗蛋', '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
  • 可以看到有一个属性的名字叫_Animal__name, 我们可以直接在类的外面访问它
print(Animal._Animal__name)         # 打印: 狗蛋

名字重整的目的: 防止外界直接访问, 防止被子类同名称属性覆盖

模块内带有两个下划线的变量与只有一个下划线的变量使用方法一致, 不再测试

3、私有属性一个简单的应用场景

  • 定义一个Person类, 并在初始化方法中添加私有属性__age
class Person:
    def __init__(self):
        self.__age = 0
  • 因为私有属性是不暴露给外界的, 所以在赋值和取值的时候, 需要定义两个方法
def setAge(self, value):
    self.__age = value
    
def getAge(self):
    return self.__age
  • 因为__age描述的是人的年龄, 所以必须是int类型, 且在0-200之间
def setAge(self, value):
    if isinstanse(value, int) and 0 < value < 200:
        self.__age = value
  • 完整代码如下:
class Person:
    def __init__(self):
        self.__age = 0
        
    def setAge(self, value):
        if isinstance(value, int) and 0 < value < 200:
            self.__age = value
            
    def getAge(self):
        return self.__age
  • 在赋值和取值时, 直接调用方法就可以了
p = Person()
p.setAge(20)            # 赋值
age = p.getAge()        # 取值
print(age)              # 打印: 20

补充: 关于属性命名时加下划线的规范问题

  1. 在名字后面添加一个下划线, 用于与系统关键词区分, 例如: class_
  2. 系统内键属性, 两端各加两个下划线, 例如: __dict__, 我们给变量命名时应该避免这种命名方式, 与系统区分开