Python: Shorten ugly code? - python

I have a ridiculous code segment in one of my programs right now:
str(len(str(len(var_text)**255)))
Is there an easy way to shorten that? 'Cause, frankly, that's ridiculous.
A option to convert a number >500 digits to scientific notation would also be helpful
(that's what I'm trying to do)
Full code:
print("Useless code rating:" , str(len(var_text)**255)[1] + "e" + str(len(str(len(var_text)**255))))

TL;DR: y = 2.408 * len(var_text)
Lets assume that your passkey is a string of characters with 256 characters available (0-255). Then just as a 16bit number holds 65536 numbers (2**16) the permutations of a string of equal length would be
n_perms = 256**len(passkey)
If you want the number of (decimal) digits in n_perms, consider the logarithm:
>>> from math import log10
>>> log10(1000)
3.0
>>> log10(9999)
3.9999565683801923
>>>
So we have length = floor(log10(n_perms)) + 1. In python, int rounds down anyway, so I'd say you want
n_perms = 256**len(var_text)
length = int(log10(n_perms)) + 1
I'd argue that 'shortening' ugly code isn't always the best way - you want it to be clear what you're doing.
Edit: On further consideration I realised that choosing base-10 to find the length of your permutations is really arbitrary anyway - so why not choose base-256!
length = log256(256**len(var_text)
length = len(var_text) # the log and exp cancel!
You are effectively just finding the length of your passkey in a different base...
Edit 2: Stand back, I'm going to attempt Mathematics!
if x = len(var_text), we want y such that
y = log10(256**x)
10**y = 256**x
10**y = (10**log10(256))**x
10**y = (10**(log10(256)x))
y = log10(256) * x
So, how's this for short:
length = log10(256) * len(var_text) # or about (2.408 * x)

This looks like it's producing a string version of the number of digits in the 255th power of the length of a string. Is that right? I'd be curious what that's used for.
You could compute the number differently, but it's not shorter and I'm not sure it's prettier:
str(int(math.ceil(math.log10(len(var_text))*255)))
or:
"%d" % math.ceil(math.log10(len(v))*255)

Are you trying to determine the number of possible strings having the same length as var_text? If so, you have your base and exponent reversed. You want to use 255**len(var_text) instead of len(var_text)**255.
But, I have to ask ... how long do these passkeys get to be, and what are you using them for?
And, why not just use the length of the passkey as an indicator of its length?

Firstly, if your main problem is manipulating huge floating point expressions, use the bigfloat package:
>>> import bigfloat
>>> bigfloat.BigFloat('1e1000')
BigFloat.exact('1.0000000000000001e+1000', precision=53)
As for the details in your question: len(str(num)) is approximately equal to log(num, 10) + 1. This is not significantly shorter, but it's certainly a better way of expressing it in code (for the benefit of anyone who doesn't know that off the top of their head). You can then simplify it with log laws:
len(str(x**y))
= log(x**y, 10) + 1
= y * log(x, 10) + 1
So maybe you'll find:
"%i" % (log(len(var_text),10)*255 + 1)
... is better? It's not significantly shorter, but it's a much clearer mathematical relationship between input and output.

Related

Can f-strings auto-pad to [the next] even number of digits on output?

Based on this answer (among others) it seems like f-strings is [one of] the preferred ways to convert to hexadecimal representation.
While one can specify an explicit target length, up to which to pad with leading zeroes, given a goal of an output with an even number of digits, and inputs with an arbitrary # of bits, I can imagine:
pre-processing to determine the number of bits of the input, to feed an input-specific value in to the fstring, or
post-processing a-la out = "0"+f"{val:x}" if len(f"{val:x}") % 2 else f"{val:02x}" (or even using .zfill())
The latter seems like it might be more efficient than the former - is there a built-in way to do this with fstrings, or a better alternative?
Examples of input + expected output:
[0x]1 -> [0x]01
[0x]22 -> [0x]22
[0x]333 -> [0x]0333
[0x]4444 -> [0x]4444
and so on.
Here's a postprocessing alternative that uses assignment expressions (Python 3.8+):
print((len(hx:=f"{val:x}") % 2) * '0' + hx)
If you still want a one-liner without assignment expressions you have to evaluate your f-string twice:
print((len(f"{val:x}") % 2) * '0' + f"{val:x}")
As a two-liner
hx = f"{val:x}"
print((len(hx) % 2) * '0' + hx)
And one more version:
print(f"{'0'[:len(hex(val))%2]}{val:x}")
I don't think there's anything built in to f-string formatting that will do this. You probably have to figure out what the "natural" width would be then round that up to the next even number.
Something like this:
def hf(n):
width = len(hex(n)) - 2 # account for leading 0x
width += width % 2 # round up
return f'{n:0{width}x}'
print(hf(1))
print(hf(15))
print(hf(16))
print(hf(255))
print(hf(256))
Output:
01
0f
10
ff
0100
You can use a variable in the pad-length part of the f-string. For example:
n = 4
val = 257
print(f"{val:0{n}x}") # 0101
Now, to figure out how many hex characters are in an integer, you just need to find how many bits are in the integer:
hex_count, rem = divmod(max(1, val.bit_length()), 4)
hex_count += (rem > 0)
(max(1, val.bit_length()) handles the case where val == 0, which has a bit length of 0)
So let's get the next even number after hex_count:
pad_length = hex_count + (hex_count % 2)
print(f"{val:0{pad_length}x}") # 0101
I'm not sure if this is any better than simply converting it to a hex string and then figuring out how much padding is needed, but I can't think of a readable way to do this all in an f-string. An unreadable way would be by combining all of the above into a single line, but IMO readable code is better than unreadable one-liners. I don't think there's a way to specify what you want as a simple f-string.
Note that negative numbers are formatted to an even number of digits, plus the - sign.

