前言

Python 没有 const 关键字,想要创建常量只能自己想办法。
这篇笔记介绍的是使用私有变量、单例模式、只读属性和不可变类型创建一个 Constant 类,在其中储存和调用所需要的常量值。

私有变量[1]

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线 __,在Python中,实例的变量名如果以 __ 开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。

单例模式[2]

class Constant(object):
    """
    A singleton class to store all constants.
    """

    __instance = None

    def __new__(cls, *args, **kw):
        if cls.__instance is None:
            cls.__instance = object.__new__(cls, *args, **kw)
        return cls.__instance

只读属性[3]

Python 中的属性[4]和 C# 中的属性是同一个概念。

class People:

    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if isinstance(age, int):
            self.__age = age
        else:
            raise ValueError

    @age.deleter
    def age(self):
        print("删除年龄数据!")

obj = People("jack", 18)
print(obj.age)
obj.age = 19
print("obj.age:  ", obj.age)
del obj.age

---------------------------
打印结果:
18
obj.age:   19
删除年龄数据!

那么如何将一个普通的方法转换为一个“伪装”的属性呢?

  • 首先,在普通方法的基础上添加 @property 装饰器,例如上面的 age() 方法。这相当于一个 get 方法,用于获取值,决定类似 result = obj.age 执行什么代码。该方法仅有一个self 参数。
  • 写一个同名的方法,添加 @xxx.setter 装饰器(xxx表示和上面方法一样的名字),比如例子中的第二个方法。这相当于编写了一个 set 方法,提供赋值功能,决定类似 obj.age = .... 的语句执行什么代码。
  • 再写一个同名的方法,并添加 @xxx.delete 装饰器,比如例子中的第三个方法。用于删除功能,决定 del obj.age 这样的语句具体执行什么代码。

简而言之,就是分别将三个方法定义为对同一个属性的获取、修改和删除。还可以定义只读属性,只需要使用 @property 声明 get 方法,不定义 setter 方法就是一个只读属性。

class Constant(object):
    def __init__(self):
        self.__val = 1

    @property
    def val(self):
        return self.__val

不可变类型

常量是不可变类型的子集,区别在于不可变类型的变量名可以重新绑定到其他类型上,而常量名和常量值都是不可变的。

a = (1, 2, 3)   # 元组是一个不可变类型
print(a)
a = 1   # 原本绑定到不可变类型的变量名可以重新绑定到其他值
print(a)

---------------------------
输出:
(1, 2, 3)
1

不可变字典

可变类型 listset 对应的不可变类型分别为 tuplefrozenset。但在 PEP 416 中拒绝了不可变字典的要求。因此我们使用 Python 3.3 中引入的 MappingProxyType 和私有变量搭配实现不可变字典的返回。

from types import MappingProxyType


class Constant(object):
    """
    A singleton class to store all constants.
    """

    __instance = None

    def __new__(cls, *args, **kw):
        if cls.__instance is None:
            cls.__instance = object.__new__(cls, *args, **kw)
        return cls.__instance

    def __init__(self):
        self.__dict1 = {a:1, b:2}

    @property
    def dict1(self):
        return MappingProxyType(self.__dict1).copy()

使用常量

Constant 是一个类,所以我们需要先实例化才能使用它。

const = Constant()
dict1 = const.dict1

  1. 廖雪峰的 Python 教程 ↩︎

  2. Python单例模式(Singleton)的N种实现 ↩︎

  3. python @property 设置只读属性 重写 getter setter 方法 ↩︎

  4. 刘江的博客教程 ↩︎