在python类中定义常量,真的需要self吗?

发布于 2024-09-28 17:39:12 字数 344 浏览 5 评论 0原文

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

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 等?

I want to define a set of constants in a class like:

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

However, I get

NameError: global name 'VAGUE' is not defined

Is there a way of defining these constants to be visiable inside the class without resorting to global or self.NONEXISTING = 0 etc.?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

枉心 2024-10-05 17:39:12

当您在类主体中分配名称时,您正在创建该类的属性。如果不直接或间接引用该类,则无法引用它们。您可以使用 Foo.VAGUE 正如其他答案所说,或者您可以使用 self.VAGUE。您不必分配self的属性。

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

When you assign to names in the class body, you're creating attributes of the class. You can't refer to them without referring to the class either directly or indirectly. You can use Foo.VAGUE as the other answers say, or you can use self.VAGUE. You do not have to assign to attributes of self.

Usually, using self.VAGUE is what you want because it allows subclasses to redefine the attribute without having to reimplement all the methods that use them -- not that that seems like a sensible thing to do in this particular example, but who knows.

感性不性感 2024-10-05 17:39:12

尝试代替:

self.status = VAGUE

这个:

self.status = Foo.VAGUE

你必须指定类

try instead of:

self.status = VAGUE

this one:

self.status = Foo.VAGUE

you MUST specify the class

诗化ㄋ丶相逢 2024-10-05 17:39:12

无论如何,都不建议任何代码使用此方法,但是可以完成如下所示的丑陋的黑客攻击。
我这样做只是为了更好地理解 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()

This one is NOT RECOMMENDED FOR ANY CODE by any means, but an ugly hack like below can be done.
I did this just to have better understanding of Python AST API, so anyone who uses this in real-world code should be shot before it does any harm :-)

#!/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()
泪是无色的血 2024-10-05 17:39:12

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

Foo.VAGUE

如果仅访问 __init__ 函数或函数内的 VAGUE,则必须在其中声明它才能以您想要的方式访问它。

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

The only way is to access it through the class name such as

Foo.VAGUE

If accessing just VAGUE inside the __init__ function, or a function, it must be declared inside that to access it the way you want.

Using self is for the instance of the class also.

蓦然回首 2024-10-05 17:39:12

在Python3中,您还可以引用VAGUE

type(self).VAGUE

这样,您可以清楚地将其作为类属性而不是对象属性进行引用,但这种方式对于类的名称更改来说是稳健的。此外,如果您在子类中重写VAGUE,则将使用子类中的值,就像您使用self.VAGUE一样。

请注意,此方法似乎在 Python2 中不起作用,至少在我的测试中不起作用,其中 type(self) 返回 instance 而不是我实例化的类。因此,考虑到 Python2 仍然广泛传播,Thomas Wouters 的答案可能更可取。

In Python3, you can also reference VAGUE as:

type(self).VAGUE

This way, you are clearly referencing it as a class attribute and not an object attribute, yet this way is robust against a name change of the class. Also if you override VAGUE in a subclass, the value from the subclass will be used, just like if you were to use self.VAGUE.

Note that this method does not appear to work in Python2, at least not in my tests, where type(self) returned instance instead of the class I instantiated. Therefore Thomas Wouters's answer is probably preferable, considering how widespread Python2 still is.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文