Python基础18-面向对象(属性限制-公有私有)

Python基础18-面向对象(属性限制-公有私有)_第1张图片
Python基础-面向对象(属性限制)

1 区域
2 公有属性
3 受保护属性
4 私有属性

  • 注意:
    1 - Python并没有真正的私有化支持,但是, 可以使用下划线完成伪私有的效果
    2 - 类属性(方法)和实例属性(方法)遵循相同的规则
    伪私有:即还是可以通过其他(如:_类名__属性名)方式进行访问或修改

1 各种区域的划分及理解

Python基础18-面向对象(属性限制-公有私有)_第2张图片
各种区域

2 公有属性访问权限

x:没有下划线修饰的,公有属性

Python基础18-面向对象(属性限制-公有私有)_第3张图片
访问权限
  • 类内部、子类内部、模块内部测试
class Animal:
    x = 10
    def test(self):
        print(Animal.x)
        print(self.x)
    pass


class Dog(Animal):
    def test2(self):
        print(Dog.x)
        print(self.x)
    pass


# 测试在类的内部访问
a = Animal()
a.test()

# 测试在子类内部访问
d = Dog()
d.test2()

# 测试在模块内访问 - 类访问:通过父类(Animal) 或 派生类访问(Dog)
print(Animal.x)
print(Dog.x)

# 测试在模块内访问 - 实例访问:通过父类(a) 或 派生类访问(d)
print(a.x)
print(d.x)

  • 跨模块测试 (通过公有变量测试,而公有变量与公有属性的区别是是否有数组)
# 文件1.py 代码
a = 666

# 文件2.py 代码
import 1  # 通过这样的导入方式,需要使用 模块名.xx 来访问
print(1.a) # 666

或者

from 1 import *
pirnt(a) # 666

3 受保护属性访问权限

_x:一个下划线修饰的,受保护属性

Python基础18-面向对象(属性限制-公有私有)_第4张图片
  • 类内部、子类内部、模块内部测试
class Animal:
    _x = 10
    def test(self):
        print(Animal._x)
        print(self._x)
    pass


class Dog(Animal):
    def test2(self):
        print(Dog._x)
        print(self._x)
    pass


# 测试在类的内部访问
a = Animal()
a.test()

# 测试在子类内部访问
d = Dog()
d.test2()

# 测试在模块内访问 - 类访问:通过父类(Animal) 或 派生类访问(Dog)
print(Animal._x)
print(Dog._x)

# 测试在模块内访问 - 实例访问:通过父类(a) 或 派生类访问(d)
print(a._x)
print(d._x)

  • 跨模块测试 (通过公有变量测试,而公有变量与公有属性的区别是是否有数组)
# 文件1.py 代码
_a = 666

# 文件2.py 代码
import 1  # 通过这样的导入方式,需要使用 模块名.xx 来访问
print(1._a) # 666

或者

from 1 import *
pirnt(_a) # error

  • 注意,如果_a 被 __all__修饰的话,则通过 from 模块名 import *方式导入也是可以访问的。
# 文件1 中 模块内其他位置
__all__ = ["_a"]
_a = 666


# 文件2 模块内访问
from 1 import *
pirnt(_a) # 666

4 私有属性

  • __x:两个下划线修饰的,私有属性
Python基础18-面向对象(属性限制-公有私有)_第5张图片
class Animal:
   __x = 10
    def test(self):
        print(Animal.__x)
        print(self.__x)
    pass


class Dog(Animal):
    def test2(self):
        print(Dog.__x)
        print(self.__x)
    pass


# 测试在类的内部访问
a = Animal()
a.test()

# 测试在子类内部访问
d = Dog()
d.test2()

# 测试在模块内访问 - 类访问:通过父类(Animal) 或 派生类访问(Dog)
print(Animal.__x)
print(Dog.__x)

# 测试在模块内访问 - 实例访问:通过父类(a) 或 派生类访问(d)
print(a.__x)
print(d.__x)

  • 跨模块测试 (通过公有变量测试,而公有变量与公有属性的区别是是否有数组)
# 文件1.py 代码
__a = 666

# 文件2.py 代码
import 1  # 通过这样的导入方式,需要使用 模块名.xx 来访问
print(1.__a) # 666

或者

from 1 import *
pirnt(__a) # error

  • 注意,如果__a 被 __all__修饰的话,则通过 from 模块名 import *方式导入也是可以访问的。
# 文件1 中 模块内其他位置
__all__ = ["__a"]
__a = 666


# 文件2 模块内访问
from 1 import *
pirnt(__a) # 666

  • 私有属性的跨模块访问规则,参照单下划线开头变量的访问原则

私有属性的实现机制

  • 实际是通过:名字重整(Name Mangling),即重改 __x 为另外一个名称, 如
    _类名__x
# 可通过__dict__类属性查看
print(Animal.__dict__)
  • 使用私有属性目的:
    1. 防止外界直接访问
    2. 防止被子类同名称属性覆盖

私有属性的应用场景

  • 通过Person类实例化的对象都拥有一个 age属性,且具有一个默认值
class Person:
    # 主要作用, 当我们创建好一个实例对象之后, 会自动的调用这个方法, 来初始化这个对象
    def __init__(self):
        #此时的 age 是添加到对应的实例里面的,也就是 age 是类实例属性,Person 内没有 age 类属性
        self.age = 18

p1 = Person()
p2 = Person()
p3 = Person()

print(p1.age) # 18
print(p2.age) # 18
print(p3.age) # 18
  • 为避免将错误的数据赋值给实例.age
p1.age = -10
  • 我们需要将 age 属性保护 起来,让实例对象不能直接访问age属性。
# 通过私有实例属性保护age -> __age
class Person:
    def __init__(self):
        # 此时的 __age 会被编译器执行名字重整 name mangling,通过print(p1.__dict__) 打印结果是: {'_Person__age': 18}
        self.__age = 18

p1 = Person()
print(p1.age) # error,因为 age 已经被名字重整了,除非你通过 p1._Person__age进行访问,但这个访问方式不稳定,这是编译器特性
  • 通过实例方法对私有实例属性 age进行访问,同时对传入的数据进行数据过滤
class Person:
    def __init__(self):
        self.__age = 18

    def setAge(self, value):
        if isinstance(value, int) and 0 < value < 200:
            self.__age = value
        else:
            print("你输入的数据有问题, 请重新输入")

    def getAge(self):
        return self.__age # 这里能够通过__age访问是因为在类的内部

p1 = Person()
print(p1.getAge()) # 18

p1.setAge(10)
print(p1.getAge()) # 10

  • 上述例子解决了
  1. 让所有实例都具有一个有默认值的"属性"(只能通过方法访问)
  2. 对赋值数据进行了过滤操作

实践小提示:
在不明白属性所在位置或是否有name mangling时候,多通过__dict__进行打印查证

5 添加下划线的规范

xx_ :"变量名_" 这个格式是为了与系统属性作区分
__xx__ :"两端带__" 一般为系统内置属性或方法, 所以以后命名注意避免

你可能感兴趣的:(Python基础18-面向对象(属性限制-公有私有))