How do I create a constant in Python?

后端 未结 30 2668
既然无缘
既然无缘 2020-11-22 09:07

Is there a way to declare a constant in Python? In Java we can create constant values in this manner:

public static          


        
30条回答
  •  死守一世寂寞
    2020-11-22 09:41

    Here it is a collection of idioms that I created as an attempt to improve some of the already available answers.

    I know the use of constant is not pythonic, and you should not do this at home!

    However, Python is such a dynamic language! This forum shows how it is possible the creation of constructs that looks and feels like constants. This answer has as the primary purpose to explore what can be expressed by the language.

    Please do not be too harsh with me :-).

    For more details I wrote a accompaniment blog about these idioms.

    In this post, I will call a constant variable to a constant reference to values (immutable or otherwise). Moreover, I say that a variable has a frozen value when it references a mutable object that a client-code cannot update its value(s).

    A space of constants (SpaceConstants)

    This idiom creates what looks like a namespace of constant variables (a.k.a. SpaceConstants). It is a modification of a code snippet by Alex Martelli to avoid the use of module objects. In particular, this modification uses what I call a class factory because within SpaceConstants function, a class called SpaceConstants is defined, and an instance of it is returned.

    I explored the use of class factory to implement a policy-based design look-alike in Python in stackoverflow and also in a blogpost.

    def SpaceConstants():
        def setattr(self, name, value):
            if hasattr(self, name):
                raise AttributeError(
                    "Cannot reassign members"
                )
            self.__dict__[name] = value
        cls = type('SpaceConstants', (), {
            '__setattr__': setattr
        })
        return cls()
    
    sc = SpaceConstants()
    
    print(sc.x) # raise "AttributeError: 'SpaceConstants' object has no attribute 'x'"
    sc.x = 2 # bind attribute x
    print(sc.x) # print "2"
    sc.x = 3 # raise "AttributeError: Cannot reassign members"
    sc.y = {'name': 'y', 'value': 2} # bind attribute y
    print(sc.y) # print "{'name': 'y', 'value': 2}"
    sc.y['name'] = 'yprime' # mutable object can be changed
    print(sc.y) # print "{'name': 'yprime', 'value': 2}"
    sc.y = {} # raise "AttributeError: Cannot reassign members"
    
    

    A space of frozen values (SpaceFrozenValues)

    This next idiom is a modification of the SpaceConstants in where referenced mutable objects are frozen. This implementation exploits what I call shared closure between setattr and getattr functions. The value of the mutable object is copied and referenced by variable cache define inside of the function shared closure. It forms what I call a closure protected copy of a mutable object.

    You must be careful in using this idiom because getattr return the value of cache by doing a deep copy. This operation could have a significant performance impact on large objects!

    from copy import deepcopy
    
    def SpaceFrozenValues():
        cache = {}
        def setattr(self, name, value):
            nonlocal cache
            if name in cache:
                raise AttributeError(
                    "Cannot reassign members"
                )
            cache[name] = deepcopy(value)
        def getattr(self, name):
            nonlocal cache
            if name not in cache:
                raise AttributeError(
                    "Object has no attribute '{}'".format(name)
                )
            return deepcopy(cache[name])
        cls = type('SpaceFrozenValues', (),{
            '__getattr__': getattr,
            '__setattr__': setattr
        })
        return cls()
    
    fv = SpaceFrozenValues()
    print(fv.x) # AttributeError: Object has no attribute 'x'
    fv.x = 2 # bind attribute x
    print(fv.x) # print "2"
    fv.x = 3 # raise "AttributeError: Cannot reassign members"
    fv.y = {'name': 'y', 'value': 2} # bind attribute y
    print(fv.y) # print "{'name': 'y', 'value': 2}"
    fv.y['name'] = 'yprime' # you can try to change mutable objects
    print(fv.y) # print "{'name': 'y', 'value': 2}"
    fv.y = {} # raise "AttributeError: Cannot reassign members"
    

    A constant space (ConstantSpace)

    This idiom is an immutable namespace of constant variables or ConstantSpace. It is a combination of awesomely simple Jon Betts' answer in stackoverflow with a class factory.

    def ConstantSpace(**args):
        args['__slots__'] = ()
        cls = type('ConstantSpace', (), args)
        return cls()
    
    cs = ConstantSpace(
        x = 2,
        y = {'name': 'y', 'value': 2}
    )
    
    print(cs.x) # print "2"
    cs.x = 3 # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
    print(cs.y) # print "{'name': 'y', 'value': 2}"
    cs.y['name'] = 'yprime' # mutable object can be changed
    print(cs.y) # print "{'name': 'yprime', 'value': 2}"
    cs.y = {} # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
    cs.z = 3 # raise "AttributeError: 'ConstantSpace' object has no attribute 'z'"
    

    A frozen space (FrozenSpace)

    This idiom is an immutable namespace of frozen variables or FrozenSpace. It is derived from the previous pattern by making each variable a protected property by closure of the generated FrozenSpace class.

    from copy import deepcopy
    
    def FreezeProperty(value):
        cache = deepcopy(value)
        return property(
            lambda self: deepcopy(cache)
        )
    
    def FrozenSpace(**args):
        args = {k: FreezeProperty(v) for k, v in args.items()}
        args['__slots__'] = ()
        cls = type('FrozenSpace', (), args)
        return cls()
    
    fs = FrozenSpace(
        x = 2,
        y = {'name': 'y', 'value': 2}
    )
    
    print(fs.x) # print "2"
    fs.x = 3 # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
    print(fs.y) # print "{'name': 'y', 'value': 2}"
    fs.y['name'] = 'yprime' # try to change mutable object
    print(fs.y) # print "{'name': 'y', 'value': 2}"
    fs.y = {} # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
    fs.z = 3 # raise "AttributeError: 'FrozenSpace' object has no attribute 'z'"
    

提交回复
热议问题