Python 3 Operator Overloading detailed

Source: Internet
Author: User

Basic knowledge

In fact, "operator overloading" simply means intercepting the built-in action in the class method ... When an instance of a class appears in a built-in operation, Python automatically calls your method, and the return value of your method becomes the result of the corresponding operation. The following is a review of the key concepts of overloading:

    • Operator overloading allows a class to intercept regular Python operations.

    • Class can overload all Python expression operators

    • Classes can overload the built-in operations such as printing, function calls, attribute point number operations, and so on

    • Overloading makes a class instance behave like a built-in type.

    • Overloading is implemented by a class method of a special name.


In other words, when a method of a particular name is provided in a class, Python automatically calls them when instances of the class appear in their related expressions. As we have learned, the operator overloading method is not required and is usually not the default, and if you do not write or inherit an operator overloaded method, it simply means that your class does not support the corresponding operation. However, when used, these methods allow the class to emulate the interfaces of the built-in objects and therefore behave more consistently.


Constructors and expressions: __init__ and __sub__

Let's take a look at a simple overloaded example. For example, the number class of the following file number.py class provides a way to intercept the instance's constructor (__init__), and there is a method to catch the subtraction expression (__sub__). This special method is a hook that can be tied to a built-in operation.

class number:    def __init__ (Self, start):                 # on number (start)          self.data = start    def __sub__ (Self,  other):                   # on instance - other        return  number (Self.data - other)         # result is  a new instance        >>> from  Number import number>>> x = number (5)                               #  number.__init__ (x, 5) >>> Y = X - 2                                  # number.__sub__ (x, 2) >>> Y.data                                     # Y  Is new number instance3

As discussed before, the __init__ constructor seen in this code is the most common operator overloading method in Python, which exists in most classes. In this section, we will give an example of some of the other tools available in this area, and take a look at the routines commonly used by these tools.


Common operator overloading methods

In a class, there is almost always an overloaded method of a special name for what you can do with built-in objects (for example, integers and lists). The following table lists some of the commonly used overloaded methods. In fact, many overloaded methods have several versions (for example, addition has __add__, __radd__, and __iadd__).

Method Overload Call
__init__ constructor function Object creation: X = Class (args)
__del__ Destructors X Object retract
__add__ operator + If there is no __iadd__, x + y, x + = y
__or__ operator | (bit or) If there is no __ior__, X | Y, X |= y
__REPR__, __str__ printing, converting Print (x), repr (x), str (x)
__call__ Function call X (*args, **kargs)
__getattr__ Point number arithmetic x.undefined
__setattr__ Property Assignment Statements X.any = value
__delattr__ Property Delete

Del X.any

__getattribute__ Property gets

X.any

__getitem__ Index operations

X[KEY],X[I:J], for loops and other iterators when not __iter__

__setitem__ Index assignment statement X[key] = value, x[i:j] = sequence
__delitem__
Index and Shard deletion Del X[key], del X[i:j]
__len__ Length Len (X), if there is no __bool__, the truth test
__bool__ Boolean test BOOL (X), true test (called __nonzero__ in Python 2.6)

__lt__, __gt__

__lt__, __ge__

__eq__, __ne__

Specific comparisons X<y, X>y, X<=y, x>=y, x = = y, x! = y (or only __cmp__ in Python 2.6)
__radd__ Right addition Other + X
__iadd__ field (Enhanced) addition X + = Y (or else __add__)
__iter, __next__ Iterative environment i = iter (X), Next (i); For loops, with if no __contains__, all comprehensions, map (F, X), others (__next__ becomes next in Python2.6)
__contains__ Member Relationship Testing Item in X (any iteration)
__index__ Integer value Hex (x), Bin (x), Oct (x), o[x], O[x:] (replaces __oct__, __hex__ in Python 2)
__ENTER__, __exit__ Environment Manager With obj as Var:

__get__, __set__

__delete

Descriptor Properties x.attr, x.attr = value, Del x.attr
__new__ Create Create an object before __init__

