Defining constants in python class, is self really needed?
Question:
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.?
Answers:
try instead of:
self.status = VAGUE
this one:
self.status = Foo.VAGUE
you MUST specify the class
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.
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.
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()
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.
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.?
try instead of:
self.status = VAGUE
this one:
self.status = Foo.VAGUE
you MUST specify the class
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.
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.
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()
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.