Python magic method

Refer to the official documents of python3.7

Basic method

__class__

See__ name__

__name__

1) Current module name, of the directly running module__ name__ Is'__ main__‘, Other modules are 'module name' or 'package name. Module name'

# tmpPkg.tmp.py

print(f'tmp.py {__name__}')



# main.py

import tmpPkg.tmp

print(f'main.py {__name__}')



> python .\main.py
tmp.py tmpPkg.tmp
main.py __main__

2) Object class name or function name

class TestC:
    pass


def testM():
    pass


print(type(TestC()))
print(TestC().__class__)
# <class '__main__.TestC'>
# <class '__main__.TestC'>

print(type(TestC()).__name__)
print(TestC().__class__.__name__)
# TestC
# TestC

print(testM.__name__)
# testM

__call__(self[, args...])

class Test:
    def __call__(self, __x, __y):
        return f'{__x} + {__y} = {__x + __y}'


t = Test()
print(t(3, 5))
# 3 + 5 = 8

__new__(cls[, ...])

See__ del__(self)

__init__(self[, ...])

See__ del__(self)

__del__(self)

new create object, static method (special here, don't mark @staticmethod)
*The new method needs to return an instance of cls. The general writing is: return super() New (cls, *args, *kwargs), otherwise__ init__ Function will not execute)
Init initializes the object, and also pay attention to calling the parent class__ init__ method
Del destroy object (not recommended): called when the reference number is 0 to destroy the object. If the parent class contains del methods, pay attention to calling the Del methods of the parent class (object class has no del methods)

Execution order: code block new method init method... del method

class Test:
    print('Execute code block')

    def __new__(cls, *args, **kwargs):
        print('implement new')
        return super().__new__(cls, *args, **kwargs)

    def __init__(self):
        print('implement init')
        super(Test, self).__init__()

    def __del__(self):
        print('implement del')
        # If the parent class contains del methods, pay attention to calling the Del methods of the parent class (object class has no del methods), so do not call them here
        # super(Test, self).__del__()


Test()
# Execute code block
# Execute new
# Execute init
# Execute del

__repr__(self)

See__ str__(self)

__str__(self)

__ repr__: The 'formal string' describing the object is usually used for debugging, so it is informative and clear.
__ str__: The 'informal string' describing the object is usually printed, so it is convenient to represent the brief introduction of information. If__ str__ Not implemented but__ repr__ Implement, then__ str__ Will use__ repr__ Returned string

class Test:
    def __repr__(self):
        return 'aaa'

    def __str__(self):
        return 'bbb'


t = Test()


print(repr(t))
print(str(t))
print(t)
# Not implemented__ repr__, Not implemented__ str__
# <__main__.Test object at 0x000002056CEEF588>
# <__main__.Test object at 0x000002056CEEF588>
# <__main__.Test object at 0x000002056CEEF588>

# Implementation__ repr__, Not implemented__ str__
# aaa
# aaa
# aaa

# Not implemented__ repr__, Implementation__ str__
# <__main__.Test object at 0x00000228D90B8608>
# bbb
# bbb

# Implementation__ repr__, Implementation__ str__
# aaa
# bbb
# bbb

__bytes__(self)

See__ bool__(self)

__bool__(self)

__ bytes__: Convert the object to bytes and call through bytes(x)
__ bool__: Returns True/False, if__ bool__ If not, call__ len__ Method, so the values of [] (empty list), () (empty tuple), '(or' ', empty string), {} (empty set or empty dictionary) are False. In addition, the defined False values include False (standard true value), None, and 0.

class Test:
    def __bytes__(self):
        return b'x01x02'

    def __bool__(self):
        return True


# Implementation__ bytes__
t = Test()
print(bytes(t))
# b'x01x02'
print(bool(t))
# True

__fspath__(self)

Return the path str of the file system where the object is located

import os


class Test:
    def __fspath__(self):
        return 'home/malloclu/TestObject'


