Переменные класса того же типа в Python - PullRequest
11 голосов
/ 06 декабря 2010

Возиться с типичным примером класса Point при изучении 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. Прямо сейчас я полагаюсь на переменные уровня модуля, и мне не нравится это решение. Кроме того, есть ли причина, почему это нельзя сделать в теле класса?

Ответы [ 4 ]

11 голосов
/ 06 декабря 2010
class Point(object):
  pass

Point.ORIGIN = Point()
6 голосов
/ 06 декабря 2010

Назначьте его по факту:

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


Point.ORIGIN = Point()
4 голосов
/ 06 декабря 2010

Вы не можете создать экземпляр класса, пока этот класс фактически не будет создан, то есть после того, как тело класса будет оценено (примечание: он выполняется как обычный код 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. Так что вы, вероятно, просто хотите сделать это, как показано на рисунке:)

2 голосов
/ 06 декабря 2010

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

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__() класса косвенно через декоратор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...