Mam klasę, która zawiera tylko pola i nie ma metod, na przykład:
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 przełożyć na dyktando. Klasa jest bardziej elastyczna dla przyszłych dodatków i może być szybka z __slots__
. Czy zatem korzystanie z dyktowania przyniesie korzyści? Czy dyktand byłby szybszy niż klasa? I szybciej niż klasa z automatami?
python
oop
class
dictionary
demon
źródło
źródło
dict
może mieć sens. niezła zaleta: kiedy debugujesz, po prostu powiedz,print(request)
a zobaczysz wszystkie informacje o stanie. przy bardziej klasycznym podejściu będziesz musiał napisać własne__str__
metody, co jest do bani, jeśli musisz to robić cały czas.Odpowiedzi:
Dlaczego miałbyś zrobić z tego słownik? Jaka jest zaleta? Co się stanie, jeśli później zechcesz dodać kod? Gdzie poszedłby twój
__init__
kod?Klasy służą do grupowania powiązanych danych (i zwykle kodu).
Słowniki służą do przechowywania relacji klucz-wartość, gdzie zwykle wszystkie klucze są tego samego typu, a wszystkie wartości są również tego samego typu. Czasami mogą być przydatne do grupowania danych, gdy nie wszystkie nazwy kluczy / atrybutów są znane z góry, ale często jest to znak, że coś jest nie tak z projektem.
Niech to będzie klasa.
źródło
__init__
. Ale masz rację: oddzieliłbym się od rzeczy, które należą do siebie.where would your __init__ code go?
dotyczy. Może to przekonać mniej doświadczonego programistę, że należy używać tylko klas, ponieważ w słowniku nie jest używana metoda init . Absurdalny.Użyj słownika, chyba że potrzebujesz dodatkowego mechanizmu klasy. Możesz również użyć
namedtuple
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ż zdziwiłbym się, gdyby słownik nie był szybszy.
źródło
Klasa w Pythonie to dykt pod spodem. Zachowanie klasy powoduje pewne obciążenie, ale bez profilera nie da się tego zauważyć. W tym przypadku uważam, że skorzystasz z zajęć, ponieważ:
źródło
Myślę, że użycie każdego z nich jest dla mnie zbyt subiektywne, abym mógł się nad tym zastanowić, więc po prostu trzymam się liczb.
Porównałem czas potrzebny do utworzenia i zmiany zmiennej w dict, klasie new_style i klasie new_style ze slotami.
Oto kod, którego użyłem do przetestowania (jest trochę niechlujny, ale spełnia swoje zadanie).
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 wynik ...
Tworzę…
Dict: 0.817466186345 Class: 1.60829183597 Class_with_slots: 1.28776730003
Zmiana zmiennej ...
Dict: 0.0735140918748 Class: 0.111714198313 Class_with_slots: 0.10618612142
Tak więc, jeśli przechowujesz tylko zmienne, potrzebujesz szybkości i nie będzie wymagało to wykonywania wielu obliczeń, zalecam użycie dyktowania (zawsze możesz po prostu utworzyć funkcję, która wygląda jak metoda). Ale jeśli naprawdę potrzebujesz zajęć, pamiętaj - zawsze używaj __ miejsc __ .
Uwaga:
Przetestowałem „Klasę” z obydwoma new_style i klas old_style. Okazuje się, że klasy w starym stylu są szybsze w tworzeniu, ale wolniej się je modyfikuje (niewiele, ale istotne, jeśli tworzysz wiele klas w ciasnej pętli (wskazówka: robisz to źle)).
Również czasy tworzenia i zmieniania zmiennych mogą się różnić na twoim komputerze, ponieważ mój jest stary i powolny. Upewnij się, że sam to przetestujesz, aby zobaczyć „prawdziwe” wyniki.
Edytować:
Później przetestowałem namedtuple: nie mogę go zmodyfikować, ale utworzenie 10000 próbek (lub czegoś podobnego) zajęło 1,4 sekundy, więc słownik jest rzeczywiście najszybszy.
Jeśli zmienię funkcję dict tak, aby zawierała klucze i wartości oraz aby zwracała dict zamiast zmiennej zawierającej dict, kiedy ją utworzę, otrzymam 0,65 zamiast 0,8 sekundy.
class Foo(dict): pass
Tworzenie jest jak klasa ze szczelinami, a zmiana zmiennej jest najwolniejsza (0,17 sekundy), więc nie używaj tych klas . wybierz dict (prędkość) lub klasę pochodną obiektu („cukierek składniowy”)
źródło
dict
(bez nadpisanych metod, jak sądzę?). Czy działa tak samo, jak klasa w nowym stylu, która została napisana od podstaw?Zgadzam się z @adw. Nigdy nie przedstawiłbym „obiektu” (w sensie OO) w słowniku. Słowniki agregują pary nazwa / wartość. Klasy reprezentują obiekty. Widziałem kod, w którym obiekty są reprezentowane za pomocą słowników i nie jest jasne, jaki jest rzeczywisty kształt rzeczy. Co się dzieje, gdy nie ma określonej nazwy / wartości? Co ogranicza klientowi możliwość wkładania czegokolwiek do środka. Albo próby wydobycia czegokolwiek. Kształt rzeczy powinien być zawsze jasno określony.
Podczas korzystania z Pythona ważne jest, aby budować z dyscypliną, ponieważ język umożliwia autorowi strzelanie sobie w stopę na wiele sposobów.
źródło
Poleciłbym zajęcia, ponieważ są to różnego rodzaju informacje związane z prośbą. Gdyby ktoś korzystał ze słownika, spodziewałbym się, że przechowywane dane będą o wiele bardziej podobne. Wytyczna, do której sam się kieruję, jest taka, że jeśli chcę zapętlić cały zestaw par klucz-> wartość i coś zrobić, używam słownika. W przeciwnym razie dane najwyraźniej mają znacznie większą strukturę niż podstawowe mapowanie klucz-> wartość, co oznacza, że prawdopodobnie lepszą alternatywą byłaby klasa.
Dlatego trzymaj się klasy.
źródło
Jeśli wszystko, co chcesz achive składnia jest jak cukierek
obj.bla = 5
zamiastobj['bla'] = 5
, szczególnie jeśli trzeba powtórzyć, że dużo, to może chcesz używać jakiś zwykły klasy Pojemnik martineaus sugestii. Niemniej jednak kod jest dość rozdęty i niepotrzebnie wolny. Możesz to uprościć:class AttrDict(dict): """ Syntax candy """ __getattr__ = dict.__getitem__ __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__
Innym powodem przejścia na
namedtuple
s lub klasę z__slots__
może być użycie pamięci. Dykty wymagają znacznie więcej pamięci niż typy list, więc warto o tym pomyśleć.W każdym razie, w Twoim konkretnym przypadku wydaje się, że nie ma motywacji do odejścia od obecnej implementacji. Wydaje się, że nie zarządzasz milionami tych obiektów, więc nie są wymagane żadne typy wyprowadzane z listy. I faktycznie zawiera pewną logikę funkcjonalną w ramach
__init__
, więc nie powinieneś też mieć z tymAttrDict
.źródło
types.SimpleNamespace
(dostępne od Pythona 3.3) jest alternatywą dla niestandardowego AttrDict.Możliwe, że będzie można mieć ciasto i je też zjeść. Innymi słowy, możesz stworzyć coś, co zapewnia funkcjonalność zarówno instancji klasy, jak i słownika. Zobacz przepis na Dɪᴄᴛɪᴏɴᴀʀʏ ᴡɪᴛʜ ᴀᴛᴛʀɪʙᴜᴛᴇ-sᴛʏʟᴇ ᴀᴄᴄᴇss ActiveState i komentarze na temat sposobów zrobienia tego.
Jeśli zdecydujesz się użyć zwykłej klasy zamiast podklasy, stwierdziłem, że przepis Tʜᴇ sɪᴍᴘʟᴇ ʙᴜᴛ ʜᴀɴᴅʏ "ᴄᴏʟʟᴇᴄᴛᴏʀ ᴏғ ᴀ ʙᴜɴᴄʜ ᴏғ ɴᴀᴍᴇᴅ sᴛᴜғғ" ᴄʟᴀss (autorstwa Alexa Martellego ) jest bardzo elastyczny i przydatny do tego typu rzeczy wygląda na to, że robisz (tj. stwórz względnie prosty agregator informacji). Ponieważ jest to klasa, możesz łatwo rozszerzyć jej funkcjonalność, dodając metody.
Na koniec należy zauważyć, że nazwy członków klas muszą być legalnymi identyfikatorami Pythona, ale klucze słownikowe nie - więc słownik zapewniłby większą swobodę w tym względzie, ponieważ klucze mogą być hashowane dowolnie (nawet coś, co nie jest ciągiem znaków).
Aktualizacja
Klasa
object
(która nie ma__dict__
) podklasy o nazwieSimpleNamespace
(która ją ma) została dodana dotypes
modułu Python 3.3 i jest kolejną alternatywą.źródło
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 korzystasz z klasy, możesz uzyskać wskazówkę dotyczącą typu, jak pokazano. A klasa obsługuje autouzupełnianie, gdy klasa jest argumentem funkcji.
źródło