web-dev-qa-db-fra.com

Définir des constantes dans la classe python, l'auto est-il 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, je reçois

NameError: global name 'VAGUE' is not defined

Existe-t-il un moyen de définir ces constantes pour qu'elles soient visibles à l'intérieur de la classe sans recourir à global ou self.NONEXISTING = 0 etc.?

27
Theodor

Lorsque vous attribuez des noms dans le corps de la 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 le disent les autres réponses, ou vous pouvez utiliser self.VAGUE. Vous n'avez pas à assigner aux attributs de self.

Habituellement, en utilisant self.VAGUE est ce que vous voulez car il permet aux sous-classes de redéfinir l'attribut sans avoir à réimplémenter toutes les méthodes qui les utilisent - pas que cela semble être une chose sensée à faire dans cet exemple particulier, mais qui sait.

36
Thomas Wouters

essayez au lieu de:

self.status = VAGUE

celui-là:

self.status = Foo.VAGUE

vous DEVEZ spécifier la classe

7
Matus

En Python3, vous pouvez également référencer VAGUE comme:

type(self).VAGUE

De cette façon, vous le faites clairement référence en tant qu'attribut de classe et non en tant qu'attribut d'objet, mais cette manière est robuste contre un changement de nom de la classe. De plus, si vous remplacez VAGUE dans une sous-classe, la valeur de la sous-classe sera utilisée, comme si vous deviez utiliser self.VAGUE.

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

3
Zeust the Unoobian

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

Foo.VAGUE

Si vous accédez uniquement à VAGUE dans le __init__ fonction, ou une fonction, il faut la déclarer à l'intérieur pour y accéder comme vous le souhaitez.

Utiliser self est également pour l'instance de la classe.

3
Iacks

Celui-ci n'est PAS RECOMMANDÉ POUR TOUT CODE par aucun moyen, mais un hack laid comme ci-dessous peut être fait. J'ai fait cela juste pour avoir une meilleure compréhension de Python AST API, donc toute personne qui l'utilise dans du code réel devrait être abattue avant de faire du 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
Taisuke Yamada