Python __add__ magic method with integers - python

When i am tring this:
>>> "a".__add__("b")
'ab'
It is working. But this
>>> 1.__add__(2)
SyntaxError: invalid syntax
is not working.
And this is working:
>>> 1 .__add__(2) # notice that space after 1
3
What is going here? Is it about variable naming rules and is python thinks I am trying to create variable when I am not using space?

Python parser is intentionally kept dumb and simple. When it sees 1., it thinks you are midway through a floating point number, and 1._ is not a valid number (or more correctly, 1. is a valid float, and you can't follow a value by _: "a" __add__("b") is also an error). Thus, anything that makes it clear that . is not a part of the number helps: having a space before the dot, as you discovered (since space is not found in numbers, Python abandons the idea of a float and goes with integral syntax). Parentheses would also help: (1).__add__(2). Adding another dot does as well: 1..__add__(2) (here, 1. is a valid number, then .__add__ does the usual thing).

The python lexical parser tries to interpret an integer followed by a dot as a floating point number. To avoid this ambiguity, you have to add an extra space.
For comparison, the same code works without problem on a double:
>>> 4.23.__add__(2)
6.23
It also works if you put the int in parentheses:
>>> (5).__add__(4)
9

When you use 1. the interpreter think you started writing float number (you can see in the IDE (atleast Pycharm) the dot is blue, not white). The space tell it to treat 1. as a complete number, 1.0. 1..__add__(2) will also do the trick.

Related

Error on performing actions with a complex number represented by capital I

I am relatively new to Python and have encountered a strange issue. I need to calculate a thickness of a tube 't' out of the eqn. which is given below. As expected, "solution" gives 4 possible values of t, of which only one is feasible, being a positive and real value. Two of my four solutions are complex and one is negative. Out of my solution-array of 4 values, I want to take that specific feasible solution and one operation is taking the real part of all values in that array. In taking the real part of one complex solution (sol2.real) out of the solution-array, it gives me always an error, while taking x.real with eg. x=1+2j gives the expected 1. I have noticed that my complex solutions don't have the normal symbol j, but the capital symbol I. The numpy and sympy packages are imported.
import numpy
import simpy
sigma_max=880000000
r_i=0.06
t = Symbol('t')
eqn=sigma_max-((9000*5.5*9.81)/(3.1415*((r_i+t)**2-r_i**2)))-(r_i+t)* \
(((25000*0.5*9.81)**2+(134*9.81)**2)**0.5)/((3.1415/4)*((r_i+t)**4-r_i**4))
solution=solve(eqn, t)
print(solution)
sol2=solution[2]
x=1+2j
print(x.real)
print(sol2.real)
The error I encounter with the "print(sol2.real)" command is:
'Add' object has no attribute 'real'
Could someone explain me what my fault is or where it goes wrong?
Many thanks in advance
Assuming the rest of your actual code is doing something reasonable, your problem is that you're mixing up sympy expressions with normal Python numbers.
Your sol2 is an expression. Sure, it looks like a number, but that's true for any expression that has nothing but constant values in it; it's still not a Python complex, any more than sympy.pi is a Python float.
What you probably want here is the re function:
>>> print(sympy.re(sol2))
-0.0720141606282290
(Of course this still isn't a Python float, it's still a sympy expression. But it prints out what I think you want to print out.)
Or, if you're done doing symbolic computation and want to convert to raw Python values, you can do that this:
>>> print(float(sympy.re(sol2)))
-0.07201416062822902
>>> print(complex(sol2).re)
-0.07201416062822902
Either of these is getting a Python float—along with any rounding errors that come from converting an exact symbolic value, or higher-precision decimal value, to float, of course.
The problem is that the items in solution are classes of sympy (e.g. type(solution[2]) is <class 'sympy.core.add.Add'>, which doesn't have an attribute real.
You can fix this by changing print(sol2.real) to print(complex(sol2).real)

what is this syntax called? (int)(value) casts the same as int(value)

I saw this in an exercise problem and had never seen this syntax used in Python before. Haven't had any luck googling it
Parentheses can be used arbitrarily for precedence purposes.
(int) evaluates to int, which then gets called with value as the argument, exactly the same as int(value). It's abusing the extra parens to look like a C-style cast, but it's distinctly unPythonic, style-wise.
A scenario in which this might make sense might include parsing a str as int or float based on whether a . occurs in the str, e.g.:
(float if '.' in value else int)(value)
Note: Having written it, still ugly, but it illustrates a use for the parens.
In a nutshell - this syntax is called redundancy.
Lets think about what brackets do first. Here is an example using arithmetic.
(3) + (2)
I'm sure you're aware that brackets are a higher precedence than + - so this expression evaluates to the following;
3 + 2
If there were something more in the brackets then that would happen first - but as there isn't there is very little point having them.
Now for your example.
(int) ("5")
The brackets on the left are redundant - and the ones on the right indicate a function call. But first, it would evaluate the brackets that are only acting as brackets.
int("5")
The two syntax's you point out are identical in behaviour - but there is no reason to use the one with extra brackets as they are redundant.
It is possible that you are currently getting C syntax confused where you can cast types using (type)object. Please note that there is no such syntax in python - the code you wrote works by coincidence due to the above.
The (int)(value) is the same as int(value). The syntax has no (other) special name. The parentheses are superfluous in this case as (int) is the same as int:
>>> (int)
<type 'int'>
>>> (int)('2')
2
You should stick with int(value) and avoid ambiguity.
Seems like something mistakenly copied from another language such as C. It's not actually invalid syntax in Python but the parentheses around int literally do nothing and are ignored.
Both does the same. In OOP, we call it type casting and concept is similar. Both the syntax does the same thing as below:-
a = (int)(34.65)
print(a)
> 34
a = int(34.65)
print(a)
> 34

Odd behavior of Python operator.xor

I am working on an encryption puzzle and am needing to take the exclusive or of two binary numbers (I'm using the operator package in Python). If I run operator.xor(1001111, 1100001) for instance I get the very weird output 2068086. Why doesn't it return 0101110 or at least 101110?
Because Python doesn't see that as binary numbers. Instead use:
operator.xor(0b1001111, 0b1100001)
The calculated answer is using the decimal values you provided, not their binary appearance. What you are really asking is...
1001111 ^ 1100001
When you mean is 79 ^ 97. Instead try using the binary literals as so...
0b1001111 ^ 0b1100001
See How do you express binary literals in Python? for more information.
Because 1001111 and 1100001 are not binary numbers. 1001111 is One million, one thousand, one hundred and eleven, while 1100001 is One million, one hundred thousands and one. Python doesn't recognize these as binary numbers. Binary numbers have to be prefixed with 0b to be recognized as binary numbers in Python/Python 3. So the correct way is this:
operator.xor(0b1001111, 0b1100001)
But hey! We get 46 as output. We should fix that. Thankfully, there IS a built-in in Python/Python 3. It's the function bin(n). That function prints a number a binary, prefixed with 0b. So our final code would be:
bin(operator.xor(0b1001111, 0b1100001))
If we want to hide the 0b (mostly in cases where that number is printed to the screen), we should use [2:] like this:
bin(operator.xor(0b1001111, 0b1100001))[2:]
A shorter way (warning looks like a tutorial for something you *should* already know)
Well, operator.xor() is too big for an operator :)
If that is the case (99.9%), instead you should use a^b. I think you already know this but why to import a whole module just for the xor operator? If you like to type the word xor instead, import the operator module like this: from operator import a, b.... Then use like this: bin(xor(a,b)). I hope you already know that stuff but I want to make sure you enjoy coding even more :)

How can I get Python to use upper case letters when printing hexadecimal values?

In Python v2.6 I can get hexadecimal for my integers in one of two ways:
print(("0x%x")%value)
print(hex(value))
However, in both cases, the hexadecimal digits are lower case. How can I get these in upper case?
Capital X (Python 2 and 3 using sprintf-style formatting):
print("0x%X" % value)
Or in python 3+ (using .format string syntax):
print("0x{:X}".format(value))
Or in python 3.6+ (using formatted string literals):
print(f"0x{value:X}")
Just use upper().
intNum = 1234
hexNum = hex(intNum).upper()
print('Upper hexadecimal number = ', hexNum)
Output:
Upper hexadecimal number = 0X4D2
print(hex(value).upper().replace('X', 'x'))
Handles negative numbers correctly.
By using uppercase %X:
>>> print("%X" % 255)
FF
Updating for Python 3.6 era: Just use 'X' in the format part, inside f-strings:
print(f"{255:X}")
(f-strings accept any valid Python expression before the : - including direct numeric expressions and variable names).
The more Python 3 idiom using f-strings would be:
value = 1234
print(f'0x{value:X}')
'0x4D2'
Notes (and why this is not a duplicate):
shows how to avoid capitalizing the '0x' prefix, which was an issue in other answers
shows how to get variable interpolation f'{value}'; nobody actually ever puts (hardcoded) hex literals in real code. There are plenty of pitfalls in doing variable interpolation: it's not f'{x:value}' nor f'{0x:value}' nor f'{value:0x}' nor even f'{value:%x}' as I also tried. So many ways to trip up. It still took me 15 minutes of trial-and-error after rereading four tutorials and whatsnew docs to get the syntax. This answer shows how to get f-string variable interpolation right; others don't.

how to avoid python numeric literals beginning with "0" being treated as octal?

I am trying to write a small Python 2.x API to support fetching a
job by jobNumber, where jobNumber is provided as an integer.
Sometimes the users provide ajobNumber as an integer literal
beginning with 0, e.g. 037537. (This is because they have been
coddled by R, a language that sanely considers 037537==37537.)
Python, however, considers integer literals starting with "0" to
be OCTAL, thus 037537!=37537, instead 037537==16223. This
strikes me as a blatant affront to the principle of least
surprise, and thankfully it looks like this was fixed in Python
3---see PEP 3127.
But I'm stuck with Python 2.7 at the moment. So my users do this:
>>> fetchJob(037537)
and silently get the wrong job (16223), or this:
>>> fetchJob(038537)
File "<stdin>", line 1
fetchJob(038537)
^
SyntaxError: invalid token
where Python is rejecting the octal-incompatible digit.
There doesn't seem to be anything provided via __future__ to
allow me to get the Py3K behavior---it would have to be built-in
to Python in some manner, since it requires a change to the lexer
at least.
Is anyone aware of how I could protect my users from getting the
wrong job in cases like this? At the moment the best I can think
of is to change that API so it take a string instead of an int.
At the moment the best I can think of is to change that API so it take a string instead of an int.
Yes, and I think this is a reasonable option given the situation.
Another option would be to make sure that all your job numbers contain at least one digit greater than 7 so that adding the leading zero will give an error immediately instead of an incorrect result, but that seems like a bigger hack than using strings.
A final option could be to educate your users. It will only take five minutes or so to explain not to add the leading zero and what can happen if you do. Even if they forget or accidentally add the zero due to old habits, they are more likely to spot the problem if they have heard of it before.
Perhaps you could take the input as a string, strip leading zeros, then convert back to an int?
test = "001234505"
test = int(test.lstrip("0")) # 1234505

Categories

Resources