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.?
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.
au lieu de:
self.status = VAGUE
celui-ci:
self.status = Foo.VAGUE
vous devez spécifier la classe
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.
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()
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.