Подтвердить что ты не робот

Должен ли я использовать класс или словарь?

У меня есть класс, который содержит только поля и никакие методы, например:

class Request(object):

    def __init__(self, environ):
        self.environ = environ
        self.request_method = environ.get('REQUEST_METHOD', None)
        self.url_scheme = environ.get('wsgi.url_scheme', None)
        self.request_uri = wsgiref.util.request_uri(environ)
        self.path = environ.get('PATH_INFO', None)
        # ...

Это легко может быть переведено в dict. Класс более гибкий для будущих дополнений и может быть быстрым с __slots__. Так будет ли польза от использования dict? Будет ли диктовать быстрее, чем класс? И быстрее, чем класс с слотами?

4b9b3361

Ответ 1

Зачем ты сделал это словарь? В чем преимущество? Что произойдет, если позже вы захотите добавить код? Куда пойдет ваш код __init__?

Классы предназначены для связывания связанных данных (и обычно кода).

Словари предназначены для хранения отношений ключ-значение, где обычно ключи имеют одинаковый тип, а все значения также относятся к одному типу. Иногда они могут быть полезны для объединения данных, когда имена ключей/атрибутов не все известны заранее, но часто это признак того, что что-то не так с вашим дизайном.

Держите это в классе.

Ответ 2

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

>>> from collections import namedtuple
>>> request = namedtuple("Request", "environ request_method url_scheme")
>>> request
<class '__main__.Request'>
>>> request.environ = "foo"
>>> request.environ
'foo'

Различия в производительности здесь будут минимальными, хотя я был бы удивлен, если бы словарь не был быстрее.

Ответ 3

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

  • Вся ваша логика живет в одной функции
  • Легко обновляется и остается инкапсулированным
  • Если вы измените что-нибудь позже, вы можете легко поддерживать интерфейс тем же самым

Ответ 4

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

Я сравнивал время, необходимое для создания и изменения переменной в классе dict, new_style и классе new_style с слотами.

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

import timeit

class Foo(object):

    def __init__(self):

        self.foo1 = 'test'
        self.foo2 = 'test'
        self.foo3 = 'test'

def create_dict():

    foo_dict = {}
    foo_dict['foo1'] = 'test'
    foo_dict['foo2'] = 'test'
    foo_dict['foo3'] = 'test'

    return foo_dict

class Bar(object):
    __slots__ = ['foo1', 'foo2', 'foo3']

    def __init__(self):

        self.foo1 = 'test'
        self.foo2 = 'test'
        self.foo3 = 'test'

tmit = timeit.timeit

print 'Creating...\n'
print 'Dict: ' + str(tmit('create_dict()', 'from __main__ import create_dict'))
print 'Class: ' + str(tmit('Foo()', 'from __main__ import Foo'))
print 'Class with slots: ' + str(tmit('Bar()', 'from __main__ import Bar'))

print '\nChanging a variable...\n'

print 'Dict: ' + str((tmit('create_dict()[\'foo3\'] = "Changed"', 'from __main__ import create_dict') - tmit('create_dict()', 'from __main__ import create_dict')))
print 'Class: ' + str((tmit('Foo().foo3 = "Changed"', 'from __main__ import Foo') - tmit('Foo()', 'from __main__ import Foo')))
print 'Class with slots: ' + str((tmit('Bar().foo3 = "Changed"', 'from __main__ import Bar') - tmit('Bar()', 'from __main__ import Bar')))

И вот результат...

Создание...

Dict: 0.817466186345
Class: 1.60829183597
Class_with_slots: 1.28776730003

Изменение переменной...

Dict: 0.0735140918748
Class: 0.111714198313
Class_with_slots: 0.10618612142

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

Примечание:

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

Также время создания и изменения переменных может отличаться на вашем компьютере, так как мой старый и медленный. Убедитесь, что вы сами проверите его, чтобы увидеть "реальные" результаты.

Изменить:

Я позже тестировал namedtuple: я не могу его изменить, но для создания 10000 образцов (или что-то в этом роде) потребовалось 1,4 секунды, поэтому словарь действительно самый быстрый.