print(os.fspath(Test()))
# home/malloclu/TestObject

(shallow) replication and deep replication

Type already exists

Copy (shallow) copy, deepcopy deep copy

from copy import copy, deepcopy

# Basic data type
a = 1.2

# 1. Quotation
b1 = a
# 2. (shallow) copy
b2 = copy(a)
# 3. Deep copy
b3 = deepcopy(a)

a = 3.4

# Because the basic data type has only assignment and no reference, the values of the three methods have not changed
print(f'{b1}\n{b2}\n{b3}\n')
# 1.2
# 1.2
# 1.2


# Simple objects (such as single-layer containers)
a = [1, 2, 3]

# 1. Quotation
b1 = a
# 2. (shallow) copy
b2 = copy(a)
# 3. Deep copy
b3 = deepcopy(a)

a[1] = 4

# Only the reference method value has changed, because it and a point to the same object in memory
print(f'{b1}\n{b2}\n{b3}\n')
# [1, 4, 3]
# [1, 2, 3]
# [1, 2, 3]


# Multi tier containers (e.g. multi tier containers, dictionary nested lists)
a = {'k': [1, 2, 3]}

# 1. Quotation
b1 = a
# 2. (shallow) copy
b2 = copy(a)
# 3. Deep copy
b3 = deepcopy(a)

a['k'][1] = 4

# The values of reference b1 and (shallow) copy b2 have changed. Among them, b1 refers to the same object in memory as a, b2 refers to the dictionary key generated by shallow copy to [1, 2, 3] of a, and b3 refers to the newly generated dictionary key generated by deep copy to [1, 2, 3].
print(f'{b1}\n{b2}\n{b3}\n')
# {'k': [1, 4, 3]}
# {'k': [1, 4, 3]}
# {'k': [1, 2, 3]}

memo

