Any way to improve this python function? - python

def __number():
# This line returns the number of the latest created object
# as a "Factura" object in format "n/year"
last = Factura.objects.filter(f_type__exact=False).latest('number')
# We convert it into a string and split it to get only the first number
spl = str(last).split('/')[0]
# Convert it into integer so we can do math
n = int(spl)
# Get the current year
y = date.today().strftime('%y')
# If none return 1/year
if n == None:
return str(1) + '/' + str(y)
# Else we increment the number in one.
else:
n = n + 1
return str(n) + '/' + str(y)
What it does: It autogenerates a number in the format '1/year' '2/year' etc. If the user introduces other number, p.e. 564/10 the function follows it and the next will be 565/10.
Even if the user introduces p.e. 34/10 after the entry with 564/10 the function will follow the largest number.
Did I do this right or there's a better way to do it?
Update:
def __number():
current_year = date.today().strftime('%y')
try:
facturas_emmited = Factura.objects.filter(f_type__exact=False)
latest_object = facturas_emmited.latest('number').__str__()
first_number = int(latest_object.split("/")[0]) + 1
except Factura.DoesNotExist:
first_number = 1
return '%s/%s' % (first_number, current_year)

This is really just the beginning, but I'd start by replacing some comments with self-documenting code.
def __number():
# "Factura" object in format "n/year"
latest_object = Factura.objects.filter(f_type__exact=False).latest('number')
# Better name can be available if you explain why the first number is important and what it means
# Do Factura objects not have a __repr__ or __str__ method that you must cast it?
first_number = int(str(latest_object).split('/')[0])
current_year = date.today().strftime('%y')
# Use "is None" rather than "== None"
if first_number is None:
return '1/%d' % current_year
# No else needed because of return above
# Why do we add 1 to first number? Comments should explain _why_, not how
return '%d/%d' % (first_number + 1, current_year)

Can last be None? If so it would be good to check for that:
# get last as before
if last:
n = int(str(last).split("/")[0]) + 1
else:
n = 1
# get y as before
return str(n) + "/" + str(y)
Another improvement here is that you only build the result string in one place.
I don't know what the Factura object is, but can you not get the value of n by calling some method on it? This would be better than converting it to a string, splitting it and taking the last part.

I solved similar problem some time ago by using object.id/year (where object/id is database id).
It guarantees that this will be unique, autoincremented (you don't need to do n = n + 1, which theoretically can lead to duplicate values in a db).
You can do this by overriding save method and only trick is that you need first to save an object (id is assigned) and then create id/year number and save again (maybe there is better way to do this than double save).
def save(self, force_insert = False, force_update = False):
super(Factura, self).save(force_insert, force_update)
current_year = date.today().strftime('%y')
self.identifier = '%s/%s'%(self.id, current_year)
super(Factura, self).save(force_insert, force_update)

Related

recursive way to find if a string is valid

hey so i am trying to write a code that that tells me if a string is valid or not .
a valid string is a string that contains an equal number of "(" and ")" and each "(" must be closed by a ")"
for example
'((()()))' this is a valid string . this isn't ')( '
this is what i wrote so far :
def is_valid_paren(s, cnt=0):
if s == "":
return True
if "(" in s:
cnt += 1
if ")" in s:
cnt -= 1
return is_valid_paren(s[1:])
it doesn't give the correct output for
"(.(a)"
yet for
"p(()r((0)))"
it does
why does it sometimes work ?
oh one more thing this is to be solved only by recursion ( without the use of loops anywhere )
While I don't understand why you want to solve this problem with recursion (it's very unnatural in this case), here is a recursive solution:
def is_valid(s):
def _is_valid(s, idx):
if idx == len(s): return 0
if s[idx] == '(': return _is_valid(s, idx + 1) + 1
if s[idx] == ')': return _is_valid(s, idx + 1) - 1
return _is_valid(s, idx + 1)
return _is_valid(s, 0) == 0
You can pass down a count of pending apertures (i.e. number of unclosed parentheses) and check if the count goes below 0 (too many closures) as you go and if it ends back at zero at the end:
def validPar(s,count=0):
if count<0 : return False # too many closing
if not s: return count == 0 # must balance pending apertures
return validPar(s[1:],count+(-1,1)[s[0]=="("]) # pass down count +/- 1
print(validPar('((()()))')) # True
Recursion
Iteration is likely to be the best method of solving this, but recursion also works. To attack this problem recursively, we need a system of checking the count of each and if at any stage that count falls below zero, the parentheses will be invalid because there are more closing brackets than opening ones. This is where the tough section comes into play: we need some method of not only returning the count, but whether or not the past ones were valid, so we will have to return and save using variables like return count, valid and count, valid = is_valid_parentheses(s[1:]). The next thing we need is some over-arching function which looks at the end results and says: "is the count equal to zero, and were the parentheses valid to begin with". From there, it needs to return the result.