All overloaded methods have two underscores before and after their names to differentiate the variable names defined in the same class. The mapping of special method names and expressions or operations is pre-defined by the Python language (described in the standard language manual). For example, __add__, as defined by the Python language, always corresponds to the expression +, regardless of what the code in the __add__ method actually does.


If you do not define an operator overloading method, it may inherit from the superclass, just like any other method. Operator overloading methods are also optional ... If you do not write or inherit a method, your class does not support these operations directly, and attempting to use them throws an exception. Some built-in operations, such as printing, have a default overloaded method (inherited from the object class implied in Python 3.x), but most built-in functions fail on the class instance if the appropriate operator overloading method is not given.


Most overloaded methods are used only in high-level programs that require objects to behave as if they were built-in types. However, __init__ constructors often appear in most classes. We have seen __init__ initial definition constructors, as well as some of the other methods in the table above. Let's use examples to illustrate the other methods in the table.



Indexes and Shards: __getitem__ and __setitem__

if (or inherited) is defined in the class, __getitem__ is automatically called for the instance's index operation. When instance x appears in an index operation such as X[i], Python invokes the __getitem__ method inherited by the instance (if any), passes X as the first argument, and the index value of the square bracket class is passed to the second argument. For example, the following class returns the square of the index value.

>>> class Indexer:def __getitem__ (Self, Index): Return index * * 2>>> X = Indexer () >>> x[2] # X[i] Calls x.__getitem__ (i) 4>>> for me in range (5):p rint (X[i], end= ") # R Uns __getitem__ (X, i) each TIME0 1 4 9 16


Intercept shards

Interestingly, in addition to indexes, __GETITEM__ is also called for Shard expressions. Formally, built-in types handle shards in the same way. For example, the following is a shard that works on a built-in list, uses the upper and lower bounds, and a stride (you can review the knowledge about shards):

>>> L = [5, 6, 7, 8, 9]>>> L[2:4] # Slice with Slice syntax[7, 8]>>> l[1 :][6, 7, 8, 9]>>> l[:-1][5, 6, 7, 8]>>> l[::2][5, 7, 9]

In fact, the Shard boundary is bound to a Shard object and passed to the list implementation of the index. In fact, we can always manually pass a Shard object ... The Shard syntax is primarily a syntactic sugar indexed with a Shard object:

>>> L[slice (2, 4)] # Slice with slice objects[7, 8]>>> l[slice (1, None)][6, 7, 8, 9]>& Gt;> L[slice (None,-1)][5, 6, 7, 8]>>> L[slice (None, none, 2)][5, 7, 9]

For classes with a __getitem__, this is important ... The method will call both the base index (with one index) and the Shard (with one Shard object). The class before us does not process shards because its mathematical assumptions pass an integer index, but the following classes will handle the shards. When called on an index, the parameter is an integer as before:

>>> Class Indexer:data = [5, 6, 7, 8, 9]def __getitem__ (self, Index):p rint ("GetItem:", index) return Self.data[ind ex]>>> X = Indexer () >>> x[0]getitem:05>>> x[1]getitem:16>>> X[-1]getitem: 19

However, when called on a shard, the method receives a Shard object that is passed directly to the nested list index in a new index expression:

>>> X[2:4]getitem:slice (2, 4, none) [7, 8]>>> X[1:]getitem:slice (1, none, none) [6, 7, 8, 9]>>> X[:-1]getitem:slice (None,-1, None) [5, 6, 7, 8]>>> X[::2]getitem:slice (None, none, 2) [5, 7, 9]

If used, the __setitem__ index assignment method intercepts indexes and shard assignments in a similar way ... It receives a Shard object for the latter, which may be passed to another index assignment in the same way:

def __setitem__ (self, Index, value): ... self.data[index] = value

In fact, __getitem__ may be called automatically in environments that are even more than indexes and shards, as described in the following subsections.

shards and indexes in Python 2.6

Prior to Python 3.0, classes could also define __getslice__ and __setslice__ methods to specifically intercept shard fetching and assignment, which would pass a series of Shard expressions and take precedence over __getitem__ and __setitem__ for sharding.


These shard-specific methods have been removed from Python 3.0, so you should use __getitem__ and __setitem__ instead to take into account that both the index and the Shard object may be parameters.


