다음과 같이 필드 만 포함하고 메서드가없는 클래스가 있습니다.
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를 사용하면 이점이 있습니까? 딕셔너리는 클래스보다 빠를까요? 슬롯이있는 클래스보다 빠르나요?
답변
왜 이것을 사전으로 만드시겠습니까? 장점은 무엇입니까? 나중에 코드를 추가하려면 어떻게됩니까? __init__
코드 는 어디에 있습니까 ?
클래스는 관련 데이터 (일반적으로 코드)를 묶는 데 사용됩니다.
사전은 키-값 관계를 저장하기위한 것으로, 일반적으로 키는 모두 동일한 유형이고 모든 값도 한 유형입니다. 경우에 따라 키 / 속성 이름이 모두 알려지지 않은 경우 데이터를 번들링하는 데 유용 할 수 있지만, 이는 종종 디자인에 문제가 있다는 신호입니다.
이 클래스를 유지하십시오.
답변
클래스의 추가 메커니즘이 필요하지 않으면 사전을 사용하십시오. namedtuple
하이브리드 접근 방식을 위해를 사용할 수도 있습니다 .
>>> from collections import namedtuple
>>> request = namedtuple("Request", "environ request_method url_scheme")
>>> request
<class '__main__.Request'>
>>> request.environ = "foo"
>>> request.environ
'foo'
사전이 더 빠르지 않으면 놀랄 것이지만 여기서 성능 차이는 최소화됩니다.
답변
파이썬의 클래스 는 그 밑에있는 dict입니다. 클래스 동작에 약간의 오버 헤드가 발생하지만 프로파일 러 없이는이를 알아 차릴 수 없습니다. 이 경우 다음과 같은 이유로 수업을 통해 혜택을받을 수 있습니다.
- 모든 논리는 단일 기능에 있습니다.
- 업데이트하기 쉽고 캡슐화 상태로 유지
- 나중에 변경하는 경우 쉽게 인터페이스를 동일하게 유지할 수 있습니다.
답변
나는 각각의 사용법이 너무 주관적이어서 그것에 대해 이해하지 못한다고 생각하기 때문에 나는 숫자에 충실 할 것입니다.
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 클래스 모두 에서 ‘Class’를 테스트했습니다 . old_style 클래스는 생성 속도가 빠르지 만 수정 속도가 느립니다 (단단한 루프에서 많은 클래스를 생성하는 경우에는 많지는 않지만 중요합니다 (팁 : 잘못하고 있음)).
또한 내 것이 오래되고 느리기 때문에 변수를 만들고 변경하는 시간은 컴퓨터에서 다를 수 있습니다. ‘실제’결과를 보려면 직접 테스트해야합니다.
편집하다:
나중에 namedtuple을 테스트했습니다. 수정할 수는 없지만 10000 개의 샘플 (또는 이와 유사한 것)을 만드는 데 1.4 초가 걸렸으므로 사전이 실제로 가장 빠릅니다.
난 경우 딕셔너리 기능을 변경 그것을 만들 때 키와 값을 포함하고 상기 딕셔너리를 포함하는 변수 대신 딕셔너리를 반환 나야 제공 0.65 대신 0.8 초가.
class Foo(dict):
pass
생성은 슬롯이있는 클래스와 같으며 변수 변경이 가장 느리므로 (0.17 초) 이러한 클래스를 사용하지 마십시오 . dict (속도) 또는 객체에서 파생 된 클래스 ( ‘구문 캔디’)로 이동
답변
@adw에 동의합니다. 나는 사전으로 “객체”(OO의 의미에서)를 표현하지 않을 것입니다. 사전은 이름 / 값 쌍을 집계합니다. 클래스는 객체를 나타냅니다. 나는 객체가 사전으로 표현되는 코드를 보았고 사물의 실제 모양이 무엇인지 명확하지 않습니다. 특정 이름 / 값이 없으면 어떻게됩니까? 클라이언트가 아무것도 넣지 못하도록 제한하는 것. 또는 모든 것을 꺼내려고 시도하는 것. 사물의 모양은 항상 명확하게 정의되어야합니다.
Python을 사용할 때 언어는 저자가 자신의 발을 쏠 수있는 다양한 방법을 허용하기 때문에 규율을 가지고 구축하는 것이 중요합니다.
답변
요청과 관련된 모든 종류의 정보이므로 수업을 추천합니다. 사전을 사용하는 사람이라면 저장된 데이터가 본질적으로 훨씬 더 유사 할 것으로 예상합니다. 내가 따르는 경향이있는 지침은 전체 키-> 값 쌍 세트를 반복하고 무언가를 수행하려면 사전을 사용한다는 것입니다. 그렇지 않으면 데이터가 기본 키-> 값 매핑보다 훨씬 더 많은 구조를 가지므로 클래스가 더 나은 대안이 될 수 있습니다.
따라서 수업에 충실하십시오.
답변
달성하려는 모든 것이. obj.bla = 5
대신에 같은 구문 사탕 obj['bla'] = 5
이면, 특히 많이 반복해야하는 경우 martineaus 제안에서와 같이 일반 컨테이너 클래스를 사용하는 것이 좋습니다. 그럼에도 불구하고 코드는 상당히 부풀고 불필요하게 느립니다. 다음과 같이 간단하게 유지할 수 있습니다.
class AttrDict(dict):
""" Syntax candy """
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
namedtuple
s 또는 클래스 로 전환하는 또 다른 이유는 __slots__
메모리 사용량 일 수 있습니다. 사전은 목록 유형보다 훨씬 더 많은 메모리를 필요로하므로 고려할 사항이 될 수 있습니다.
어쨌든, 특정 경우에는 현재 구현에서 전환하려는 동기가없는 것 같습니다. 수백만 개의 이러한 개체를 유지하지 않는 것 같으므로 목록 파생 유형이 필요하지 않습니다. 그리고 실제로 내부에 일부 기능적 논리가 포함 __init__
되어 있으므로 AttrDict
.