Czy powinienem używać klasy czy słownika?

Mam klasę, która zawiera tylko pola i żadnych metod, jak to:

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)
        # ...

Można to łatwo przetłumaczyć na dict. Klasa jest bardziej elastyczna dla przyszłych dodatków i może być szybka dzięki __slots__. Czy zamiast tego można skorzystać z dict? Czy dict byłby szybszy od klasy? I szybciej niż klasa z automatami?

Author: martineau, 2010-10-28

9 answers

Dlaczego, u licha, zrobiłeś z tego słownik? Jaka jest przewaga? Co się stanie, jeśli później chcesz dodać kod? Gdzie by poszedł twój __init__ kod?

Klasy służą do łączenia powiązanych danych (i zazwyczaj kodu).

Słowniki służą do przechowywania relacji klucz-wartość, gdzie zazwyczaj klucze są tego samego typu, a wszystkie wartości są również jednego typu. Czasami mogą być przydatne do łączenia danych, gdy nazwy kluczy/atrybutów nie są znane z góry, ale często to znak, że coś jest nie tak z Twoim projektem.

Zachowaj klasę.

 21
Author: adw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 17:18:27

Użyj słownika, chyba że potrzebujesz dodatkowego mechanizmu klasy. Możesz również użyć namedtuple dla podejścia hybrydowego:

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

Różnice w wydajności tutaj będą minimalne, chociaż byłbym zaskoczony, gdyby słownik nie był szybszy.

 32
Author: Katriel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 16:47:02

Klasa w Pythonie to dict pod spodem. Masz trochę na głowie z zachowaniem klasy, ale nie będziesz w stanie tego zauważyć bez profilera. W tym przypadku wierzę, że korzystasz z klasy, ponieważ:

  • cała twoja logika żyje w jednej funkcji
  • Jest łatwy do aktualizacji i pozostaje zamknięty
  • jeśli zmienisz coś później, możesz łatwo utrzymać interfejs tak samo
 30
Author: Bruce Armstrong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 17:00:01

Myślę, że użycie każdego z nich jest zbyt subiektywne dla mnie, aby się w to wkręcić, więc będę trzymać się liczb.

Porównałem czas potrzebny na stworzenie i zmianę zmiennej w dict, klasie new_style i klasie new_style z slotami.

Oto kod, którego użyłem, aby go przetestować(jest trochę brudny, ale wykonuje swoją pracę.)

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')))

A oto wyjście...

Tworzenie...

Dict: 0.817466186345
Class: 1.60829183597
Class_with_slots: 1.28776730003

Zmiana zmiennej...

Dict: 0.0735140918748
Class: 0.111714198313
Class_with_slots: 0.10618612142

Więc, jeśli tylko przechowywanie zmiennych wymaga szybkości i nie wymaga wielu obliczeń, polecam użycie dict(zawsze można po prostu zrobić funkcję, która wygląda jak metoda). Ale jeśli naprawdę potrzebujesz zajęć, pamiętaj - zawsze używaj __sloty__.

Uwaga:

Przetestowałem 'Class' z obie klasy new_style i old_style. Okazuje się, że klasy old_style są szybsze do tworzenia, ale wolniejsze do modyfikowania(nie o wiele, ale znaczące, jeśli tworzysz wiele zajęć w ciasnej pętli (wskazówka: robisz to źle)).

Również czasy tworzenia i zmiany zmiennych mogą się różnić na twoim komputerze, ponieważ mój jest stary i powolny. Upewnij się, że przetestujesz go samodzielnie, aby zobaczyć "prawdziwe" wyniki.

Edit:

Później przetestowałem namedtuple: nie mogę go modyfikować, ale stworzenie 10000 próbek (lub coś w tym stylu) zajęło 1,4 sekundy, więc słownik jest rzeczywiście najszybszy.

If I change the dict function to zawierać klucze i wartości i zwracać dict zamiast zmiennej zawierającej dict kiedy go utworzyć daje mi 0.65 zamiast 0.8 sekundy.

class Foo(dict):
    pass

Tworzenie jest jak klasa z slotami, a zmiana zmiennej jest najwolniejsza (0,17 sekundy), więc nie używaj tych klas. wybierz dict (speed) lub klasę wywodzącą się z object ('syntax')

 17
