Определение констант в классе Python, действительно ли нужно self? - PullRequest
25 голосов
/ 22 октября 2010

Я хочу определить набор констант в классе, например:

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 и т. Д .?

Ответы [ 5 ]

36 голосов
/ 22 октября 2010

Когда вы присваиваете имена в теле класса, вы создаете атрибуты класса.Вы не можете ссылаться на них, не обращаясь к классу ни прямо, ни косвенно.Вы можете использовать Foo.VAGUE, как говорят другие ответы, или вы можете использовать self.VAGUE.Вам не нужно присваивать атрибутам self.

Обычно, использование self.VAGUE - это то, что вам нужно, поскольку он позволяет подклассам переопределять атрибут без необходимости переопределения всех методов.которые используют их - не то, что кажется разумным в этом конкретном примере, но кто знает.

7 голосов
/ 22 октября 2010

попробуйте вместо:

self.status = VAGUE

вот этот:

self.status = Foo.VAGUE

Вы ДОЛЖНЫ указать класс

3 голосов
/ 27 марта 2015

В Python3 вы также можете ссылаться на VAGUE как:

type(self).VAGUE

Таким образом, вы явно ссылаетесь на него как на атрибут класса, а не на атрибут объекта, но этот способ устойчив к изменению имени класса. Также, если вы переопределите VAGUE в подклассе, будет использоваться значение из подкласса, как если бы вы использовали self.VAGUE.

Обратите внимание, что этот метод не работает в Python2, по крайней мере, в моих тестах, где type(self) возвратил instance вместо класса, который я создал. Поэтому ответ Томаса Воутерса, вероятно, предпочтительнее, учитывая, насколько широко распространен Python2.

3 голосов
/ 12 июля 2011

Этот НЕ РЕКОМЕНДУЕТСЯ ДЛЯ ЛЮБОГО КОДА каким-либо образом, но можно сделать уродливый взлом, как показано ниже. Я сделал это просто для лучшего понимания 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()
3 голосов
/ 22 октября 2010

Единственный способ - получить к нему доступ через имя класса, например

Foo.VAGUE

. При доступе только к VAGUE внутри функции __init__ или функции, она должна быть объявлена ​​внутри нее для доступатак, как вы хотите.

Использование self также относится к экземпляру класса.

...