2010-12-06 10 views
7

Прослушивание типичного примера класса Point при изучении Python, я заметил, что по какой-то причине у меня не может быть уровня класса (статическая переменная) того же типа, что и класс. Например.Переменные класса одного и того же типа в Python

class Point: 

    ORIGIN = Point() # doesn't work 

    def __init__(self, x=0, y=0): 
    self.x = x 
    self.y = y 

в то время как одни и те же работы в Java:

class Point { 

    public static void main(final String[] args) { } 

    private static final Point ORIGIN = new Point(0, 0); 

    private int x; 

    private int y; 

    public Point(int x, int y) { 
    this.x = x; 
    this.y = y; 
    } 

} 

Вопрос заключается в том: есть ли способ достичь того же в Python. Сейчас я полагаюсь на переменные уровня модуля, и мне не нравится это решение. Кроме того, есть ли причина, почему это невозможно сделать в теле класса?

+2

«Сейчас я полагаюсь на переменные уровня модуля, и мне не нравится это решение» Почему бы и нет? Глобалы на уровне модулей делают намного больше смысла, чем странные статические конечные переменные, скрытые внутри определений классов. Что случилось с более простым решением? – 2010-12-06 19:17:27

+0

@ S.Lott: аргументация состоит в том, что ORIGIN * не * принадлежит к любому модулю. Если, например, Я решил перенести этот класс на другой модуль, так как ссылка ORIGIN всегда остается неизменной. – sasuke 2010-12-06 19:42:23

+0

put ORIGIN - это экземпляр Point и принадлежит к тому же самому модулю. Если вы переместите один, почему бы вам не переместить другого? – 2010-12-06 19:56:00

ответ

4

Вы не можете создать экземпляр класса, пока этот класс не будет создан, который после того, как тело класса будет оценено (обратите внимание: оно выполняется как обычный код Python).

То же самое касается вашего примера Java: ClassLoader создает класс Point, а затем выполняет код из полей static.

Грубый эквивалент загрузчика классов в Python является метаклассом, так что вы могли бы сделать что-то вроде этого:

def class_with_static(name, bases, body): 
    static_block = body.pop("__static__", None) 
    klass = type(name, bases, body) 
    if static_block: 
     static_block(klass) 
    return klass 

class Point(object): 
    __metaclass__ = class_with_static 

    def __static__(cls): 
     cls.ORIGIN = cls() 

    def __init__(self, x=0, y=0): 
     self.x = x 
     self.y = y 

assert isinstance(Point.ORIGIN, Point) 
assert Point.ORIGIN.x == Point.ORIGIN.y == 0 
assert not hasattr(Point, "__static__") 

Конечно, это будет иметь некоторые другие последствия, как: все подклассы Point будет иметь ORIGIN собственный атрибут. Так что вы, вероятно, просто хотите сделать это, как показано другими.

8
class Point(object): 
    pass 

Point.ORIGIN = Point() 
5

Назначают его постфактум:

class Point: 
    def __init__(self, x=0, y=0): 
    self.x = x 
    self.y = y 


Point.ORIGIN = Point() 
2

Вы можете сделать это с классом декоратора, хотя я не уверен, что хорошее название для него было бы. Вот как:

def add_class_var(name, *args, **kwrds): 
    def decorator(cls): 
     setattr(cls, name, cls(*args, **kwrds)) 
     return cls 
    return decorator 

@add_class_var('ORIGIN') 
class Point: 
    def __init__(self, x=0, y=0): 
     self.x = x 
     self.y = y 

print Point.ORIGIN, Point.ORIGIN.x, Point.ORIGIN.y 
# <__main__.Point instance at 0x00B5B418> 0 0 

Хотя не используется в коде выше, вы также можете передать аргументы __init__() конструктора класса косвенно через декоратор.

Смежные вопросы