Définir les constantes en classe python, est-ce que self est vraiment nécessaire?

je veux définir un ensemble de constantes dans une classe comme:

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

Cependant, j'obtiens

NameError: global name 'VAGUE' is not defined

y a-t-il une façon de définir ces constantes à être visibles à l'intérieur de la classe sans recourir à global ou self.NONEXISTING = 0 etc.?

25
demandé sur Theodor 2010-10-22 13:33:54

5 réponses

lorsque vous assignez des noms dans le corps de classe, vous créez des attributs de la classe. Vous ne pouvez pas vous y référer sans vous référer directement ou indirectement à la classe. Vous pouvez utiliser Foo.VAGUE comme d'autres réponses dire, ou vous pouvez utiliser self.VAGUE. Vous n'avez pas à assign aux attributs de self.

habituellement, en utilisant self.VAGUE est ce que vous voulez parce qu'il permet aux sous-classes de redéfinir l'attribut sans avoir à réimposer toutes les méthodes qui les utilisent -- non pas que cela ressemble à une chose sensée à faire dans cet exemple particulier, mais qui sait.

36
répondu Thomas Wouters 2010-10-22 10:09:18

au lieu de:

self.status = VAGUE

celui-ci:

self.status = Foo.VAGUE

vous devez spécifier la classe

7
répondu Matus 2010-10-22 09:42:28

la seule façon est d'y accéder par le nom de classe tel que

Foo.VAGUE

si l'accès est VAGUE à l'intérieur du __init__ fonction, ou une fonction, elle doit être déclarée à l'intérieur de celle-ci pour y accéder comme vous le souhaitez.

utiliser self est aussi pour l'instance de la classe.

3
répondu Iacks 2010-10-22 10:06:28

celui-ci N'est pas recommandé pour N'importe quel CODE par n'importe quel moyen, mais un hack laid comme ci-dessous peut être fait. J'ai fait cela juste pour avoir une meilleure compréhension de L'API Python AST, donc quiconque utilise ce code dans le monde réel devrait être abattu avant qu'il ne fasse le moindre mal: -)

#!/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()
3
répondu Taisuke Yamada 2011-07-12 13:47:24

en Python3, vous pouvez aussi faire référence à VAGUE comme:

type(self).VAGUE

de cette façon, vous le référencez clairement comme un attribut de classe et non un attribut d'objet, mais cette façon est robuste contre un changement de nom de la classe. Aussi si vous outrepassez VAGUE dans une sous-classe, la valeur de la sous-classe qui sera utilisée, tout comme si vous étiez à utiliser self.VAGUE.

Notez que cette méthode ne semble pas fonctionner en Python2, au moins pas dans mes tests, où type(self) retourné instance au lieu de la classe j'ai instancié. Par conséquent, la réponse de Thomas Wouters est probablement préférable, compte tenu de L'étendue de Python2.

3
répondu Zeust the Unoobian 2015-03-27 10:05:39