Index iterations: __getitem__

Beginners may not immediately be able to grasp the technique, but these techniques are very useful, the purpose of the For statement is from 0 to a larger index value, repeat the index operation of the sequence, know the detection of an exception beyond the boundary. Therefore, __getitem__ can also be a way of overloading iterations in Python. If this method is defined, the for loop invokes the class's __getitem__ each time it loops, and continues to match with a higher offset value. This is a "buy one, get one" scenario: any built-in or user-defined objects that respond to an index operation will also respond to iterations.

>>> class Stepper:def __getitem__ (Self, i): return self.data[i]>>> X = Stepper () >>> x.data = "S Pam ">>> >>> x[1] ' p ' >>> for item in X:print (item, end= ') S p a M

In fact, this is actually the case of "buy one and get one". Any class that supports a for loop will automatically support all of Python's iterative environments, many of which we've already seen in the front. For example, membership tests in, list parsing, built-in function map, list and tuple assignment operations, and type construction methods also automatically invoke __getitem__ (if defined).

>>> ' P ' in xtrue>>> [C for C in x][' s ', ' P ', ' a ', ' m ']>>> list (map (Str.upper, X)) [' s ', ' P ', ' a ' , ' M ']>>> (A, B, C, D) = X>>> A, b, C (' s ', ' P ', ' a ') >>> list (x), tuple (x), '. Join (x) ([' s ', ' P ' , ' A ', ' m '], (' S ', ' P ', ' a ', ' m '), ' Spam ') >>> X<__main__.stepper object at 0x000001e19957df28>

In practical applications, this technique can be used to create pairs that provide sequence interfaces, and add logic to the built-in sequence type operations.




Iterator objects: __iter__ and __next__

Although the __getitem__ technique above is effective, it is really just a fallback method of iteration. today, all iteration environments in Python try the __iter__ method first, and then try to __getitem__. that is, they prefer to use iterative protocols and then repeat the indexing of objects . An index operation is attempted only if the object does not support an iterative protocol . In general, you should also prioritize the use of __iter__, which is better able to support a general iterative environment than __GETITER__.


Technically, the iterative environment is implemented by calling the built-in function iter to try to find the __iter__ method, and this method should return an iterator object. If provided, Python repeats the next method of invoking the iterator object, knowing that the stopiteration exception occurred. If this type of __iter__ method is not found, Python will instead use the __getitem__ mechanism, repeating the index as before with an offset, knowing that a Indexerror exception occurs (a next built-in function can be easily used for manual iterations: Next (I) Is the same as i.__next__ ().


User-defined Iterators

In the __iter__ mechanism, a class implements a user-defined iterator by implementing an iterator protocol. For example, the following iters.py defines a user-defined iterator to generate a squared value.

>>> class Squares:def __init__ (self, start, stop): Self.value = Start-1self.stop = Stopdef __iter__ (self): return Selfdef __next__ (self): if Self.value = = self.stop:raise Stopiterationself.value + 1return self.value * 2>>> fo R I in Squares (1, 5):p rint (i, end= ") 1 4 9 16 25

Here, the iterator object is the instance self, and the next method should be part of the class. In a more scum-like scenario, an iterator object can be defined as an object of an individual class or its own state information, supporting multiple iterations of the same data (see this example below). The signal from the Python raise statement indicates the end of the iteration. Manual iterations are also valid for built-in types:

>>> X = Squares (1, 5) >>> I = iter (X) >>> Next (i) 1>>> next (i) 4......>>> next ( i) 25>>> next (i) Traceback (most recent): File "<pyshell#91>", line 1, <module> next (i ) File "<pyshell#77>", line 9, in __next__ raise Stopiterationstopiteration

The equivalent code written by __getitem__ may not be natural, and the for will iterate over all 0 and higher value offset values. The offset value passed in and the range of the resulting value is only indirectly related (0: n needs to be set to start. Stop). Because the __iter__ object retains state information explicitly during invocation, it is more versatile than __getitem__.


Other than that
















This article from "Professor elder brother" blog, reprint please contact the author!

Python 3 Operator Overloading detailed

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.