Fraction calculator in python workaround

So, I have to create a python script that given 2 fractions and an operand will print the result of the operation. This was intended to be solved by firstly asking for one fraction and saving it into a variable, then ask for another fraction and lastly ask for the operand. But out of curiosity I've tried to give this problem a different point of view.
My idea was to ask for the full operation and save the input string into a variable, then with the function exec() I could get the decimal result of the given operation, finally to deal with decimals my idea was to multiply by 10 to the power of the number of decimal digits and then dividing by 10 to that same power, this way I could have a fraction as a result. So I went on to code and managed to program this out, my only issue is that the number of decimal digits is limited so normally the result that my script returns is a very big fraction that is very close to what the real fraction is. So I was wondering if there is any workaround for this. Here is my code and an example for further explanation:
op = input('Enter operation: ')
try:
exec('a = ' + op)
except:
print('Invalid operation')
def euclides(a, b):
while a != 0 and b != 0:
if a < b: b = b%a
else: a = a%b
if a == 0: return b
elif b == 0: return a
print(f'{int(a*10**len(str(a).split(".")[1])/euclides(a*10**len(str(a).split(".")[1]),10**len(str(a).split(".")[1])))}/{int(10**len(str(a).split(".")[1])/euclides(a*10**len(str(a).split(".")[1]),10**len(str(a).split(".")[1])))}')
EXAMPLE:
op input => 4/3+5/7
Result of script => 5119047619047619/2500000000000000 = 2.04761904761
Result I'm looking for => 43/21 = 2.047619 period
Thank you for your help in advance
What are your constraints as to what standard or add-on modules you can use? Without taking into account constraints you haven't specified, there are much better ways to go about what you're doing. Your problem seems to be summed up by "the result that my script returns is a very big fraction" and your question seems to be "I was wondering if there is any workaround for this?". There are a number of "work arounds". But it's pretty hard to guess what the best solution is for you as you don't tell us what tools you can and can't use to accomplish your task.
As an example, here's an elegant solution if you can use regular expressions and the fractions module, and if you can assume that the input will always be in the very strict format of <int>/<int>+<int>/<int>:
import re
import fractions
op = input('Enter operation: ')
m = re.match(r"(\d+)/(\d+)\+(\d+)/(\d+)", op)
if not m:
raise('Invalid operation')
gps = list(map(int, m.groups()))
f = fractions.Fraction(gps[0], gps[1]) + fractions.Fraction(gps[2], gps[3])
print(f)
print (float(f))
print(round(float(f), 6))
Result:
43/21
2.0476190476190474
2.047619
This answers your current question. I don't, however, know if this violates the terms of your assignment.
Could just turn all natural numbers into Fractions and evaluate:
>>> op = '4/3+5/7'
>>> import re, fractions
>>> print(eval(re.sub(r'(\d+)', r'fractions.Fraction(\1)', op)))
43/21
Works for other cases as well (unlike the accepted answer's solution, which only does the sum of exactly two fractions that must be positive and must not have spaces), for example:
>>> op = '-1/2 + 3/4 - 5/6'
>>> print(eval(re.sub(r'(\d+)', r'fractions.Fraction(\1)', op)))
-7/12
Checking:
>>> -7/12, -1/2 + 3/4 - 5/6
(-0.5833333333333334, -0.5833333333333334)

Python: Add character to string an amount of times depending on an int

Ok, I may have totally butchered the phrasing of this question since I can't quite word it but here's what I'm trying to do:
I have a CPU percentage displayed as characters in a string which show up on a display. If the cpu were at 52% I'd want the string to be CPU:[#####] 64% CPU:[######] but 77% CPU:[#######/] etc.
The only method I can thing off is having 20 if statements each with their own version of the string which shows dependant on the int percentage's value. I feel this would be a horrible way of doing it and wanted to know if there is a better way I can do this?
Assuming you want the number of hashes to be the “tens digit” of the CPU percentage, this simple function will do it:
def cpu_usage_str(cpu_percentage):
return 'CPU:[%s]' % ('#' * cpu_percentage / 10)
print cpu_usage_str(52)
print cpu_usage_str(64)
print cpu_usage_str(72)
(This assumes Python 2, with floor division. You’d need to tweak it slightly for Python 3.) We use the * operator to repeat the '#' string the appropriate number of times.
output:
CPU:[#####]
CPU:[######]
CPU:[#######]
Alternatively, if you’d like to round to the nearest ten (so 36% becomes CPU:[####], say), then this will do the trick:
def alt_cpu_usage_str(cpu_percentage):
return 'CPU:[%s]' % ('#' * int(round(cpu_percentage / 10.0)))
First we divide by 10.0 to get a float between 0.0 and 10.0. Then round takes us to the nearest whole number. This is represented by a float, so we convert it to an int and then repeat the string as before.
If we want a backslash to indicate whether we're under/over 5, then we need to consider cpu_percentage % 10 (the % is the modulo operator). Here's a function which does it:
def with_slashes_cpu_usage_str(cpu_percentage):
hashes_str = '#' * int(round(cpu_percentage / 10))
slash_str = '/' * (cpu_percentage % 10 >= 5)
return 'CPU:[{hash}{slash}]'.format(hash=hashes_str, slash=slash_str)
We construct the hash and slash strings separately. The bool cpu_percentage % 10 >= 5 is coerced to 0 or 1, which gives us the slash, or not, depending on whether or not we're in the upper half of that 10.
If you want [ ]5% not to print that trailing slash, then change to a strict inequality.
You can repeat a string using *, e.g. '#'*5 gives '#####'
print "CPU:[%s]"%('#'*(percentage/10))

Split integer into two concatenated hex strings- Python

I need to transmit a value that is larger than 65535 via two different hex strings so that when the strings are received, they can be concatenated to form the integer again. For example if the value was 70000 then the two strings would be 0x0001 and 0x1170.
I thought it would be as simple as converting the integer to hex then shifting it right by 4 to get the top string and removing all but the last 4 characters for the bottom.
I think I might be struggling with some syntax (fairly new to Python) and probably some of the logic too. Can anyone think of an easy way to do this?
Thanks
Use divmod builtin function:
>>> [hex(x) for x in divmod(70000, 65536)]
['0x1', '0x1170']
Your algorithm can be implemented easily, as in Lev Levitsky's answer:
hex(big)[2:-4], hex(big)[-4:]
However, it will fail for numbers under 65536.
You could fix that, but you're probably better off splitting the number, then converting the two halves into hex, instead of splitting the hex string.
ecatmur's answer is probably the simplest way to do this:
[hex(x) for x in divmod(70000, 65536)]
Or you could translate your "shift right/truncate" algorithm on the numbers like this:
hex(x >> 16), hex(x & 0xFFFF)
If you need these to be strings like '0x0006' rather than '0x6', instead of calling hex on the parts, you can do this:
['%#06x' % (x,) for x in divmod(x, 65536)]
Or, using the more modern string formatting style:
['0x{:04x}'.format(x) for x in divmod(x, 65536)]
But on the other side, you again probably want to undo this by converting to ints first and then shifting and masking the numbers, instead of concatenating the strings. The inverse of ecatmur's answer is:
int(bighalf) * 65536 + int(smallhalf)
The (equivalent) inverse of the shift/mask implementation is:
(int(bighalf) << 16) | int(smallhalf)
And in that case, you don't need the extra 0s on the left.
It's also worth pointing out that none of these algorithms will work if the number can be negative, or greater than 4294967295, but only because the problem is impossible in those cases.
You mean like this?
In [1]: big = 12345678
In [2]: first, second = hex(big)[2:][:-4], hex(big)[2:][-4:]
In [3]: first, second
Out[3]: ('bc', '614e')
In [4]: int(first+second, 16)
Out[4]: 12345678
Being wary of big/little endians, what you could do to keep it simple is:
val = 70000
to_send = '{:08X}'.format(val) # '00011170'
decoded = int('00011170', 16) # 70000
EDIT: to be very clear then...
hex1, hex2 = to_send[:4], to_send[4:] # send these two and on receipt
my_number = int(hex1 + hex2, 16)
for numbers greater than 65536 or for numbers whose with length >=5, you can use slicing:
>>> num=70000
>>> var1=hex(num)[:-4]
>>> var2='0x'+hex(num)[-4:]
>>> integ=int(var1+var2[2:],16)
>>> print(integ)
70000

Integer to Unique String

There's probably someone else who asked a similar question, but I didn't take much time to search for this, so just point me to it if someone's already answered this.
I'm trying to take an integer (or long) and turn it into a string, in a very specific way.
The goal is essentially to split the integer into 8-bit segments, then take each of those segments and get the corresponding ASCII character for that chunk, then glue the chunks together.
This is easy to implement, but I'm not sure I'm going about it in the most efficient way.
>>> def stringify(integer):
output = ""
part = integer & 255
while integer != 0:
output += chr(part)
integer = integer >> 8
return output
>>> stringify(10)
'\n'
>>> stringify(10 << 8 | 10)
'\n\n'
>>> stringify(32)
' '
Is there a more efficient way to do this?
Is this built into Python?
EDIT:
Also, as this will be run sequentially in a tight loop, is there some way to streamline it for such use?
>>> for n in xrange(1000): ## EXAMPLE!
print stringify(n)
...
struct can easily do this for integers up to 64 bits in size. Any larger will require you to carve the number up first.
>>> struct.pack('>Q', 12345678901234567890)
'\xabT\xa9\x8c\xeb\x1f\n\xd2'

Categories