In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article introduces the relevant knowledge of "how to understand the binary arithmetic operation of Python". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
View the C code
By convention, we start by looking at the bytecode compiled by the CPython interpreter.
> def sub (): a-b... > > import dis > dis.dis (sub) 10 LOAD_GLOBAL 0 (a) 2 LOAD_GLOBAL 1 (b) 4 BINARY_SUBTRACT 6 POP_TOP 8 LOAD_CONST 0 (None) 10 RETURN_VALUE
It seems that we need to delve deeper into the BINARY_SUBTRACT opcode. Looking up the Python/ceval.c file, you can see that the C code that implements the opcode is as follows:
Case TARGET (BINARY_SUBTRACT): {PyObject * right = POP (); PyObject * left = TOP (); PyObject * diff = PyNumber_Subtract (left, right); Py_DECREF (right); Py_DECREF (left); SET_TOP (diff); if (diff = = NULL) goto error; DISPATCH ();}
Source: https://github.com/python/cpython/blob/6f8c8320e9eac9bc7a7f653b43506e75916ce8e8/Python/ceval.c#L1569-L1579
The key code here is PyNumber_Subtract (), which implements the actual semantics of subtraction. Continuing to look at some of the macros of this function, you can find the binary_op1 () function. It provides a general way to manage binary operations.
However, instead of using it as a reference for implementation, we will use Python's data model, which is well documented and clearly describes the semantics used in subtraction.
Learn from the data model
Reading through the documentation of the data model, you will find that two methods play a key role in implementing subtraction: _ _ sub__ and _ _ rsub__.
1. _ _ sub__ () method
When a-b is executed, it looks for _ _ sub__ () in the type of an and takes b as its parameter. This is much like _ _ getattribute__ () in my article on property access, where special / magic methods are parsed based on the type of object, not for performance purposes; in the following sample code, I use _ mro_getattr () to represent this process.
Therefore, if _ _ sub__ () is defined, then type (a). _ _ sub__ (a _ sub__) will be used for subtraction. (translation note: magic method belongs to the type of object, not to object)
This means that in essence, subtraction is just a method call! You can also think of it as the operator.sub () function in the standard library.
We will copy this function to implement our model, using the names lhs and rhs to represent the left and right side of Amurb, respectively, to make the sample code easier to understand.
# subtract def sub (lhs: Any, rhs: Any, /)-> Any: "Implement the binary operation `a-b`."by calling _ _ sub__ () Lhs_type = type (lhs) try: subtract = _ mro_getattr (lhs_type, "_ sub__") except AttributeError: msg = f "unsupported operand type (s) for -: {type (rhs)! r}" raise TypeError (msg) else: return subtract (lhs, rhs)
2. Let the right side use _ _ rsub__ ()
But what if a doesn't implement _ _ sub__ ()? If an and b are different types, then we will try to call _ _ rsub__ () of b (the "r" in _ _ rsub__ means "right", which represents the right side of the operator).
When both sides of the operation are of different types, this ensures that they both have a chance to try to make the expression valid. When they are the same, we assume that _ _ sub__ () can handle it. However, even if the implementations on both sides are the same, you still have to call _ _ rsub__ () in case one of the objects is another (subclass).
3. Do not care about the type
Now, both sides of the expression can participate in the operation! But what if, for some reason, the type of an object doesn't support subtraction (for example, it doesn't support 4-"stuff")? In this case, all _ _ sub__ or _ _ rsub__ can do is return NotImplemented.
This is the signal returned to Python, which should proceed to the next operation in an attempt to make the code work properly. For our code, this means that we need to check the return value of the method before we can assume that it works.
# implementation of subtraction Both the left and right sides of the expression can participate in the operation _ MISSING = object () def sub (lhs: Any, rhs: Any, /)-> Any: # lhs.__sub__ lhs_type = type (lhs) try: lhs_method = debuiltins._mro_getattr (lhs_type) "_ sub__") except AttributeError: lhs_method = _ MISSING # lhs.__rsub__ (for knowing if rhs.__rub__ should be called first) try: lhs_rmethod = debuiltins._mro_getattr (lhs_type "_ _ rsub__") except AttributeError: lhs_rmethod = _ MISSING # rhs.__rsub__ rhs_type = type (rhs) try: rhs_method = debuiltins._mro_getattr (rhs_type, "_ _ rsub__") except AttributeError: rhs_method = _ MISSING call_lhs = lhs, lhs_method Rhs call_rhs = rhs, rhs_method, lhs if lhs_type is not rhs_type: calls = call_lhs, call_rhs else: calls = (call_lhs,) for first_obj, meth, second_obj in calls: if meth is _ MISSING: continue value = meth (first_obj Second_obj) if value is not NotImplemented: return value else: raise TypeError (f "unsupported operand type (s) for -: {lhsroomtypeaccounr} and {rhsroomtypefolr}")
4. the subclass takes precedence over the parent class
If you look at the documentation for _ _ rsub__ (), you will notice a comment. It says that if the right side of a subtraction expression is the subclass on the left (real subclass, not the same class), and the _ _ rsub__ () method of the two objects is different, _ _ rsub__ () is called before calling _ _ sub__ (). In other words, if b is a subclass of a, the order of calls is reversed.
This seems to be a strange special case, but there is a reason behind it. When you create a subclass, this means that you inject new logic into the operations provided by the parent class. This logic does not have to be added to the parent class, otherwise when the parent class operates on the subclass, it is easy to override the operation that the subclass wants to implement.
Specifically, suppose you have a class called Spam, and when you execute Spam ()-Spam (), you get an instance of LessSpam. Then you create a subclass of Spam called Bacon, so that when you use Spam to subtract Bacon, you get VeggieSpam.
Without the above rule, Spam ()-Bacon () will get LessSpam because Spam doesn't know that subtracting Bacon should lead to VeggieSpam.
However, with the above rules, you will get the expected result VeggieSpam, because Bacon.__rsub__ () will be called in the expression first (if you evaluate Bacon ()-Spam (), you will also get the correct result, because Bacon.__sub__ () will be called first, so the rule says that the different methods of the two classes need to be different, not just a subclass determined by issubclass ().)
The complete implementation of subtraction in # Python _ MISSING = object () def sub (lhs: Any, rhs: Any, /)-> Any: # lhs.__sub__ lhs_type = type (lhs) try: lhs_method = debuiltins._mro_getattr (lhs_type "_ sub__") except AttributeError: lhs_method = _ MISSING # lhs.__rsub__ (for knowing if rhs.__rub__ should be called first) try: lhs_rmethod = debuiltins._mro_getattr (lhs_type "_ _ rsub__") except AttributeError: lhs_rmethod = _ MISSING # rhs.__rsub__ rhs_type = type (rhs) try: rhs_method = debuiltins._mro_getattr (rhs_type, "_ _ rsub__") except AttributeError: rhs_method = _ MISSING call_lhs = lhs, lhs_method Rhs call_rhs = rhs, rhs_method, lhs if (rhs_type is not _ MISSING # Do we care? And rhs_type is not lhs_type # Could RHS be a subclass? And issubclass (rhs_type, lhs_type) # RHS is a subclass! And lhs_rmethod is not rhs_method # Is _ _ ritual _ actually different?: calls = call_rhs, call_lhs elif lhs_type is not rhs_type: calls = call_lhs, call_rhs else: calls = (call_lhs,) for first_obj, meth Second_obj in calls: if meth is _ MISSING: continue value = meth (first_obj, second_obj) if value is not NotImplemented: return value else: raise TypeError (f "unsupported operand type (s) for -: {lhsroomtypeaccounr} and {rhsroomtypepragr}")
Extended to other binary operations
If the subtraction operation is solved, what about other binary operations? Well, it turns out they operate in the same way, but happen to use different special / magic method names.
So, if we can promote this approach, then we can implement the semantics of 13 operations: +, -, *, @, /, /,%, *, &, ^, and |.
Because of the flexibility of closures and Python in object introspection, we can extract the creation of operator functions.
# A function to create a closure, which implements the logic of binary operation _ MISSING = object () def _ create_binary_op (name: str, operator: str)-> Any: "Create a binary operation function. The `name` parameter specifies the name of the special method used for the binary operation (e.g. `sub`for `_ _ sub__`). The `operator`name is the token representing the binary operation (e.g. `- `for subtraction)." Lhs_method_name = f "_ {name} _" def binary_op (lhs: Any, rhs: Any, /)-> Any: "A closure implementing a binary operation in Python." Rhs_method_name = f "_ r {name} _" # lhs.__*__ lhs_type = type (lhs) try: lhs_method = debuiltins._mro_getattr (lhs_type Lhs_method_name) except AttributeError: lhs_method = _ MISSING # lhs.__r*__ (for knowing if rhs.__r*__ should be called first) try: lhs_rmethod = debuiltins._mro_getattr (lhs_type Rhs_method_name) except AttributeError: lhs_rmethod = _ MISSING # rhs.__r*__ rhs_type = type (rhs) try: rhs_method = debuiltins._mro_getattr (rhs_type, rhs_method_name) except AttributeError: rhs_method = _ MISSING call_lhs = lhs, lhs_method, rhs call_rhs = rhs, rhs_method Lhs if (rhs_type is not _ MISSING # Do we care? And rhs_type is not lhs_type # Could RHS be a subclass? And issubclass (rhs_type, lhs_type) # RHS is a subclass! And lhs_rmethod is not rhs_method # Is _ _ ritual _ actually different?: calls = call_rhs, call_lhs elif lhs_type is not rhs_type: calls = call_lhs, call_rhs else: calls = (call_lhs,) for first_obj, meth Second_obj in calls: if meth is _ MISSING: continue value = meth (first_obj Second_obj) if value is not NotImplemented: return value else: exc = TypeError (f "unsupported operand type (s) for {operator}: {lhsroomtypeaccounr} and {rhsroomtypefolr}") exc._binary_op = operator raise exc
With this code, you can define the subtraction operation as _ create_binary_op ("sub", "-"), and then repeat other operations as needed.
This is the end of the content of "how to understand the binary arithmetic of Python". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.