목차
머리말
Introduction
归纳这些方法
我发现几乎没有人使用**=
이러한 메소드를 일반화하세요
백엔드 개발 파이썬 튜토리얼 강화된 연산 할당 '-=' 연산에 대한 자세한 설명

강화된 연산 할당 '-=' 연산에 대한 자세한 설명

Sep 11, 2020 pm 05:11 PM
python 과제

강화된 연산 할당 '-=' 연산에 대한 자세한 설명

관련 학습 권장 사항: python 튜토리얼

머리말

이 문서는 Python 구문 설탕에 대한 일련의 문서 중 하나입니다. 최신 소스 코드는 desugar 프로젝트(github.com/brettcannon…

Introduction

Python에는 향상된 산술 할당(증강 산술 할당)이라는 기능이 있습니다. 아마 여러분은 이에 익숙하지 않을 수도 있습니다. 호출됨 수학 연산을 수행하면서 실제로 할당을 수행합니다. 예를 들어 a -= b는 뺄셈의 향상된 산술 할당입니다. 增强算术赋值(augmented arithmetic assignment)的东西。可能你不熟悉这个叫法,其实就是在做数学运算的同时进行赋值,例如 a -= b 就是减法的增强算术赋值。

增强赋值是在 Python 2.0 版本中 加入进来的。(译注:在 PEP-203 中引入)

剖析-=

因为 Python 不允许覆盖式赋值,所以相比其它有特殊/魔术方法的操作,它实现增强赋值的方式可能跟你想象的不完全一样。

首先,要知道a -= b在语义上与 a = a-b 相同。但也要意识到,如果你预先知道要将一个对象赋给一个变量名,相比a - b 的盲操作,就可能会更高效。

例如,最起码的好处是可以避免创建一个新对象:如果可以就地修改一个对象,那么返回 self,就比重新构造一个新对象要高效。

因此,Python 提供了一个__isub__() 方法。如果它被定义在赋值操作的左侧(通常称为 lvalue),则会调用右侧的值(通常称为 rvalue )。所以对于a -= b ,就会尝试去调用 a.__isub__(b)。

如果调用的结果是 NotImplemented,或者根本不存在结果,那么 Python 会退回到常规的二元算术运算:a - b。(译注:作者关于二元运算的文章,译文在此)

最终无论用了哪种方法,返回值都会被赋值给 a。

下面是简单的伪代码,a -= b 被分解成:

# 实现 a -= b 的伪代码if hasattr(a, "__isub__"):
    _value = a.__isub__(b)    if _value is not NotImplemented:
        a = _value    else:
        a = a - b    del _value else:
     a = a - b复制代码
로그인 후 복사

归纳这些方法

由于我们已经实现了二元算术运算,因此归纳增强算术运算并不太复杂。

通过传入二元算术运算函数,并做一些自省(以及处理可能发生的 TypeError),它可以被漂亮地归纳成:

def _create_binary_inplace_op(binary_op: _BinaryOp) -> Callable[[Any, Any], Any]:

    binary_operation_name = binary_op.__name__[2:-2]
    method_name = f"__i{binary_operation_name}__"
    operator = f"{binary_op._operator}="

    def binary_inplace_op(lvalue: Any, rvalue: Any, /) -> Any:
        lvalue_type = type(lvalue)        try:
            method = debuiltins._mro_getattr(lvalue_type, method_name)        except AttributeError:            pass
        else:
            value = method(lvalue, rvalue)            if value is not NotImplemented:                return value        try:            return binary_op(lvalue, rvalue)        except TypeError as exc:            # If the TypeError is due to the binary arithmetic operator, suppress
            # it so we can raise the appropriate one for the agumented assignment.
            if exc._binary_op != binary_op._operator:                raise
        raise TypeError(            f"unsupported operand type(s) for {operator}: {lvalue_type!r} and {type(rvalue)!r}"
        )

    binary_inplace_op.__name__ = binary_inplace_op.__qualname__ = method_name
    binary_inplace_op.__doc__ = (        f"""Implement the augmented arithmetic assignment `a {operator} b`."""
    )    return binary_inplace_op复制代码
로그인 후 복사

这使得定义的 -= 支持 _create_binary_inplace_op(__ sub__),且可以推断出其它内容:函数名、调用什么 __i*__ 函数,以及当二元算术运算出问题时,该调用哪个可调用对象。

我发现几乎没有人使用**=

在写本文的代码时,我碰上了 **= 的一个奇怪的测试错误。在所有确保 __pow__ 会被适当地调用的测试中,有个测试用例对于 Python 标准库中的operator 模块却是失败。

我的代码通常没问题,如果代码与 CPython 的代码之间存在差异,通常会意味着是我哪里出错了。

但是,无论我多么仔细地排查代码,我都无法定位出为什么我的测试会通过,而标准库则失败。

我决定深入地了解 CPython 内部发生了什么。从反汇编字节码开始:

>>> def test(): a **= b... >>> import dis>>> dis.dis(test)  1           0 LOAD_FAST                0 (a)              2 LOAD_GLOBAL              0 (b)              4 INPLACE_POWER              6 STORE_FAST               0 (a)              8 LOAD_CONST               0 (None)             10 RETURN_VALUE复制代码
로그인 후 복사

通过它,我找到了在 eval 循环中的INPLACE_POWER

        case TARGET(INPLACE_POWER): {
            PyObject *exp = POP();
            PyObject *base = TOP();
            PyObject *res = PyNumber_InPlacePower(base, exp, Py_None);
            Py_DECREF(base);
            Py_DECREF(exp);
            SET_TOP(res);            if (res == NULL)                goto error;
            DISPATCH();
        }复制代码
로그인 후 복사

出处:github.com/python/cpyt…

然后找到PyNumber_InPlacePower()

Python 2.0에서 향상된 할당이 추가되었습니다. (번역: PEP-203에서 도입됨)

분석-=

파이썬은 덮어쓰기 할당을 허용하지 않기 때문에 특수/마법 메서드를 사용하는 다른 작업에 비해 향상된 할당을 구현합니다.

먼저, a -= b는 의미론적으로 a = a-b와 동일하다는 점을 알아두세요. 하지만 객체를 a에 할당하려는 것을 미리 알고 있다면 변수 이름은 a - b의 맹목적인 작업보다 더 효율적일 수 있습니다. 예를 들어, 최소한의 이점은 새 개체 생성을 피할 수 있다는 것입니다. 개체를 제자리에서 수정할 수 있는 경우 self를 반환하면 새 객체를 재구성하는 것보다 더 효율적입니다. 따라서 할당 작업의 왼쪽에 정의된 경우(일반적으로 lvalue라고 함) Python은 __isub__() 메서드를 제공하고 오른쪽의 값(일반적으로 rvalue라고 함)은 다음과 같습니다. 따라서 a -= b의 경우 a.__isub__(b) 호출을 시도합니다.

호출 결과가 NotImplemented이거나 결과가 전혀 없으면 Python은 실패합니다. 일반 이진 산술 연산으로 돌아가기: a - b (주석: 이진 연산에 대한 저자의 기사, 번역은 여기에 있음)

결국 어떤 방법을 사용하든 반환 값은 다음과 같습니다.

다음은 간단한 의사 코드입니다. a -= b는 다음과 같이 분해됩니다.

PyObject *PyNumber_InPlacePower(PyObject *v, PyObject *w, PyObject *z){    if (v->ob_type->tp_as_number &&
        v->ob_type->tp_as_number->nb_inplace_power != NULL) {        return ternary_op(v, w, z, NB_SLOT(nb_inplace_power), "**=");
    }    else {        return ternary_op(v, w, z, NB_SLOT(nb_power), "**=");
    }
}复制代码
로그인 후 복사

이러한 메소드를 일반화하세요

이미 이진 산술이 구현되어 있으므로 향상된 산술을 일반화하는 것은 그리 복잡하지 않습니다.

이진 산술 연산 함수를 전달하고 몇 가지 자체 검사(발생할 수 있는 TypeError 처리 포함)를 수행하면 다음과 같이 깔끔하게 요약될 수 있습니다. rrreee이것은 정의된 _create_binary_inplace_op(__ sub__)를 지원하고 함수 이름, 호출되는 __i*__ 함수, 이진 산술 연산 시 문제가 발생할 때 호출할 수 있는 항목 등을 추론할 수 있습니다.

**=를 사용하는 사람이 거의 없다는 사실을 발견했습니다

이 기사의 코드를 작성하는 동안 **=에 대한 이상한 테스트에 직면했습니다. 실수. __pow__가 적절하게 호출되는지 확인하는 모든 테스트 중에서 Python 표준 라이브러리의 operator 모듈에 대해 실패하는 테스트가 있습니다. 🎜🎜내 코드는 일반적으로 문제가 없으며, 코드와 CPython의 코드 사이에 차이가 있으면 일반적으로 내가 뭔가 잘못하고 있다는 의미입니다. 🎜🎜그러나 코드 문제를 아무리 주의 깊게 해결해도 테스트는 통과하는데 표준 라이브러리는 실패하는 이유를 정확히 알 수 없습니다. 🎜🎜저는 CPython 내부에서 무슨 일이 일어나고 있는지 자세히 살펴보기로 했습니다. 디스어셈블리 바이트코드에서 시작: 🎜rrreee🎜이를 통해 평가 루프에서 INPLACE_POWER를 찾았습니다: 🎜rrreee🎜출처: github.com/python/cpyt…🎜🎜그런 다음 PyNumber_InPlacePower( ): 🎜rrreee🎜출처: github.com/python/cpyt…🎜🎜안심 ~ 코드는 __ipow__가 정의되면 호출되지만 __ipow__가 없는 경우에만 __pow__가 호출된다는 것을 보여줍니다. 🎜🎜그러나 올바른 접근 방식은 다음과 같습니다. 🎜 __ipow__를 호출할 때 문제가 발생하여 NotImplemented를 반환하거나 전혀 반환이 없는 경우 __pow__ 및 __rpow__를 호출해야 합니다. 🎜🎜🎜즉, 위 코드는 __ipow__가 있을 때 실수로 a**b의 대체 의미를 건너뛰는 것입니다! 🎜🎜사실 이 문제는 부분적으로 발견되었으며 약 11개월 전에 버그가 접수되었습니다. 문제를 해결하고 python-dev에 문서화했습니다. 🎜🎜현재로서는 Python 3.10에서 이 문제가 수정될 것으로 보입니다. 또한 3.8 및 3.9 문서에 **= 버그가 있다는 알림을 추가해야 합니다(이 문제는 오래 전에 있었을 수도 있지만 이전 Python 버전에는 보안 전용 유지 관리 모드가 있으므로 설명서는 변경되지 않습니다. 🎜🎜수정된 코드는 의미론적 변경이므로 이식되지 않을 가능성이 높으며 누군가 실수로 문제가 있는 의미론에 의존했는지 알기 어렵습니다. 하지만 이 문제가 발견되기까지 너무 오랜 시간이 걸렸습니다. 이는 **=가 널리 사용되지 않았음을 의미합니다. 그렇지 않으면 문제가 오래 전에 발견되었을 것입니다. 🎜🎜🎜프로그래밍 학습에 대해 더 자세히 알고 싶다면 🎜php training🎜 칼럼을 주목해주세요! 🎜🎜🎜

위 내용은 강화된 연산 할당 '-=' 연산에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Linux 시스템에서 Python 통역사를 삭제할 수 있습니까? Linux 시스템에서 Python 통역사를 삭제할 수 있습니까? Apr 02, 2025 am 07:00 AM

Linux 시스템과 함께 제공되는 Python 통역사를 제거하는 문제와 관련하여 많은 Linux 배포판이 설치 될 때 Python 통역사를 사전 설치하고 패키지 관리자를 사용하지 않습니다 ...

파이썬에서 맞춤형 데코레이터의 Pylance 유형 감지 문제를 해결하는 방법은 무엇입니까? 파이썬에서 맞춤형 데코레이터의 Pylance 유형 감지 문제를 해결하는 방법은 무엇입니까? Apr 02, 2025 am 06:42 AM

Pylance 유형 감지 문제 솔루션 Python 프로그래밍에서 사용자 정의 데코레이터를 사용할 때 Decorator는 행을 추가하는 데 사용할 수있는 강력한 도구입니다 ...

Python 3.6 피클 파일로드 오류 modulenotfounderRor : 피클 파일 '__builtin__'를로드하면 어떻게해야합니까? Python 3.6 피클 파일로드 오류 modulenotfounderRor : 피클 파일 '__builtin__'를로드하면 어떻게해야합니까? Apr 02, 2025 am 06:27 AM

Python 3.6에 피클 파일 로딩 3.6 환경 오류 : ModulenotFounderRor : nomodulename ...

Fastapi와 Aiohttp는 동일한 글로벌 이벤트 루프를 공유합니까? Fastapi와 Aiohttp는 동일한 글로벌 이벤트 루프를 공유합니까? Apr 02, 2025 am 06:12 AM

파이썬 비동기 라이브러리 사이의 호환성 문제 파이썬에서 비동기 프로그래밍은 동시성과 I/O의 프로세스가되었습니다 ...

파이썬에서 신호를 통해 부모 프로세스를 죽인 후 아동 프로세스가 종료되도록하는 방법은 무엇입니까? 파이썬에서 신호를 통해 부모 프로세스를 죽인 후 아동 프로세스가 종료되도록하는 방법은 무엇입니까? Apr 02, 2025 am 06:39 AM

아동 프로세스의 문제와 해결책은 신호를 사용하여 부모 프로세스를 죽일 때 계속 실행됩니다. Python 프로그래밍에서 신호를 통해 부모 프로세스를 죽인 후에도 아동 프로세스는 여전히 ...

Python 3.6에 피클 파일을로드 할 때 '__builtin__'모듈을 찾을 수없는 경우 어떻게해야합니까? Python 3.6에 피클 파일을로드 할 때 '__builtin__'모듈을 찾을 수없는 경우 어떻게해야합니까? Apr 02, 2025 am 07:12 AM

Python 3.6에 피클 파일로드 3.6 환경 보고서 오류 : modulenotfounderror : nomodulename ...

See all articles