Определить константы в классе python, действительно ли нужно?
Я хочу определить набор констант в классе, например:
class Foo(object):
(NONEXISTING,VAGUE,CONFIRMED) = (0,1,2)
def __init__(self):
self.status = VAGUE
Однако я получаю
NameError: global name 'VAGUE' is not defined
Есть ли способ определения этих констант для видимости внутри класса, не прибегая к global
или self.NONEXISTING = 0
и т.д.?
Ответы
Ответ 1
Когда вы назначаете имена в классе, вы создаете атрибуты класса. Вы не можете обращаться к ним, не обращаясь к классу прямо или косвенно. Вы можете использовать Foo.VAGUE
, как говорят другие ответы, или вы можете использовать self.VAGUE
. Вам не нужно назначать атрибуты self
.
Обычно использование self.VAGUE
- это то, что вы хотите, потому что оно позволяет подклассам переопределять атрибут без необходимости переопределять все используемые им методы - не то, что кажется в этом конкретном примере разумным, но кто знает.
Ответ 2
попробуйте вместо:
self.status = VAGUE
этот:
self.status = Foo.VAGUE
вы ДОЛЖНЫ указать класс
Ответ 3
Единственный способ получить доступ к нему через имя класса, например
Foo.VAGUE
Если вы используете только VAGUE внутри функции __init__
или функцию, она должна быть объявлена внутри, чтобы получить доступ к ней так, как вы хотите.
Использование self также для экземпляра класса.
Ответ 4
Этот НЕ РЕКОМЕНДУЕТСЯ ДЛЯ ЛЮБОГО КОДА любым способом, но может быть сделан уродливый взлом, как показано ниже.
Я сделал это просто, чтобы лучше понять API Python AST, поэтому каждый, кто использует это в реальном коде, должен быть застрелен до того, как он навредит: -)
#!/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()
Ответ 5
В Python3 вы также можете ссылаться на VAGUE
как:
type(self).VAGUE
Таким образом, вы явно ссылаетесь на него как на атрибут класса, а не на атрибут объекта, но этот способ устойчив к изменению имени класса. Также, если вы переопределите VAGUE
в подклассе, будет использоваться значение из подкласса, как если бы вы использовали self.VAGUE
.
Обратите внимание, что этот метод не работает в Python2, по крайней мере, не в моих тестах, где type(self)
возвратил instance
вместо экземпляра класса I. Поэтому ответ Томаса Уутерса, вероятно, предпочтительнее, учитывая, насколько распространен Python2.