Python classes and definitions

Here is my python code:
class Solution():
def isPalindrome(self):
return str(self.x) == str(self.x)[::-1]
s1 = Solution()
s1.x = 121
s1.isPalindrome()
It checks to see if the input is a palindrome. I want to create a new object that has the x value 121 and when I execute the isPalindrom function, I want it to return either a true or false boolean answer.
Currently when I run this program, nothing gets outputted. I am a bit lost as to where to go from here, would appreciate help.
Just print out the return value of isPalindrome(), because if you have a line with only a return value (this case being a boolean), the compiler won't know what to do with it.
class Solution():
def isPalindrome(self):
return str(self.x) == str(self.x)[::-1]
s1 = Solution()
s1.x = 121
print(s1.isPalindrome())
You're not telling the program to print anything. Try using print to make it reveal the answer.
Along with printing results we can also make class more pythonic.
class Solution:
def __init__(self):
self.input = None
def is_palindrome(self):
if isinstance(self.input, str):
return self.input == self.input[::-1]
print("Error: Expects str input")
return False # or leave blank to return None
s1 = Solution()
print(s1.is_palindrome())
s1.input = "121"
print(s1.is_palindrome())
output
Error: Expects str input
False
True
The main idea here is divide number. let's take number 122. First of all you need store it in a variable, in this case r_num. While loop is used and the last digit of the number is obtained by using the modulus operator %. The last digit 2 is then stored at the one’s place, second last at the ten’s place and so on. The last digit is then removed by truly dividing the number with 10, here we use //. And lastly the reverse of the number is then compared with the integer value stored in the temporary variable tmp if both are equal, the number is a palindrome, otherwise it is not a palindrome.
def ispalindrom(x):
r_num = 0
tmp = x
while tmp > 0:
r_num = (r_num * 10) + tmp % 10
tmp = tmp // 10
if x == r_num:
return True
return False

alternative for range and len functions

