跳转至

类和对象-变量

私有变量

私有变量: 通过某种手段,使得对象中的属性或方法无法被外部访问。在 Python 中仅限从一个对象内部才能访问的私有变量并不存在,但是 Python 引用了另外一个方法- name mangling 名称改写,语法为在名称的前方加两个连续下划线。

Python
# 定义类并定义私有变量
class A:
    def __init__(self, x):
        self.__x = x

    def set_x(self, x):
        self.__x = x

    def get_x(self):
        print(self.__x)

# 此时无法在对象外部访问__x
a = A(250)
# 只可以通过get_x进行访问
print(a.get_x())  # 250
# 通过set_x进行修改__x
a.set_x(520)
print(a.get_x())  # 520
# 通过__dict__可以查看私有变量
print(a.__dict__)  # {'_A__x': 520}
# 可以直接访问c._A__X访问私有变量值,格式:对象名加点然后类名加连续的双下划线再加变量名即可
print(a._A__x)  # 520

# 定义类并定义一个私有方法,和私有变量类似
class B:
    def __init__(self):
        self.x = 1

    def __func(self):
        print("hello word", self.x)

    def get_func(self):
        self.__func()

b = B()
# 同样不可以直接使用私有方法
# 只能在对象的内部使用
b.get_func()  # hello word 1
# 或者使用b._B__func(),不建议这样使用
b._B__func()  # hello word 1

# 注意对象外部是不可以直接通过下表添加私有变量的
# 比如:b.__y = 250,这样不会发生异常,但是添加的也不是私有变量

定义下划线变量

单个下划线开头

仅供内部使用的变量,属于约定俗称的规则,不要随意的访问它,也不要随意的去修改它。

单个下划线结尾

通过结尾添加下划线可以是内置的 BIF 当成普通变量使用。

效率提升

Python 对象动态添加属性的方法属于字典的使用,通过使用 __dict__ 方法可以看到对应对象的属性,同样可以使用 __dict__方法直接设置对象的属性。

Python
# 创建类
class A:
    def __init__(self):
        self.x = 1


# 创建对象
a = A()
# 通过__dict__查看对象的属性
print(a.__dict__)  # {'x': 1}
# 通过对象创建属性
a.y = 100
print(a.__dict__)  # {'x': 1, 'y': 100}
# 通过__dict__键值创建属性
a.__dict__["z"] = 300
print(a.__dict__)  # {'x': 1, 'y': 100, 'z': 300}

字典的效率高,但是字典的占用内存空间却非常大, 动态添加属性的方法恰恰使用的是字典的方式,这样如果确定某个类的属性不会发生改变,这个时候可以使用 __slots__ 属性,避免用字典存放造成的空间浪费。这样通过类创建的对象就会划分固定的地址空间存放指定的属性,这也就是对象失去了动态创建属性的功能,进而 __dict__ 属性也就没有了。空间就节约下来了。

Python
# 定义类
class A:
  # 注意这里slots里面的属性定义完成后,在类中只可以有这些属性,可以暂且定义部分,另外的可以通过对象外部定义
    __slots__ = ["x", "y"]

    def __init__(self, x):
        self.x = x
        print(self.x)

# 按照指定的变量定义可以正常使用
a = A(1)  # 1
# 可以通过对象外部定义y
a.y = 100 # y=100
# 有且只可以定义x和y

继承自父类的 __slots__属性是不会在子类中生效的,Python 只会关注各个具有的类中定义的 __slots__属性。

Python
# 定义类A
class A:
    __slots__ = ["x", "y"]

    def __init__(self, x):
        self.x = x

# 定义类B继承类A
class B(A):
    pass

# 定义对象b
b = B(1)
# B继承A所以A的属性B同样可以使用
print(b.x)  # 1
b.y = 100
print(b.y)  # 100
# b可以随意动态的添加属性,不继承对应的__slots__限制
b.z = 300
print(b.z)
# 但是A的属性不会同步到B的属性中
print(b.__dict__)  # {'z': 300}
# 同样查看b的__slots__属性可以发现有,但是这个属性是属于A的
print(b.__slots__)  # ['x', 'y']