класс __init__ (не экземпляр __init__) - PullRequest
8 голосов
/ 25 апреля 2010

Вот очень простой пример того, что я пытаюсь обойти:

class Test(object):
    some_dict = {Test: True}

Проблема в том, что я не могу обратиться к тесту, пока он еще определяется

Обычно, я бы просто сделал это:

class Test(object):
    some_dict = {}
    def __init__(self):
        if self.__class__.some_dict == {}:
            self.__class__.some_dict = {Test: True}

Но я никогда не создаю экземпляр этого класса. Это действительно просто контейнер для хранения группы связанных функций и данных (у меня есть несколько таких классов, и я передаю ссылки на них, поэтому необходимо, чтобы Test был его собственным классом)

Итак, мой вопрос: как я могу сослаться на Test во время его определения или есть что-то похожее на __init__, которое вызывается, как только класс определен? Если возможно, я хочу, чтобы self.some_dict = {Test: True} оставался внутри определения класса. Пока это единственный способ, которым я знаю, как это сделать:

class Test(object):
    @classmethod
    def class_init(cls):
        cls.some_dict = {Test: True}
Test.class_init()

Ответы [ 5 ]

12 голосов
/ 25 апреля 2010

Класс на самом деле не существует, пока он определен. Оператор class работает так, что тело оператора выполняется в виде блока кода в отдельном пространстве имен. В конце выполнения это пространство имен передается метаклассу (например, type), и метакласс создает класс, используя пространство имен в качестве пространства атрибутов.

Из вашего описания, он не звучит так, чтобы Test был классом. Похоже, это должен быть модуль . some_dict является глобальным - даже если это атрибут класса, в вашей программе есть только один такой атрибут, так что он ничуть не лучше, чем глобальный - и любые методы класса, которые есть в классе, могут быть просто функциями. *

Если вы действительно хотите, чтобы это был класс, у вас есть три варианта: установить dict после определения класса:

class Test:
    some_dict = {}
Test.some_dict[Test] = True

Используйте декоратор класса (в Python 2.6 или новее):

def set_some_dict(cls):
    cls.some_dict[cls] = True

@set_some_dict
class Test:
    some_dict = {}

Или используйте метакласс:

class SomeDictSetterType(type):
    def __init__(self, name, bases, attrs):
        self.some_dict[self] = True
        super(SomeDictSetterType, self).__init__(name, bases, attrs)

class Test(object):
    __metaclass__ = SomeDictSetterType
    some_dict = {}
4 голосов
/ 25 апреля 2010

Вы можете добавить атрибут some_dict после определения основного класса.

class Test(object):
  pass
Test.some_dict = {Test: True}
1 голос
/ 07 июня 2012

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

(странная) альтернатива тому, что предлагали другие: вы могли бы использовать __new__:

class Test(object):
    def __new__(cls):
        cls.some_dict = {cls: True}

Test()

Вы можете даже __new__ вернуть ссылку на класс и использовать декоратор для его вызова:

def instantiate(cls):
    return cls()

@instantiate
class Test(object):
    def __new__(cls):
        cls.some_dict = {cls: True}
        return cls
0 голосов
/ 07 июня 2012

Первый пример Томаса очень хорош, но вот более Pythonic способ сделать то же самое.

class Test:
    x = {}
    @classmethod
    def init(cls):
        # do whatever setup you need here
        cls.x[cls] = True
Test.init()
0 голосов
/ 26 апреля 2010

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

def Meta(name, bases, ns):
    klass = type(name, bases, ns)
    setattr(klass, 'some_dict', { klass: True })
    return klass

class Test(object):
    __metaclass__ = Meta

print Test.some_dict
...