2013-02-22 6 views
11

Когда и как статические методы предполагают использовать в python? Мы уже установили метод класса, поскольку по возможности следует избегать использования фабричного метода для создания экземпляра объекта. Другими словами, не рекомендуется использовать методы класса в качестве альтернативного конструктора (см. Factory method for python object - best practice).Использование статических методов в python - лучшая практика

Допустим, у меня есть класс, используемый для представления данных сущности в базе данных. Представьте, что это объект dict, содержащий имена полей и значения полей, а одно из полей - идентификационный номер, который делает данные уникальными.

class Entity(object): 
    def __init__(self, data, db_connection): 
     self._data = data 
     self._db_connection 

Вот мой __init__ метод принимает данные сущности dict объект. Допустим, у меня есть только идентификационный номер, и я хочу создать экземпляр Entity. Сначала мне нужно будет найти остальную часть данных, а затем создать экземпляр моего объекта Entity. Из моего предыдущего вопроса мы установили, что, по возможности, следует избегать использования метода класса как фабричного метода.

class Entity(object): 

    @classmethod 
    def from_id(cls, id_number, db_connection): 
     filters = [['id', 'is', id_number]] 
     data = db_connection.find(filters) 
     return cls(data, db_connection) 

    def __init__(self, data, db_connection): 
     self._data = data 
     self._db_connection 


# Create entity 
entity = Entity.from_id(id_number, db_connection) 

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

class Entity(object): 

    @staticmethod 
    def data_from_id(id_number, db_connection): 
     filters = [['id', 'is', id_number]] 
     data = db_connection.find(filters) 
     return data 


# Create entity 
data = Entity.data_from_id(id_number, db_connection) 
entity = Entity(data) 

Или имеет смысл использовать автономную функцию для поиска данных сущности из идентификационного номера.

def find_data_from_id(id_number, db_connection): 
    filters = [['id', 'is', id_number]] 
    data = db_connection.find(filters) 
    return data 


# Create entity. 
data = find_data_from_id(id_number, db_connection) 
entity = Entity(data, db_connection) 

Примечание: Я не хочу, чтобы изменить свой __init__ метод. Раньше люди предлагали сделать мой метод __init__ похожим на это __init__(self, data=None, id_number=None), но может быть 101 способ найти данные сущности, поэтому я бы предпочел сохранить эту логику в некоторой степени. Имеют смысл?

+0

Название этого вопроса и вступительное предложение звучат так, как будто вы копаете по причинам использования языковых функций, вместо того, чтобы пытаться выяснить правильный способ написания кода, который вы хотите. Я не говорю, что это то, что вы делаете (фактическое тело вашего вопроса определенно является реальным, действительным вопросом). Просто вы должны немного подумать о своей фразировке. Таким образом, вы можете получать ответы только от людей, которым нравится обсуждать языковые функции, а не намного больше людей, которые пишут хороший Python. – abarnert

ответ

6

Ваш первый пример имеет для меня наибольший смысл: Entity.from_id довольно краткий и понятный.

Он избегает использования data в следующих двух примерах, которые не описывают, что возвращается; data используется для построения Entity. Если вы хотите быть конкретным относительно data, который используется для построения Entity, то вы можете назвать свой метод чем-то вроде Entity.with_data_for_id или эквивалентной функцией entity_with_data_for_id.

Использование глагола, такого как find, также может быть довольно запутанным, так как оно не дает никаких указаний на возвращаемое значение - какова функция, которая должна выполняться, когда она найдена данными? (Да, я понимаю, str есть метод find, не будет ли лучше по имени index_of Но есть также index ...?) Это напоминает мне классический:

find x

Я всегда стараюсь думать какое имя будет указывать на кого-то (а) без знания системы и (б) знание других частей системы - не сказать, что я всегда успешный!

+1

Хороший пункт о поиске. Это похоже на старые приключения Скотта Адамса, где, поскольку основная головоломка заключалась в том, чтобы выяснить, какие существительные должны дать универсальному «использованию» глагола. (В игре Infocom вы должны «взять веревку с катушки, привязать ее веревку к крюку, а затем спуститься вниз по веревке», в игре SA вы «ИСПОЛЬЗУЙТЕ СПОЙ. ИСПОЛЬЗУЙТЕ КРЮ. ИСПОЛЬЗУЙТЕ ВЕРЕВКУ».) – abarnert

10

Ответ на связанный вопрос конкретно говорит, что это:

@classmethod является идиоматическим способом сделать «альтернативный конструктор» -есть примеры во всем STDLIB-itertools.chain.from_iterable, DateTime .datetime.fromordinal и т. д.

Так что я не знаю, как вы поняли, что использование метода класса по своей сути является плохим. Мне действительно нравится идея использования classmethod в вашей конкретной ситуации, поскольку она делает следующий код и использует api easy.

Альтернативой было бы использовать аргументы конструктора по умолчанию так:

class Entity(object): 
    def __init__(self, id, db_connection, data=None): 
     self.id = id 
     self.db_connection = db_connection 
     if data is None: 
      self.data = self.from_id(id, db_connection) 
     else: 
      self.data = data 

    def from_id(cls, id_number, db_connection): 
     filters = [['id', 'is', id_number]] 
     return db_connection.find(filters) 

Я предпочитаю версию classmethod, что вы написали изначально, однако. Тем более, что data довольно неоднозначный.

16

Когда и как статические методы предполагают использовать в python?

Ответ glib: Не очень часто.

Даже мерцание, но не совсем как бесполезный ответ: когда они делают ваш код более читаемым.


Во-первых, давайте крюк the docs:

Статические методы в Python аналогичны тем, которые содержатся в Java или C++. Также см. classmethod() для варианта, который полезен для создания альтернативных конструкторов классов.

Итак, когда вам нужен статический метод в C++, вам нужен статический метод в Python, правильно?

Ну, нет.

В Java нет функций, просто методов, поэтому вы создаете псевдоклассы, которые являются просто связями статических методов. Способ сделать то же самое в Python - просто использовать свободные функции.

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

C++ не имеет того же ограничения, что и Java, но многие стили C++ довольно похожи. (С другой стороны, если вы программист «Современный C++», который усвоил «свободные функции являются частью интерфейса класса», идиоты, ваши инстинкты для «где статические методы полезны», вероятно, довольно приличные для Python.)


Но если вы пришли на это из первых принципов, а не из другого языка, есть более простой способ смотреть на вещи:

@staticmethod в основном только глобальная функция.Если у вас есть функция foo_module.bar(), которая будет более читаема по какой-либо причине, если она была написана как foo_module.BazClass.bar(), сделайте ее @staticmethod. Если нет, не делайте этого. Это действительно все, что нужно. Единственная проблема заключается в создании ваших инстинктов для того, что более читаемо для идиоматического программиста Python.

И, конечно, используйте @classmethod, если вам нужен доступ к классу, но конструкторы с альтернативными экземплярами - это парадигма для этого, как подразумевают документы. Хотя вы часто можете моделировать @classmethod с помощью @staticmethod, просто явно ссылаясь на класс (особенно если у вас нет большого количества подклассов), вы не должны.


Наконец, получив на свой конкретный вопрос:

Если единственная причина, клиенты никогда не должны искать данные по идентификатору, чтобы построить Entity, который звучит как деталь реализации, вы не должны быть обнажая , а также делает клиентский код более сложным. Просто используйте конструктор. Если вы не хотите изменять свой __init__ (и вы правы, что есть веские причины, по которым вы, возможно, не захотите), используйте @classmethod в качестве альтернативного конструктора: Entity.from_id(id_number, db_connection).

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

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