вызов класса / статического метода из переменной класса в python - PullRequest
7 голосов
/ 15 февраля 2011

Я пытаюсь заставить класс ImageLoader обрабатывать загрузку и обработку ресурсов изображения следующим образом:

class ImageLoader:
    TileTable = __loadTileTable('image path', some other variable)

    @staticmethod
    def _loadTileTable(arg1, arg2):
        blah blah

однако при компиляции я получаю: NameError: name '_loadTileTable' is not defined

Если заменить вторую строку на TileTable = ImageLoader.__loadTileTable('image path', some other variable), тогда я получу NameError: name 'ImageLoader' is not defined

Когда я перехожу с C # на Python, статические классы со статическими методами - это то, что я бы использовал для реализации этого. Тем не менее, я открыт для того, чтобы сделать это в целом в Python (то есть вызывать статические библиотечные функции, которые сгруппированы только по функциональности).

UPDATE: Прочитав оба ответа, я получаю картину, что то, что я пытаюсь сделать, вероятно, неправильно. Как бы я реализовал ImageLoader, чтобы я мог сделать это:

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

module1.py

aTile = ImageLoader.TileTable[1]

module2.py

anotherTile = ImageLoader.TileTable[2]

в идеале я бы заполнил TileTable только один раз.

Обновление:

Спасибо за все ответы, я нашел свой последний ответ на заполнение TileTable только один раз в модулях python doco

"Модуль может содержать исполняемый файл заявления, а также функции определения. Эти заявления предназначен для инициализации модуля. Они исполняются только в первый раз модуль импортируется куда-то "

Что касается статического класса, я собираюсь отказаться от классов и просто сделать переменную уровня модуля.

Ответы [ 4 ]

5 голосов
/ 15 февраля 2011

Отвечая только на обновленный вопрос, в Python вы должны сделать TileTable переменную с именем tile_table в модуле с именем imageloader. Нет никакой причины помещать все это в класс.

Итак, вы получите:

module1.py

import imageloader
aTile = imageloader.tile_table[1]

module2.py

import imageloader
anotherTile = imageloader.tile_table[2]

и imageload.py выглядит примерно так:

def _loadTileTable(arg1, arg2):
    pass # blah blah
tile_table = _loadTileTable('image path', other_var)

Думайте о модуле Python как об одном экземпляре на других языках (который на самом деле есть), и вы сможете согласовать это с любыми предрассудками ОО, унаследованными от других языков.

3 голосов
/ 16 февраля 2011

Переменные уровня класса, которые обновляются, - это плохо, плохо. Мы ожидаем по умолчанию, что объект экземпляры с состоянием и классы без состояния.

В этом случае мы пытаемся «волшебным образом» инициализировать коллекцию как переменную класса, что является крайне плохой идеей. Коллекция - это просто объект с простыми атрибутами уровня экземпляра.

Магическая таблица плиток не должна быть скрытой статической частью ImageLoader. Там нет возможных причин для этого. Это должен быть аргумент для ImageLoader, если вы хотите избежать его загрузки более одного раза.

Их разделение способствует тестируемости. Это не произвольно. Это как юнит тестирование.

То, что вы хотите, это.

class ImageLoader( object ):
    def __init__( self, theTileTable ):
        self.tile_table= theTileTable

class TileTable( object ):
    def __init__( self, path, some_other_arg ):
         self.tileTable= self._loadTileTable( path, some_other_arg )
    def _loadTileTable(arg1, arg2):
         blah blah

Нет ничего статичного. Независимые подразделения. Более легко проверяется. Никаких странных зависимостей. Нет магии.

3 голосов
/ 15 февраля 2011

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

TileTable = _loadTileTable(arg1, arg2)
@staticmethod
def _loadTileTable(arg1, arg2):
    pass # blah blah
ImageLoader = type('ImageLoader', (), {'TileTable': TileTable, '_loadTileTable': _loadTileTable})
del TileTable
del _loadTileTable

Как вы можете видеть, вызов _loadTileTable появляется перед его определением.В вашем примере в определении класса вызов _loadTileTable должен идти после определения _loadTileTable.

Одно из возможных исправлений - просто переупорядочить определение класса.

class ImageLoader:
    def _loadTileTable(arg1, arg2):
        pass # blah, blah

    TileTable = _loadTileTable('image path', other_var)

Примечаниечто я удалил «staticmethod», потому что в точке, где вызывается _loadTileTable, он вызывается как функция, а не как метод.Если вы действительно хотите, чтобы он был доступен после инициализации класса, вы можете определить его как статический метод после факта.

class ImageLoader:
    def _loadTileTable(arg1, arg2):
        pass # blah, blah

    TileTable = _loadTileTable('image path', other_var)

    _loadTileTable = staticmethod(_loadTileTable)
2 голосов
/ 15 февраля 2011

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

Но, если вы сделаете это, вы получите новую ошибку:

NameError: name 'arg1' is not defined

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

Итак, правильный способ сделать это - перегрузить метод __init__(), чтобы присвоение TileTable происходило только при создании класса:

class ImageLoader(object):
    def __init__(self, arg1, arg2):
        self.TileTable = self._loadTileTable(arg1, arg2)

    @staticmethod
    def _loadTileTable(arg1, arg2):
        print arg1, arg2

Это дает вам возможность вызывать ImageLoader._loadTileTable() без экземпляра, но также позволяет создавать переменную TileTable экземпляра при создании экземпляра.

Использование метода класса
В ответ на мой комментарий о возможной необходимости использования метода класса, вот пример, который охватывает это:

class ImageLoader:
    @classmethod
    def _loadTileTable(cls, arg1, arg2):
        return arg1, arg2

# We're creating the class variable outside of the class definition. If you're doing 
# this in a module, no one will ever notice.
ImageLoader.TileTable = ImageLoader._loadTileTable('foo', 'bar')

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

>>> i = ImageLoader()
>>> i
<__main__.ImageLoader instance at 0x100488f80>
>>> ImageLoader.TileTable
('foo', 'bar')
>>> i.TileTable
('foo', 'bar')

Там у вас есть экземпляр i, у которого есть доступ к переменной класса, но ImageLoader.TileTable все еще доступен из объекта класса без необходимости в экземпляре.

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