The memory id to object correspondence is preserved in the memo dictionary in order to reconstruct the complex object graph perfectly. (if you don't want to know the corresponding relationship, this parameter can not be used when deepcopy(x, memo={}) is used)

from copy import deepcopy

a = {'k': [1, 2, 3]}
memo={}
b = deepcopy(a, memo=memo)
print(memo)
# {2092202719048: [1, 2, 3], 
# 2092195593896: {'k': [1, 2, 3]}, 
# 2092195593976: [[1, 2, 3], {'k': [1, 2, 3]}]}

Custom type

(shallow) replication and deep replication should be implemented separately__ copy__(self) and__ deepcopy__(self, memodict={}) method

from copy import copy, deepcopy


class Test:
    def __init__(self, val):
        self.val = val

    def __copy__(self):
        return Test(self.val)

    def __deepcopy__(self, memodict={}):
        return Test(deepcopy(self.val, memodict))

    def __str__(self):
        return str(self.val)


# Simple objects (such as single-layer containers)
a = Test([1, 2, 3])

# 1. Quotation
b1 = a
# 2. (shallow) copy
b2 = copy(a)
# 3. Deep copy
b3 = deepcopy(a)

a.val[1] = 4

# The values of reference b1 and (shallow) copy B2 have changed. Among them, b1 and a point to the same object in memory, B2 because b2.val and a.val point to the same object in memory, and B3 because b3.val points to the newly generated [1, 2, 3].
print(f'{b1}\n{b2}\n{b3}\n')
# [1, 4, 3]
# [1, 4, 3]
# [1, 2, 3]

Operator overloading

Binary operator

Method list

SymboloperationInverse operationIncremental assignment operation (e.g. + =, - = etc.)remarks
+__add__(self, other)__iadd__(self, other)__radd__(self, other)
-__sub__(self, other)__isub__(self, other)__rsub__(self, other)
*__mul__(self, other)__imul__(self, other)__rmul__(self, other)
@__matmul__(self, other)__imatmul__(self, other)__rmatmul__(self, other)numpy type implements this method, that is, matrix multiplication, basic data type and list are not implemented
/__truediv__(self, other)__itruediv__(self, other)__rtruediv__(self, other)divide
//__floordiv__(self, other)__ifloordiv__(self, other)__rfloordiv__(self, other)Divide by
%__mod__(self, other)__imod__(self, other)__rmod__(self, other)
divmod(__x, __y)__divmod__(self, other)__rdivmod__(self, other)Return (quotient, remainder)
**__pow__(self, other[, modulo])__ipow__(self, other[, modulo])__rpow__(self, other[, modulo])modulo is complemented by the other power of self or the other power of self
<<__lshift__(self, other)__ilshift__(self, other)__rlshift__(self, other)Shift left
>>__rshift__(self, other)__irshift__(self, other)__rrshift__(self, other)Shift right
&__and__(self, other)__iand__(self, other)__rand__(self, other)Bitwise AND
^__xor__(self, other)__ixor__(self, other)__rxor__(self, other)Bitwise XOR
``__or__(self, other)__ior__(self, other)__ror__(self, other)
<__lt__(self, other)
<=__le__(self, other)
==__eq__(self, other)
!=__ne__(self, other)
>__gt__(self, other)
>=__ge__(self, other)

Operation & inverse operation

Take + as an example. A + B requires that a is overloaded__ add__ Operator or B overloaded__ radd__ Operator (if both are overloaded, use a's _uadd _uoperator) [code 1]

In overloaded operator methods, the type of another operand should be judged, and an exception should be thrown manually when no code is written for this type [code 2]
(for example, A+B, A belongs to classA, B belongs to classB, and A overloads the _add_method. When executing A+C (C belongs to classC), A can also call the__ add__ Method, but A's__ add__ The function body is written for classB, so it is likely that various exceptions will occur when the execution fails. Therefore, it is recommended to use A__ add__ In the function body, first judge the type of another operand, write different operations for different types, and manually throw exceptions for types that are not considered.)

class TestA:
    def __add__(self, other):
        return f'add TestA + {type(other).__name__}'


class TestB:
    pass


class TestC:
    def __radd__(self, other):
        return f'radd {type(other).__name__} + TestC'


# Left operand overloaded__ add__, Will be called, and there is no requirement for overload of right operation
print(TestA() + TestA())
print(TestA() + TestB())
print(TestA() + TestC())
print(TestA() + 1)
# add TestA + TestA
# add TestA + TestB
# add TestA + TestC
# add TestA + int

# Left operand not overloaded__ add__, If the right operation is overloaded__ radd__ Will be called, otherwise an error will be reported
print(TestB() + TestA())
print(TestB() + TestB())
print(TestB() + TestC())
print(TestB() + 1)
# TypeError: unsupported operand type(s) for +: 'TestB' and 'TestA'
# TypeError: unsupported operand type(s) for +: 'TestB' and 'TestB'
# radd TestB + TestC
# TypeError: unsupported operand type(s) for +: 'TestB' and 'int'
# Not recommended
class TestA:
    def __add__(self, other):
        if isinstance(other, int):
            return f'add TestA + {type(other).__name__}'


print(TestA() + 1)
print(TestA() + 'tmp')
# add TestA + int
# None


# **Recommend**
class TestB:
    def __add__(self, other):
        if isinstance(other, int):
            return f'add TestA + {type(other).__name__}'
        raise TypeError(f"unsupported operand type(s) for +: 'TestA' and '{type(other).__name__}'")


print(TestB() + 1)
print(TestB() + 'tmp')
# add TestA + int
# TypeError: unsupported operand type(s) for +: 'TestA' and 'str'

Incremental assignment operation

class TestA:
    def __init__(self, val):
        self.val = val

    def __add__(self, other):
        if isinstance(other, TestA):
            return TestA(self.val + other.val)
        raise TypeError(f"unsupported operand type(s) for +: 'TestA' and '{type(other).__name__}'")


A = TestA(1)
B = TestA(2)
C = A + B
print(C.val)


class TestB:
    def __init__(self, val):
        self.val = val

    def __iadd__(self, other):
        if isinstance(other, TestB):
            return TestB(self.val + other.val)
        raise TypeError(f"unsupported operand type(s) for +: 'TestB' and '{type(other).__name__}'")


A = TestB(4)
B = TestB(5)
A += B
print(A.val)

Unary operator

Method list

Symbolmethodremarks
-__neg__(self)Positive, return any type
+__pos__(self)Take negative, return any type
abs(__x)__abs__(self)Absolute value, any return type
~__invert__(self)Negate by bit, return any type
complex(__x)__complex__(self)Convert to complex number and return the complex type, such as 1+2j
int()__int__(self)Convert to integer and return integer type, such as 3
float()__float__(self)Convert to floating-point number and return the floating-point number type, such as 4.5
Automatic call__index__(self)This function is automatically called where an integer is required to convert the object to an integer type, such as when it is used as an index of a list slice.
round(__x, ndigits=0)__round__(self[, ndigits])Round to the nearest and return any type. The implemented int type result is an integer multiple of 10^(-ndigits) (ndigits defaults to 0, and the result defaults to an integer multiple of 1, that is, rounding), round(0.5) is 0, and round(0.50001) is 1.
math.trunc(__x)__trunc__(self)Truncate and round, and the return type is arbitrary. The implemented int type math.trunc(-2.3) is -2.
math.floor(__x)__floor__(self)Round down and return any type. The implemented int type math.floor(-2.3) is -3.
math.ceil(__x)__ceil__(self)Round up and return any type. The implemented int type math.ceil(-2.7) is -2.

Example

import math


class Test:
    def __neg__(self):
        return 'a'

    def __pos__(self):
        return 'b'

    def __abs__(self):
        return 'c'

    def __invert__(self):
        return 'd'

    def __complex__(self):
        return 1 + 2j

    def __int__(self):
        return 3

    def __float__(self):
        return 4.5

    def __index__(self):
        return 1

    def __round__(self, n=None):
        return 'e'

    def __trunc__(self):
        return 'f'

    def __floor__(self):
        return 'g'

    def __ceil__(self):
        return 'h'



print(-Test())
print(+Test())
print(abs(Test()))
print(~Test())
# a
# b
# c
# d

print(complex(Test()))
print(int(Test()))
print(float(Test()))
# (1+2j)
# 3
# 4.5

testlist = ['aa', 'bb', 'cc']
print(testlist[Test()])
print(testlist[Test():Test()])
# bb
# []

print(round(Test()))
print(math.trunc(Test()))
print(math.floor(Test()))
print(math.ceil(Test()))
# e
# f
# g
# h

iterator

View the csdn in detail

__ next__ Return the next value of the iterator object (if no StopIteration exception is thrown). If this method is implemented, the object can continuously return the next value as a parameter of next(__x)
__ iter__ Return an iterator object (the class to which the object belongs has implemented the \u next \u method). If this method is implemented, the object can iterate in the for loop and constantly return the next value

class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a
    def __iter__(self):
        return self
        
>>>fibs = Fibs()
>>>next(fibs)
1
>>>next(fibs)
2
>>>for f in fibs:
...    if f > 1000:
...        print(f)
...        break
        
1597

cle/details/111863037)

__ next__ Return the next value of the iterator object (if no StopIteration exception is thrown). If this method is implemented, the object can continuously return the next value as a parameter of next(__x)
__ iter__ Return an iterator object (the class to which the object belongs has implemented the \u next \u method). If this method is implemented, the object can iterate in the for loop and constantly return the next value

class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a
    def __iter__(self):
        return self
        
>>>fibs = Fibs()
>>>next(fibs)
1
>>>next(fibs)
2
>>>for f in fibs:
...    if f > 1000:
...        print(f)
...        break
        
1597

Tags: Python

Posted by TubeRev on Wed, 10 Aug 2022 21:40:11 +0300