Author: alexpinho98,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-30 20:31:05

Zgadzam się z @adw. Nigdy nie reprezentowałbym" obiektu " (w sensie OO) ze słownikiem. Słowniki agregują pary nazwa / wartość. Klasy reprezentują obiekty. Widziałem kod, w którym obiekty są reprezentowane przez słowniki i nie jest jasne, jaki jest rzeczywisty kształt rzeczy. Co się dzieje, gdy nie ma określonej nazwy/wartości? Co ogranicza klienta przed włożeniem czegokolwiek. Albo próbuje cokolwiek wyciągnąć. Kształt rzeczy powinien być zawsze jasno określony.

Podczas używania Pythona ważne jest, aby budować z dyscypliną, ponieważ język pozwala na wiele sposobów, aby autor mógł strzelić sobie w stopę.

 8
Author: jaydel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-07 14:48:48

Polecam klasę, ponieważ są to wszelkiego rodzaju informacje związane z zapytaniem. Gdyby użyć słownika, spodziewałbym się, że przechowywane dane będą o wiele bardziej podobne. Wytyczna, którą mam tendencję do naśladowania siebie jest taka, że jeśli mogę chcieć zapętlić cały zestaw par klucz- > wartość i zrobić coś, używam słownika. W przeciwnym razie dane najwyraźniej mają znacznie większą strukturę niż podstawowe mapowanie klucza - > wartości, co oznacza, że Klasa byłaby prawdopodobnie lepszą alternatywą.

Dlatego trzymaj się klasy.

 5
Author: Stigma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 17:00:58

Być może uda się mieć swoje ciasto i je zjeść. Innymi słowy, możesz stworzyć coś, co zapewni funkcjonalność zarówno klasy, jak I instancji słownika. Zobacz ofertę ActiveState słownik z dostępem w stylu atrybutów przepis i komentarze na temat sposobów na to.

Jeśli zdecydujesz się użyć zwykłej klasy, a nie podklasy, znalazłem 'Prosty, ale poręczny "kolekcjoner kilku nazwanych rzeczy" klasa " przepis na Bardzo elastyczny i przydatny do tego, co wygląda, jak robisz (np. stwórz względny prosty agregator informacji). Ponieważ jest to klasa, możesz łatwo rozszerzyć jej funkcjonalność później, dodając metody.

Na koniec należy zauważyć, że nazwy członków klasy muszą być legalnymi identyfikatorami Pythona, ale klucze słownikowe Nie-więc słownik zapewniłby większą swobodę w tym zakresie, ponieważ klucze mogą być dowolnymi hashami (nawet czymś, co nie jest ciągiem znaków).

 2
Author: martineau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-28 21:06:28

Jeśli wszystko, co chcesz osiągnąć, to składnia w stylu obj.bla = 5 zamiast obj['bla'] = 5, zwłaszcza jeśli musisz to często powtarzać, może chcesz użyć jakiejś zwykłej klasy kontenera, jak w sugestii martineausa. Niemniej jednak tamtejszy kod jest dość nadęty i niepotrzebnie powolny. Można tak trzymać:

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

Innym powodem przełączenia na namedtuple S lub klasę z __slots__ może być użycie pamięci. Dyski wymagają znacznie więcej pamięci niż typy list, więc może to być punkt do myślenia około.

W każdym razie, w twoim konkretnym przypadku, nie wydaje się być żadnej motywacji do odejścia od bieżącej implementacji. Wydaje się, że nie utrzymujesz milionów tych obiektów, więc nie jest wymagana lista typów pochodnych. I faktycznie zawiera jakąś logikę funkcjonalną w __init__, więc nie powinieneś też mieć AttrDict.

 2
Author: Michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-28 12:04:56
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)

Polecam zajęcia. Jeśli używasz klasy, możesz uzyskać podpowiedź Typu, Jak pokazano. A obsługa klasy jest automatycznie uzupełniana, gdy klasa jest argumentem funkcji.

Tutaj wpisz opis obrazka

 0
Author: LowQualityDelivery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-29 12:44:44