In the For loop below
def __str__(self):
output = " "
for i in range(len(self)):
output += (self.str_data[i] +" ")
return output
How may I rewrite this without using range & len or any other inbuilt function?
PS: str_data was used as instance variable of type list in init earlier.
If you're allowed to use join() then you could do:
def __str__(self):
return " ".join(self.str_data)
This is of course assuming len(self) would result in the same as len(self.str_data).
To result in exactly the same as your loop it would actually have to be:
def __str__(self):
if self.str_data:
return " " + " ".join(self.str_data) + " "
else:
return " "
New Answer: (no builtin functions)
def __str__(self):
output=""
for i in self.str_data:
output+=i+" "
return output
Old Answer :
asterisk and double asterisk can be used within list, tuple, set and dictionary, * will unpack list/tuple and ** will unpack dictionary.
my_array = [0,1,2,3,4,5]
print(*my_array) # unpacking list
Another method using join:
print(' '.join(map(str,my_array)))
Output
0 1 2 3 4 5
For the given question:
Since return keyword returns only one argument we can't unpack the list and we have to use function
def __str__(self):
return ' '.join(self.str_data)
You mean like this ?
class A(object):
def __init__(self, str_data):
self.str_data = str_data
def __str__(self):
return " ".join(str(s) for s in self)
def __iter__(self):
return iter(self.str_data)
if __name__ == '__main__':
print(A(["1", "2", "3", "4", "5"]))
import json
def __str__(self):
output = json.dumps(self.str_data)
output = output[1:-1].replace(',', ' ')
return output
For your consideration.
The previous answer is perfectly fine (if you omit builtins), but here's a closer version to what you did:
output = ""
array = [0,1,2,3,4,5]
for i in array:
output += "%s " % (i)
output = output[:-1]
return output
Please note that I change the way you set your output, as I think in order to get the result you want. Like you asked it does not use any builtin and it works just fine.
You could use highlevel functions like
" ".join(map(lambda x:x+" ",self.str_data))
map applies the function to add a space to each string in self.str_data and " ".join adds each of those to the starting string.
But of course these are built-in functions, it's just a really clean solution.
Alternitive to len() could be for loop - like:
var1 = 0
for var2 in list1:
var1 += 1

Python hw assignment String is replacing values, instead of adding them onto string consecutively

The code for this program is supposed to take the base 10 number to the specified base and return a string corresponding to the digits for the number represented in the specified base with a space between each of the digits. So far everything works perfectly but the problem I'm having currently is that each value is being replaced in the list instead of actually adding them to the list one by one. Here is the code I currently have. The prints are for me to keep track of the output for the time being. Thanks for any help
def convert(num, base):
results = ''
if num < base:
results.append(num)
print(results)
else:
results.append(num % base)
print(results)
return convert(num // base, base)
def main():
num, base = input("Enter a number and a base, seperated by a comma: ")
num, base = int(num), int(base)
convert(num, base)
if __name__ == '__main__':
main()
Current output:
9
6
7
Desired output:
9 6 7
results is a local variable and will be re-initialized to '' each time you call your convert function. Change the method signature to accept an optional results argument to prevent this from happening:
def convert(num, base, results=''): # Notice the optional argument
if num < base:
results = results + " " + str(num)
print(results)
else:
results = results + " " + str(num % base)
print(results)
return convert(num // base, base, results) # Notice how we pass the existing result variable
As a side note, strings (such as results) do not have an append method, either change it to string concatenation using +, use .format() statement, or change the type of the results variable to a list instead.

For loops-accumulation

This is from one of the question for my class, learning to use Forloops-accum-if. And I'm kind of stuck on the question.
I have to write a code which would use accumulation.
This is what I have so far.
def sequenceDigitsAndStars (dig):
st = "*"
for i in range(len(dig)):
if(st[i].isdigit()):
dig = dig + st[i]
return st
I'm getting 'Memory Error' with this code.
The question says that, if I type in a number (i.e. 5) for the value dig, it should return 0*1*2*3*4*5*. I'm having trouble on it, I don't know how I can make it so that it would put the number, starting from the 0, alternating with star (0*). (how can I make it alternate the pattern 0*, when I use a number as input value?)
I guess this should fix your problem.
def sequenceDigitsAndStars (dig):
st = ""
for i in range(dig + 1):
st += str(i) + '*'
return st
How about...
def sequenceDigitsAndStars (dig):
return '*'.join(map(str, range(dig))
This will get the range of 0-dig, convert it to an array of strings and then combine the elements with *s
If you want a trailing *, you can do
def sequenceDigitsAndStars (dig):
return '*'.join(map(str, range(dig)) + '*'
Are you trying to do this?
print "*".join(str(i) for i in xrange(dig+1))
Since it's homework you can use this:
def sequenceDigitsAndStars (dig):
string = ""
for i in range(dig):
string += str(i)+"*"
string += str(i+1)
return string
which has a couple of performance issues.

Categories