在python类中定义常量,是否真的需要自己?

时间:2010-10-22 09:33:54

标签: python constants visibility

我想在类中定义一组常量,如:

class Foo(object):
   (NONEXISTING,VAGUE,CONFIRMED) = (0,1,2)
   def __init__(self):
       self.status = VAGUE

然而,我得到了

NameError: global name 'VAGUE' is not defined

有没有办法在不使用globalself.NONEXISTING = 0等的情况下将这些常量定义为可在班级内查看?

5 个答案:

答案 0 :(得分:36)

当您在类体中指定名称时,您将创建该类的属性。如果不直接或间接地引用该类,则无法引用它们。您可以像其他答案一样使用Foo.VAGUE,也可以使用self.VAGUE。您不必将分配分配给self

的属性

通常,使用self.VAGUE是你想要的,因为它允许子类重新定义属性而不必重新实现使用它们的所有方法 - 而不是在这个特定的例子中这似乎是一件明智的事情,但是谁知道呢。

答案 1 :(得分:7)

尝试而不是:

self.status = VAGUE

这一个:

self.status = Foo.VAGUE

你必须指定班级

答案 2 :(得分:3)

唯一的方法是通过类名称访问它,例如

Foo.VAGUE

如果只访问__init__函数或函数中的VAGUE,则必须在其中声明它以便按照您希望的方式访问它。

使用self也适用于类的实例。

答案 3 :(得分:3)

任何代码都不建议使用任何代码,但可以完成下面的丑陋黑客攻击。 我之所以这样做只是为了更好地理解Python AST API,所以任何在真实世界代码中使用它的人都应该在它造成任何伤害之前拍摄: - )

#!/usr/bin/python
# -*- coding: utf-8-unix -*-
#
# AST hack to replace symbol reference in instance methods,
# so it will be resolved as a reference to class variables.
#

import inspect, types, ast

def trim(src):
    lines = src.split("\n")
    start = lines[0].lstrip()
    n = lines[0].index(start)
    src = "\n".join([line[n:] for line in lines])
    return src

#
# Method decorator that replaces symbol reference in a method
# so it will use symbols in belonging class instead of the one
# in global namespace.
#
def nsinclude(*args):
    # usecase: @nsinclude()
    # use classname in calling frame as a fallback
    stack = inspect.stack()
    opts  = [stack[1][3]]

    def wrap(func):
        if func.func_name == "tempfunc":
            return func

        def invoke(*args, **kw):
            base = eval(opts[0])

            src = trim(inspect.getsource(func))
            basenode = ast.parse(src)

            class hackfunc(ast.NodeTransformer):
                def visit_Name(self, node):
                    try:
                        # if base class (set in @nsinclude) can resolve
                        # given name, modify AST node to use that instead
                        val = getattr(base, node.id)

                        newnode = ast.parse("%s.%s" % (opts[0], node.id))
                        newnode = next(ast.iter_child_nodes(newnode))
                        newnode = next(ast.iter_child_nodes(newnode))
                        ast.copy_location(newnode, node)
                        return ast.fix_missing_locations(newnode)
                    except:
                        return node

            class hackcode(ast.NodeVisitor):
                def visit_FunctionDef(self, node):
                    if func.func_name != "tempfunc":
                        node.name = "tempfunc"
                        hackfunc().visit(node)

            hackcode().visit(basenode)

            newmod = compile(basenode, '<ast>', 'exec')
            eval(newmod)
            newfunc = eval("tempfunc")
            newfunc(*args, **kw)
        return invoke


    # usecase: @nsinclude
    if args and isinstance(args[0], types.FunctionType):
        return wrap(args[0])

    # usecase: @nsinclude("someclass")
    if args and args[0]:
        opts[0] = args[0]
    return wrap

class Bar:
    FOO = 987
    BAR = 876

class Foo:
    FOO = 123
    BAR = 234

    # import from belonging class
    @nsinclude
    def dump1(self, *args):
        print("dump1: FOO = " + str(FOO))


    # import from specified class (Bar)
    @nsinclude("Bar")
    def dump2(self, *args):
        print("dump2: BAR = " + str(BAR))

Foo().dump1()
Foo().dump2()

答案 4 :(得分:3)

在Python3中,您还可以将VAGUE引用为:

type(self).VAGUE

这样,您显然将它作为类属性而不是对象属性引用,但这种方式对于类的名称更改是强健的。此外,如果您覆盖子类中的VAGUE,则将使用子类中的值,就像您使用self.VAGUE一样。

请注意,此方法似乎在Python2中不起作用,至少在我的测试中没有,type(self)返回instance而不是我实例化的类。因此,考虑到Python2的普及程度,Thomas Wouters的答案可能更可取。