복붙노트

[PYTHON] 파이썬에서는 두 객체가 언제 같습니까?

PYTHON

파이썬에서는 두 객체가 언제 같습니까?

2가 2이고 3이 3 인 것처럼 보입니다. 파이썬에서는 항상 사실이며, 일반적으로 정수에 대한 참조는 같은 정수에 대한 다른 참조와 같습니다. None (없음)이 아닌 경우도 마찬가지입니다. 나는 이것이 사용자 정의 유형이나 변경 가능한 유형에는 일어나지 않는다는 것을 안다. 그러나 그것은 때때로 불변 형에도 실패합니다 :

>>> () is ()
True
>>> (2,) is (2,)
False

빈 터플의 두 개의 독립적 인 구조가 메모리의 같은 객체에 대한 참조를 산출하지만 동일한 하나의 (불변의) 요소 튜플에 대한 두 개의 독립적 인 구조가 결국 두 개의 동일한 객체를 생성하게됩니다. 테스트를 거쳤으며 프리즈 세트는 튜플과 비슷한 방식으로 작동합니다.

객체가 메모리에 복제 될지 아니면 많은 참조가있는 단일 인스턴스가 있을지를 결정하는 것은 무엇입니까? 그것은 어떤면에서 객체가 "원자"인지 여부에 달려 있습니까? 구현에 따라 다릅니 까?

