Python 연산 자 를 정확하게 다시 불 러 오 는 방법 예시 상세 설명
연산 자 를 다시 불 러 오 는 것 은 모두 가 낯 설 지 않다 고 믿 습 니 다.연산 자 를 다시 불 러 오 는 역할 은 사용자 가 정의 하 는 대상 에 게 접두사 연산 자(예 를 들 어+와|)나 일원 연산 자(예 를 들 어-와~)를 사용 하 게 하 는 것 입 니 다.파 이 썬 에서 함수 호출((),속성 접근(.)과 요소 접근/절편([])도 연산 자 입 니 다.
우 리 는 Vector 류 를 위해 몇 개의 연산 자 를 간략하게 실현 했다.add__ 와mul__ 방법 은 특수 한 방법 으로 연산 자 를 다시 싣 는 방법 을 보 여주 기 위해 서 였 으 나,일부 작은 문 제 는 우리 에 게 무시 되 었 다.그 밖 에 우리 가 정의 한
Vector2d.__eq__
방법 은Vector(3, 4) == [3, 4]
이 사실(True)이 라 고 생각 하 는데 이것 은 합 리 적 이지 않 을 수 있다.다음은 상세 한 소 개 를 함께 살 펴 보 겠 습 니 다.연산 자 과부하 기초
어떤 바닥 에 서 는 연산 자 를 다시 싣 는 명성 이 좋 지 않다.이 언어 특성 이 남용 되 어 프로그래머 를 곤 혹 스 럽 게 하고 결함 과 예상 치 못 한 성능 병목 을 초래 할 수 있다.그러나 적절하게 사용 하면 API 가 잘 사용 되 고 코드 가 쉽게 읽 힐 수 있다.Python 은 약간의 제한 을 가 하여 유연성,가용성 과 안전성 방면 의 균형 을 잘 잡 았 다.
일원 연산 자
-(__neg__)
일원 취 음산 연산 자.만약 x 가-2 라면-x==2.
+(__pos__)
일원 취 정 산술 연산 자.보통 x==+x 이지 만 예외 도 있 습 니 다.궁금 하 다 면'x 와+x 가 언제 같 지 않 습 니까?'란 을 읽 으 세 요.
~(__invert__)
정수 에 대해 비트 에 따라 반 을 취하 고~x==-(x+1)로 정의 합 니 다.만약 x 가 2 라면~x==-3.
일원 연산 자 를 지원 하 는 것 은 매우 간단 하 며,상응하는 특수 한 방법 만 실현 하면 된다.이 특수 한 방법 들 은 하나의 인자,self 만 있다.그리고 그 유형 에 맞 는 논 리 를 사용 하여 이 루어 진다.그러나 연산 자의 기본 규칙 을 지 켜 야 한다.항상 새로운 대상 으로 돌아 가 야 한다.self 를 수정 할 수 없 으 며,적당 한 형식의 새로운 인 스 턴 스 를 만 들 고 되 돌려 야 한 다 는 것 이다.
-와+에 있어 서 결 과 는 self 와 같은 인 스 턴 스 일 수 있 습 니 다.대부분의 경우,+self 의 던 전 으로 돌아 가 는 것 이 좋 습 니 다.abs(...)의 결 과 는 스칼라 일 것 입 니 다.그러나~에 있어 서 어떤 결과 가 합 리 적 이 라 고 말 하기 어렵다.왜냐하면 정 수 를 처리 하 는 위치 가 아 닐 수도 있 기 때문이다.예 를 들 어 ORM 에서 SQL WHERE 자 구 는 반 집합 으로 돌아 가 야 한다.
def __abs__(self):
return math.sqrt(sum(x * x for x in self))
def __neg__(self):
return Vector(-x for x in self) # -v, Vector , self
def __pos__(self):
return Vector(self) # +v, Vector , self
x 와+x 는 언제 같 지 않 습 니까?모든 사람 은 x==+x,그리고 Python 에서 거의 모든 상황 에서 그렇다 고 생각 합 니 다.하지만 표준 라 이브 러 리 에서 x 두 예 를 찾 았 습 니 다!=+x 의 경우.
첫 번 째 예 는
decimal.Decimal
류 와 관계 가 있다.x 가 Decimal 인 스 턴 스 라면 산술 연산 의 문맥 에서 만 든 다음 에 서로 다른 문맥 에서+x 를 계산 하면 x!=+x。예 를 들 어 x 가 있 는 문맥 은 특정한 정 도 를 사용 하고+x 를 계산 할 때 정밀도 가 변 했다.예 를 들 어 아래 의 것 이다.🌰산술 연산 문맥 의 정밀도 변 화 는 x 가+x 와 같 지 않 을 수 있 습 니 다.
>>> import decimal
>>> ctx = decimal.getcontext() #
>>> ctx.prec = 40 # 40
>>> one_third = decimal.Decimal('1') / decimal.Decimal('3') # 1/3
>>> one_third
Decimal('0.3333333333333333333333333333333333333333') # , 40
>>> one_third == +one_third #one_third = +one_thied TRUE
True
>>> ctx.prec = 28 # 28
>>> one_third == +one_third #one_third = +one_thied FalseFalse >>> +one_third Decimal('0.3333333333333333333333333333') # +one_third, 28
각+onethird 표현 식 은 모두 one 을 사용 합 니 다.third 의 값 은 새 Decimal 인 스 턴 스 를 만 들 지만 현재 산술 연산 컨 텍스트 의 정 도 를 사용 합 니 다.x != +x 의 두 번 째 예 는 collections.counter 문서 에서(https://docs.python.org/3/library/collections.html#collections.Counter)。클래스 는 몇 개의 산술 연산 자 를 실 현 했 습 니 다.예 를 들 어 접두사 연산 자+는 두 개의 Counter 인 스 턴 스 의 계수 기 를 합 친 것 입 니 다.그러나 실 용적 인 측면 에서 Counter 를 더 하면 마이너스 와 0 값 수 는 결과 에서 삭 제 됩 니 다.한편,1 원 연산 자+는 빈 Counter 를 추가 하 는 것 과 같 기 때문에 새로운 Counter 를 만 들 고 0 이상 의 카운터 만 유지 합 니 다.
🌰 1 원 연산 자+새 Counter 인 스 턴 스 를 얻 었 지만 0 값 과 마이너스 카운터 가 없습니다.
>>> from collections import Counter
>>> ct = Counter('abracadabra')
>>> ct['r'] = -3
>>> ct['d'] = 0
>>> ct
Counter({'a': 5, 'r': -3, 'b': 2, 'c': 1, 'd': 0})
>>> +ct
Counter({'a': 5, 'b': 2, 'c': 1})
과부하 벡터 덧셈 연산 자+두 개의 유클리드 벡터 를 합치 면 새로운 벡터 를 얻 을 수 있 는데,그것 의 각 분량 은 두 개의 벡터 중 상응하는 분량 의 합 이다.예 를 들 면:
>>> v1 = Vector([3, 4, 5])
>>> v2 = Vector([6, 7, 8])
>>> v1 + v2
Vector([9.0, 11.0, 13.0])
>>> v1 + v2 == Vector([3+6, 4+7, 5+8])
True
이러한 기본 적 인 요 구 를 확인 한 후,add__ 방법의 실현 은 짧 고 간결 하 다.🌰 아래 와 같다
def __add__(self, other):
pairs = itertools.zip_longest(self, other, fillvalue=0.0) # ,a self,b other, , fillvalue
return Vector(a + b for a, b in pairs) # pairs
또한 Vector 를 원 그룹 이나 숫자 를 생 성 할 수 있 는 교체 대상 에 추가 할 수 있 습 니 다.
# Vector
def __add__(self, other):
pairs = itertools.zip_longest(self, other, fillvalue=0.0) # ,a self,b other, , fillvalue
return Vector(a + b for a, b in pairs) # pairs
def __radd__(self, other): # __add__
return self + other
__radd__ 보통 이렇게 간단 합 니 다.적당 한 연산 자 를 직접 호출 합 니 다.여기 서 의뢰add__。교환 가능 한 연산 자 는 모두 이렇게 할 수 있다.숫자 와 벡터 를 처리 할 때+교환 할 수 있 지만 연결 순 서 는 안 됩 니 다.적재량 곱셈 연산 자*
Vector([1,2,3])*x 는 무슨 뜻 입 니까?x 가 숫자 라면 스칼라 적(scalar produt)을 계산 하 는 것 입 니 다.결 과 는 새로운 Vector 인 스 턴 스 이 고 각 분량 은 x-이것 을 요소 급 곱셈(element wise multiplication)이 라 고 합 니 다.
>>> v1 = Vector([1, 2, 3])
>>> v1 * 10
Vector([10.0, 20.0, 30.0])
>>> 11 * v1
Vector([11.0, 22.0, 33.0])
Vector 작업 수의 축적 과 관련 된 것 은 두 개의 벡터 의 점 적(dotprodut)이 라 고 합 니 다.벡터 하 나 를 1 로 보면×N 매트릭스,다른 벡터 를 N 으로 봅 니 다.×1 행렬,그러면 행렬 곱셈 이다.NumPy 등 라 이브 러 리 는 현재 이 두 가지 의미 의*를 다시 불 러 오지 않 고*로 스칼라 적 을 계산 하 는 것 입 니 다.예 를 들 어 NumPy 에서 점 적 사용numpy.dot()
함수 로 계산한다.스칼라 적 화제 로 돌아가다.우 리 는 여전히 가장 간단 하고 사용 가능 한 을 먼저 실현 한다.mul__ 와rmul__방법:
def __mul__(self, scalar):
if isinstance(scalar, numbers.Real):
return Vector(n * scalar for n in self)
else:
return NotImplemented
def __rmul__(self, scalar):
return self * scalar
이 두 가지 방법 은 확실히 사용 할 수 있 지만,호 환 되 지 않 는 조작 수 를 제공 할 때 문제 가 생 길 수 있다.scalar 매개 변수의 값 이 숫자 라면 부동 소수점 과 곱 한 적 은 다른 부동 소수점 입 니 다(Vector 류 는 내부 에서 부동 소수점 배열 을 사용 하기 때 문 입 니 다).따라서 복 수 를 사용 할 수 없 지만 int,bool(int 의 하위 클래스),심지어fractions.Fraction
인 스 턴 스 등 스칼라 일 수 있 습 니 다.점 적 에 필요 한@기 호 를 제공 합 니 다(예 를 들 어 a@b 는 a 와 b 의 점 적 입 니 다).@연산 자 는 특별한 방법 으로matmul__、__rmatmul__ 와imatmul__ 지원 을 제공 합 니 다.이름 은"matrix multiplication"(행렬 곱셈)에서 가 져 옵 니 다.
>>> va = Vector([1, 2, 3])
>>> vz = Vector([5, 6, 7])
>>> va @ vz == 38.0 # 1*5 + 2*6 + 3*7
True
>>> [10, 20, 30] @ vz
380.0
>>> va @ 3
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for @: 'Vector' and 'int'
다음은 해당 하 는 특수 한 방법의 코드 입 니 다.
>>> va = Vector([1, 2, 3])
>>> vz = Vector([5, 6, 7])
>>> va @ vz == 38.0 # 1*5 + 2*6 + 3*7
True
>>> [10, 20, 30] @ vz
380.0
>>> va @ 3
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for @: 'Vector' and 'int'
많은 비교 연산 자Python 해석 기 는 여러 비교 연산 자(==,!=,>,<,>=,<=)의 처 리 는 앞의 글 과 유사 하지만 두 가지 측면 에서 중대 한 차이 가 있다.
패 킷
접두사 연산 자
정방 향 방법 호출
역방향 방법 호출
예비 메커니즘
대등 성
a == b
a.__eq__(b)
b.__eq__(a)
반환 id(a)==id(b)
a != b
a.__ne__(b)
b.__ne__(a)
not 로 돌아 가기(a==b)
정렬
a > b
a.__gt__(b)
b.__lt__(a)
TypeError 던 지기
a < b
a.__lt__(b)
b.__gt__(a)
TypeError 던 지기
a >= b
a.__ge__(b)
b.__le__(a)
TypeError 던 지기
a <= b
a.__le__(b)
b.__ge__(a)
T type 오류 던 지기
아래 거 봐.🌰
from array import array
import reprlib
import math
import numbers
import functools
import operator
import itertools
class Vector:
typecode = 'd'
def __init__(self, components):
self._components = array(self.typecode, components)
def __iter__(self):
return iter(self._components)
def __repr__(self):
components = reprlib.repr(self._components)
components = components[components.find('['):-1]
return 'Vector({})'.format(components)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return (bytes([ord(self.typecode)]) + bytes(self._components))
def __eq__(self, other):
return (len(self) == len(other) and all(a == b for a, b in zip(self, other)))
def __hash__(self):
hashes = map(hash, self._components)
return functools.reduce(operator.xor, hashes, 0)
def __add__(self, other):
pairs = itertools.zip_longest(self, other, fillvalue=0.0) # ,a self,b other, , fillvalue
return Vector(a + b for a, b in pairs) # pairs
def __radd__(self, other): # __add__
return self + other
def __mul__(self, scalar):
if isinstance(scalar, numbers.Real):
return Vector(n * scalar for n in self)
else:
return NotImplemented
def __rmul__(self, scalar):
return self * scalar
def __matmul__(self, other):
try:
return sum(a * b for a, b in zip(self, other))
except TypeError:
return NotImplemented
def __rmatmul__(self, other):
return self @ other
def __abs__(self):
return math.sqrt(sum(x * x for x in self))
def __neg__(self):
return Vector(-x for x in self) # -v, Vector , self
def __pos__(self):
return Vector(self) # +v, Vector , self
def __bool__(self):
return bool(abs(self))
def __len__(self):
return len(self._components)
def __getitem__(self, index):
cls = type(self)
if isinstance(index, slice):
return cls(self._components[index])
elif isinstance(index, numbers.Integral):
return self._components[index]
else:
msg = '{.__name__} indices must be integers'
raise TypeError(msg.format(cls))
shorcut_names = 'xyzt'
def __getattr__(self, name):
cls = type(self)
if len(name) == 1:
pos = cls.shorcut_names.find(name)
if 0 <= pos < len(self._components):
return self._components[pos]
msg = '{.__name__!r} object has no attribute {!r}'
raise AttributeError(msg.format(cls, name))
def angle(self, n):
r = math.sqrt(sum(x * x for x in self[n:]))
a = math.atan2(r, self[n-1])
if (n == len(self) - 1 ) and (self[-1] < 0):
return math.pi * 2 - a
else:
return a
def angles(self):
return (self.angle(n) for n in range(1, len(self)))
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('h'):
fmt_spec = fmt_spec[:-1]
coords = itertools.chain([abs(self)], self.angles())
outer_fmt = '<{}>'
else:
coords = self
outer_fmt = '({})'
components = (format(c, fmt_spec) for c in coords)
return outer_fmt.format(', '.join(components))
@classmethod
def frombytes(cls, octets):
typecode = chr(octets[0])
memv = memoryview(octets[1:]).cast(typecode)
return cls(memv)
va = Vector([1.0, 2.0, 3.0])
vb = Vector(range(1, 4))
print('va == vb:', va == vb) # Vector
t3 = (1, 2, 3)
print('va == t3:', va == t3)
print('[1, 2] == (1, 2):', [1, 2] == (1, 2))
위의 코드 가 실 행 된 결 과 는 다음 과 같 습 니 다.
va == vb: True
va == t3: True
[1, 2] == (1, 2): False
Python 자체 에서 단 서 를 찾 아 보 니[1,2] == (1, 2)
결 과 는 False 였 다.그래서 우 리 는 보수 적 으로 유형 검 사 를 해 야 한다.두 번 째 조작 수가 Vector 인 스 턴 스(또는 Vector 하위 클래스 의 인 스 턴 스)라면eq__ 방법의 현재 논리.그렇지 않 으 면 NotImplemented 로 돌아 가 Python 에 게 처리 하도록 합 니 다.🌰 vector_v8.py:Vector 류 의 개선eq__ 방법.
def __eq__(self, other):
if isinstance(other, Vector): # Vector
return (len(self) == len(other) and all(a == b for a, b in zip(self, other)))
else:
return NotImplemented # , NotImplemented
개 선 된 코드 실행 결과:
>>> va = Vector([1.0, 2.0, 3.0])
>>> vb = Vector(range(1, 4))
>>> va == vb
True
>>> t3 = (1, 2, 3)
>>> va == t3
False
증분 할당 연산 자Vector 클래스 는 증분 할당 연산 자+=과*=을 지원 합 니 다.예 는 다음 과 같 습 니 다.
🌰 증분 할당 은 변경 할 수 없 는 목 표를 수정 하지 않 고 새 인 스 턴 스 를 만 든 다음 다시 연결 합 니 다.
>>> v1 = Vector([1, 2, 3])
>>> v1_alias = v1 # , Vector([1, 2, 3])
>>> id(v1) # v1 Vector ID
>>> v1 += Vector([4, 5, 6]) #
>>> v1 #
Vector([5.0, 7.0, 9.0])
>>> id(v1) # Vector
>>> v1_alias # v1_alias, Vector
Vector([1.0, 2.0, 3.0])
>>> v1 *= 11 #
>>> v1 # , , Vector
Vector([55.0, 77.0, 99.0])
>>> id(v1)
전체 코드:
from array import array
import reprlib
import math
import numbers
import functools
import operator
import itertools
class Vector:
typecode = 'd'
def __init__(self, components):
self._components = array(self.typecode, components)
def __iter__(self):
return iter(self._components)
def __repr__(self):
components = reprlib.repr(self._components)
components = components[components.find('['):-1]
return 'Vector({})'.format(components)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return (bytes([ord(self.typecode)]) + bytes(self._components))
def __eq__(self, other):
if isinstance(other, Vector):
return (len(self) == len(other) and all(a == b for a, b in zip(self, other)))
else:
return NotImplemented
def __hash__(self):
hashes = map(hash, self._components)
return functools.reduce(operator.xor, hashes, 0)
def __add__(self, other):
pairs = itertools.zip_longest(self, other, fillvalue=0.0)
return Vector(a + b for a, b in pairs)
def __radd__(self, other):
return self + other
def __mul__(self, scalar):
if isinstance(scalar, numbers.Real):
return Vector(n * scalar for n in self)
else:
return NotImplemented
def __rmul__(self, scalar):
return self * scalar
def __matmul__(self, other):
try:
return sum(a * b for a, b in zip(self, other))
except TypeError:
return NotImplemented
def __rmatmul__(self, other):
return self @ other
def __abs__(self):
return math.sqrt(sum(x * x for x in self))
def __neg__(self):
return Vector(-x for x in self)
def __pos__(self):
return Vector(self)
def __bool__(self):
return bool(abs(self))
def __len__(self):
return len(self._components)
def __getitem__(self, index):
cls = type(self)
if isinstance(index, slice):
return cls(self._components[index])
elif isinstance(index, numbers.Integral):
return self._components[index]
else:
msg = '{.__name__} indices must be integers'
raise TypeError(msg.format(cls))
shorcut_names = 'xyzt'
def __getattr__(self, name):
cls = type(self)
if len(name) == 1:
pos = cls.shorcut_names.find(name)
if 0 <= pos < len(self._components):
return self._components[pos]
msg = '{.__name__!r} object has no attribute {!r}'
raise AttributeError(msg.format(cls, name))
def angle(self, n):
r = math.sqrt(sum(x * x for x in self[n:]))
a = math.atan2(r, self[n-1])
if (n == len(self) - 1 ) and (self[-1] < 0):
return math.pi * 2 - a
else:
return a
def angles(self):
return (self.angle(n) for n in range(1, len(self)))
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('h'):
fmt_spec = fmt_spec[:-1]
coords = itertools.chain([abs(self)], self.angles())
outer_fmt = '<{}>'
else:
coords = self
outer_fmt = '({})'
components = (format(c, fmt_spec) for c in coords)
return outer_fmt.format(', '.join(components))
@classmethod
def frombytes(cls, octets):
typecode = chr(octets[0])
memv = memoryview(octets[1:]).cast(typecode)
return cls(memv)
총결산이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
로마 숫자를 정수로 또는 그 반대로 변환그 중 하나는 로마 숫자를 정수로 변환하는 함수를 만드는 것이었고 두 번째는 그 반대를 수행하는 함수를 만드는 것이었습니다. 문자만 포함합니다'I', 'V', 'X', 'L', 'C', 'D', 'M' ; 문자열이 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.