programing tip

수업이나 사전을 사용해야합니까?

itbloger 2020. 9. 22. 07:47
반응형

수업이나 사전을 사용해야합니까?


다음과 같이 필드 만 포함하고 메서드가없는 클래스가 있습니다.

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를 사용하는 것이 좋습니다 (항상 메서드처럼 보이는 함수를 만들 수 있음). 그러나 정말로 클래스가 필요한 경우 항상 __ 슬롯 __을 사용하십시오 .

노트 :

I tested the 'Class' with both new_style and old_style classes. It turns out that old_style classes are faster to create but slower to modify(not by much but significant if you're creating lots of classes in a tight loop (tip: you're doing it wrong)).

Also the times for creating and changing variables may differ on your computer since mine is old and slow. Make sure you test it yourself to see the 'real' results.

Edit:

I later tested the namedtuple: i can't modify it but to create the 10000 samples (or something like that) it took 1.4 seconds so the dictionary is indeed the fastest.

If i change the dict function to include the keys and values and to return the dict instead of the variable containing the dict when i create it it gives me 0.65 instead of 0.8 seconds.

class Foo(dict):
    pass

Creating is like a class with slots and changing the variable is the slowest (0.17 seconds) so do not use these classes. go for a dict (speed) or for the class derived from object ('syntax candy')


I agree with @adw. I would never represent an "object" (in an OO sense) with a dictionary. Dictionaries aggregate name/value pairs. Classes represent objects. I've seen code where the objects are represented with dictionaries and it's unclear what the actual shape of the thing is. What happens when certain name/values aren't there? What restricts the client from putting anything at all in. Or trying to get anything at all out. The shape of the thing should always be clearly defined.

When using Python it is important to build with discipline as the language allows many ways for the author to shoot him/herself in the foot.


I would recommend a class, as it is all sorts of information involved with a request. Were one to use a dictionary, I'd expect the data stored to be far more similar in nature. A guideline I tend to follow myself is that if I may want to loop over the entire set of key->value pairs and do something, I use a dictionary. Otherwise, the data apparently has far more structure than a basic key->value mapping, meaning a class would likely be a better alternative.

Hence, stick with the class.


If all that you want to achive is syntax candy like obj.bla = 5 instead of obj['bla'] = 5, especially if you have to repeat that a lot, you maybe want to use some plain container class as in martineaus suggestion. Nevertheless, the code there is quite bloated and unnecessarily slow. You can keep it simple like that:

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

Another reason to switch to namedtuples or a class with __slots__ could be memory usage. Dicts require significantly more memory than list types, so this could be a point to think about.

Anyways, in your specific case, there doesn't seem to be any motivation to switch away from your current implementation. You don't seem to maintain millions of these objects, so no list-derived-types required. And it's actually containing some functional logic within the __init__, so you also shouldn't got with AttrDict.


It may be possible to have your cake and eat it, too. In other words you can create something that provides the functionality of both a class and dictionary instance. See the ActiveState's Dɪᴄᴛɪᴏɴᴀʀʏ ᴡɪᴛʜ ᴀᴛᴛʀɪʙᴜᴛᴇ-sᴛʏʟᴇ ᴀᴄᴄᴇss recipe and comments on ways of doing that.

If you decide to use a regular class rather than a subclass, I've found the Tʜᴇ sɪᴍᴘʟᴇ ʙᴜᴛ ʜᴀɴᴅʏ "ᴄᴏʟʟᴇᴄᴛᴏʀ ᴏғ ᴀ ʙᴜɴᴄʜ ᴏғ ɴᴀᴍᴇᴅ sᴛᴜғғ" ᴄʟᴀss recipe (by Alex Martelli) to be very flexible and useful for the sort of thing it looks like you're doing (i.e. create a relative simple aggregator of information). Since it's a class you can easily extend its functionality further by adding methods.

Lastly it should be noted that the names of class members must be legal Python identifiers, but dictionary keys do not—so a dictionary would provide greater freedom in that regard because keys can be anything hashable (even something that's not a string).

Update

A class object (which doesn't have a __dict__) subclass named SimpleNamespace (which does have one) was added to Python 3.3, and is yet another alternative.


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)

I recommend a class. If you use a class, you can get type hint as shown. And Class support auto complete when class is argument of function.

enter image description here

참고URL : https://stackoverflow.com/questions/4045161/should-i-use-a-class-or-dictionary

반응형