해결법

  1. ==============================

    1.파이썬에는 하나의 인스턴스 만 가질 수있는 몇 가지 유형이 있습니다. 이러한 인스턴스의 예로는 None, NotImplemented 및 Ellipsis가 있습니다. 이것들은 (정의상) 싱글 톤 들이므로 None None과 같은 것들은 NoneType의 새로운 인스턴스를 생성 할 방법이 없기 때문에 True를 리턴하도록 보장됩니다.

    파이썬에는 하나의 인스턴스 만 가질 수있는 몇 가지 유형이 있습니다. 이러한 인스턴스의 예로는 None, NotImplemented 및 Ellipsis가 있습니다. 이것들은 (정의상) 싱글 톤 들이므로 None None과 같은 것들은 NoneType의 새로운 인스턴스를 생성 할 방법이 없기 때문에 True를 리턴하도록 보장됩니다.

    또한 몇 배의 doubleton을 제공합니다. True, False 2 - True 참조는 모두 같은 객체를 가리 킵니다. 다시 말해, bool의 새 인스턴스를 만들 수있는 방법이 없기 때문입니다.

    위의 것들은 모두 파이썬 언어로 보장됩니다. 그러나 이미 알고 있듯이 재사용을 위해 몇 가지 인스턴스를 저장하는 일부 유형 (모두 불변)이 있습니다. 이것은 언어에 의해 허용되지만 다른 구현은 최적화 전략에 따라이 허용 한도를 사용할지 여부를 선택할 수 있습니다. 이 범주에 속하는 몇 가지 예는 작은 정수 (-5 -> 255), 빈 튜플 및 빈 frozenset입니다.

    마지막으로 Cpython은 파싱하는 동안 특정 불변 객체를 인턴으로 고용합니다 ...

    예 : Cpython으로 다음 스크립트를 실행하면 True를 반환하는 것을 볼 수 있습니다 :

    def foo():
        return (2,)
    
    if __name__ == '__main__':
        print foo() is foo()
    

    이것은 정말로 이상하게 보인다. Cpython이하는 속임수는 함수 foo를 생성 할 때마다 다른 간단한 (변경 불가능한) 리터럴을 포함하는 튜플 리터럴을 보는 것입니다. 이 튜플 (또는 그것의 등가물)을 반복해서 생성하는 대신, 파이썬은 한번만 생성합니다. 전체 거래가 불변이므로 해당 오브젝트가 변경 될 위험이 없습니다. 동일한 타이트한 루프가 반복해서 호출되는 경우 성능에 큰 도움이 될 수 있습니다. 작은 문자열은 또한 금지됩니다. 여기서 실제 승리는 사전 조회에 있습니다. 파이썬은 (엄청나게 빠름) 포인터 비교를 수행 한 다음 해시 충돌을 확인할 때 더 느린 문자열 비교로 돌아갑니다. 파이썬의 많은 부분이 사전 검색을 기반으로하므로 언어 ​​전체에 대한 큰 최적화가 될 수 있습니다.

    1 나는 그 말을 방금 썼을지도 모르지만 ... 바라건대 당신이 그 생각을 ... 2 정상적인 상황에서 개체가 True에 대한 참조인지 확인하지 않아도됩니다. 일반적으로 개체가 "사실"인지 여부 만 신경 써야합니다. if some_instance : ...가 브랜치를 실행할 경우. 그러나, 나는 이것을 완전성을 위해서 여기에 넣었습니다.

    싱글 톤이 아닌 것을 비교하는 데 사용할 수 있습니다. 한 가지 일반적인 용도는 센티널 값을 만드는 것입니다.

    sentinel = object()
    item = next(iterable, sentinel)
    if items is sentinel:
       # iterable exhausted.
    

    또는:

    _sentinel = object()
    def function(a, b, none_is_ok_value_here=_sentinel):
        if none_is_ok_value_here is sentinel:
            # Treat the function as if `none_is_ok_value_here` was not provided.
    

    이 이야기의 도덕은 항상 당신이 말하는 것을 말합니다. 값이 다른 값인지 확인하려면 is 연산자를 사용하십시오. 값이 다른 값 (가능하면 별개)과 같은지 확인하려면 ==를 사용하십시오. is와 ==의 차이점과 어느 것을 사용해야하는지에 대한 자세한 내용은 다음 게시물 중 하나를 참조하십시오.

    우리는이 CPython 구현 세부 사항에 대해 이야기했으며 최적화라고 주장했습니다. 이 모든 최적화에서 얻은 결과를 측정하는 것이 좋을 것입니다 (is 연산자로 작업 할 때 약간의 혼란을 제외하고).

    다음은 같은 문자열을 사용하여 다른 문자열 대신 값을 조회하는 경우 얼마나 빨리 사전 조회를 하는지를 보여주기 위해 실행할 수있는 작은 스크립트입니다. 참고, 나는 변수 이름에 "interned"라는 용어를 사용합니다.이 값들은 반드시 억누를 수는 없습니다. 나는 단지 "interned"문자열이 사전에있는 문자열임을 나타 내기 위해 이것을 사용하고 있습니다.

    import timeit
    
    interned = 'foo'
    not_interned = (interned + ' ').strip()
    
    assert interned is not not_interned
    
    
    d = {interned: 'bar'}
    
    print('Timings for short strings')
    number = 100000000
    print(timeit.timeit(
        'd[interned]',
        setup='from __main__ import interned, d',
        number=number))
    print(timeit.timeit(
        'd[not_interned]',
        setup='from __main__ import not_interned, d',
        number=number))
    
    
    ####################################################
    
    interned_long = interned * 100
    not_interned_long = (interned_long + ' ').strip()
    
    d[interned_long] = 'baz'
    
    assert interned_long is not not_interned_long
    print('Timings for long strings')
    print(timeit.timeit(
        'd[interned_long]',
        setup='from __main__ import interned_long, d',
        number=number))
    print(timeit.timeit(
        'd[not_interned_long]',
        setup='from __main__ import not_interned_long, d',
        number=number))
    

    정확한 값은별로 중요하지 않지만 내 컴퓨터에서는 짧은 문자열이 약 1 부분 만 빠릅니다. 문자열이 비교할 문자가 더 많으면 문자열 비교가 더 오래 걸리기 때문에 긴 문자열은 거의 2 배 더 빠릅니다. 차이점은 python3.x와는 크게 다르지 않지만 여전히 확실합니다.

    다음은 함께 놀 수있는 작은 스크립트입니다.

    import timeit
    
    def foo_tuple():
        return (2, 3, 4)
    
    def foo_list():
        return [2, 3, 4]
    
    assert foo_tuple() is foo_tuple()
    
    number = 10000000
    t_interned_tuple = timeit.timeit('foo_tuple()', setup='from __main__ import foo_tuple', number=number)
    t_list = (timeit.timeit('foo_list()', setup='from __main__ import foo_list', number=number))
    
    print(t_interned_tuple)
    print(t_list)
    print(t_interned_tuple / t_list)
    print('*' * 80)
    
    
    def tuple_creation(x):
        return (x,)
    
    def list_creation(x):
        return [x]
    
    t_create_tuple = timeit.timeit('tuple_creation(2)', setup='from __main__ import tuple_creation', number=number)
    t_create_list = timeit.timeit('list_creation(2)', setup='from __main__ import list_creation', number=number)
    print(t_create_tuple)
    print(t_create_list)
    print(t_create_tuple / t_create_list)
    

    이것은 시간이 좀 더 까다 롭습니다 (그리고 코멘트에서 시간을 측정하는 방법에 대해 더 좋은 아이디어를 얻게되어 기쁩니다). 이것의 요지는 평균적으로 (그리고 내 컴퓨터에서) 튜플은리스트가 생성 될 때까지 약 60 %의 시간이 걸린다는 것입니다. 그러나 foo_tuple ()은 foo_list ()가 걸리는 시간의 평균이 약 40 %입니다. 이는 우리가이 인턴들로부터 약간의 스피드 업을 실제로 얻음을 보여줍니다. 튜플이 커질수록 시간이 절약되는 것 같습니다. (긴리스트를 만드는 데 더 많은 시간이 걸립니다 - 튜플 "creation"은 이미 생성 된 이후로 일정 시간이 걸립니다).

    또한이 "인턴"이라고 불렀습니다. 실제로는 (적어도 현이 억류 된 것과 같은 의미는 아닙니다). 이 간단한 스크립트의 차이점을 확인할 수 있습니다.

    def foo_tuple():
        return (2,)
    
    def bar_tuple():
        return (2,)
    
    def foo_string():
        return 'foo'
    
    def bar_string():
        return 'foo'
    
    print(foo_tuple() is foo_tuple())  # True
    print(foo_tuple() is bar_tuple())  # False
    
    print(foo_string() is bar_string())  # True
    

    문자열은 실제로 "interned"입니다. 동일한 리터럴 표기법을 사용하는 다른 호출은 동일한 객체를 반환합니다. 튜플 "interning"은 한 줄에 한정된 것처럼 보입니다.

  2. ==============================

    2.구현에 따라 다릅니다.

    구현에 따라 다릅니다.

    CPython은 불변의 객체를 메모리에 캐시합니다. 이것은 1과 2와 같은 "작은"정수 (아래 주석에서 언급 한 것처럼 -5에서 255까지)에 해당됩니다. CPython은 성능상의 이유로이 작업을 수행합니다. 작은 정수는 대부분의 프로그램에서 일반적으로 사용되기 때문에 메모리가 하나만 만들어지기 때문에 메모리를 절약 할 수 있습니다 (정수는 불변이므로 안전합니다).

    None과 같은 "싱글 톤"객체에 대해서도 마찬가지입니다. 주어진 시간에 오직 하나의 없음 만 존재합니다.

    다른 객체 (예 : 빈 튜플, ())는 싱글 톤으로 구현 될 수도 있고 그렇지 않을 수도 있습니다.

    일반적으로, 당신은 반드시 불변 객체가 이런 방식으로 구현 될 것이라고 가정하지 않아야합니다. CPython은 성능상의 이유로 그렇게하지만, 다른 구현체는 그렇지 않을 수 있으며, CPython은 앞으로 어느 시점에서이를 중단 할 수도 있습니다. (단, None은 None이 될 수 있습니다. x는 None이 일반적인 파이썬 관용구이며 다른 인터프리터와 버전에서 구현 될 가능성이 큽니다.)

    일반적으로 is 대신에 ==를 사용하려고합니다. Python의 is 연산자는 변수가 None인지 확인하는 경우를 제외하고 자주 사용되지 않습니다.

  3. from https://stackoverflow.com/questions/36898917/in-python-when-are-two-objects-the-same by cc-by-sa and MIT license