Если я изменить функцию dict, чтобы включить ключи и значения и вернуть dict вместо переменной, содержащей dict, когда я ее создаю, она дает мне 0,65 вместо 0,8 секунды.

class Foo(dict):
    pass

Создание похоже на класс со слотами, и изменение переменной является самым медленным (0,17 секунды), поэтому не используют эти классы. перейти на dict (скорость) или для класса, полученного из объекта ( "синтаксическая конфета" )

Ответ 5

Я согласен с @adw. Я бы никогда не представлял "объект" (в смысле ОО) со словарем. Словари объединяют пары имени/значения. Классы представляют объекты. Я видел код, где объекты представлены со словарями, и неясно, какова фактическая форма вещи. Что происходит, когда определенного имени/значений нет? Что ограничивает клиента от чего-то вообще. Или пытаться получить что-нибудь вообще. Форма вещи всегда должна быть четко определена.

При использовании Python важно строить с дисциплиной, так как язык позволяет многим авторам стрелять в ногу.

Ответ 6

Я бы рекомендовал класс, так как это вся информация, связанная с запросом. Если бы я использовал словарь, я бы ожидал, что данные, которые будут храниться, будут гораздо более похожими по своей природе. Руководящий принцип, который я обычно придерживаюсь, заключается в том, что если я захочу перебрать весь набор пар ключ- > значение и сделать что-то, я использую словарь. В противном случае данные, по-видимому, имеют гораздо большую структуру, чем базовое отображение ключа- > значение, что означает, что класс, скорее всего, станет лучшей альтернативой.

Следовательно, придерживайтесь класса.

Ответ 7

Может быть возможно иметь свой пирог и съесть это также. Другими словами, вы можете создать нечто, обеспечивающее функциональность как экземпляра класса, так и словаря. См. рецепт ActiveState Dɪᴄᴛɪᴏɴᴀʀʏ ᴡɪᴛʜ ᴀᴛᴛʀɪʙᴜᴛᴇ -s ᴛʏʟᴇ ᴀᴄᴄᴇss и комментарии о том, как это сделать.

Если вы решите использовать обычный класс, а не подкласс, я обнаружил, что рецепт Tʜᴇ sɪᴍᴘʟᴇ ʙᴜᴛ ʜᴀɴᴅʏ "ᴄᴏʟʟᴇᴄᴛᴏʀ ᴏғ ᴀ ʙᴜɴᴄʜ ɴᴀᴍᴇᴅ ɴᴀᴍᴇᴅ sᴛᴜғғ" ᴄʟᴀss (автор Алекс Мартелли) очень гибкий и полезный для такого рода вещей это выглядит так, как будто вы делаете (т.е. создаете относительно простой агрегатор информации). Поскольку это класс, вы можете легко расширить его функциональность, добавив методы.

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

Обновление

Класс object (у которого нет __dict__) подкласса с именем SimpleNamespace (у которого он есть) был добавлен в types Модуль Python 3.3 и еще одна альтернатива.

Ответ 8

Если все, что вы хотите достичь, это синтаксическая конфета, например obj.bla = 5 вместо obj['bla'] = 5, особенно если вам нужно много повторить, возможно, вам захочется использовать какой-то простой контейнерный класс, как в предложении martineaus. Тем не менее, код там довольно раздутый и излишне медленный. Вы можете сохранить его простым:

class AttrDict(dict):
    """ Syntax candy """
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

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

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

Ответ 9

class ClassWithSlotBase:
    __slots__ = ('a', 'b',)

def __init__(self):
    self.a: str = "test"
    self.b: float = 0.0


def test_type_hint(_b: float) -> None:
    print(_b)


class_tmp = ClassWithSlotBase()

test_type_hint(class_tmp.a)

Я рекомендую класс. Если вы используете класс, вы можете получить подсказку типа, как показано на рисунке. И поддержка класса автоматически завершается, когда класс является аргументом функции.

enter image description here