I just started learning about classes and am trying to build a calculator
that tells people how much they need to tip the waiter as a small project
but instead of entering myself the information i want that a user will do it himself so it will suit hes needs.
now i think i built it right, the computer accepts the inputs but when i try to call
the return_answer() function i get an error that says:
AttributeError: tip_calculator object has no attribute 'return_answer'
can someone please explain to me what i am doing wrong and how to fix it?
thanks in advance :)
class tip_calculator:
def __init__(self,bill,amount_of_diners,precent):
self.bill = bill
self.amount_of_diners = amount_of_diners
self.precent = precent
def return_answer(self):
print("The amount you need to pay is: ", precent * bill / amount_of_diners ** bill * 1000)
calc = tip_calculator(int(input("What was the bill?: ")),
int(input("With how many people did you eat?: ")),
int(input("what '%' do you want to give the waiter?: ")))
calc.return_answer()
The problem is with the indentation:
class tip_calculator:
def __init__(self, bill, amount_of_diners, precent):
self.bill = bill
self.amount_of_diners = amount_of_diners
self.precent = precent
def return_answer(self):
print(
"The amount you need to pay is: ",
self.precent * self.bill / self.amount_of_diners ** self.bill * 1000,
)
calc = tip_calculator(
int(input("What was the bill?: ")),
int(input("With how many people did you eat?: ")),
int(input("what '%' do you want to give the waiter?: ")),
)
calc.return_answer()
Your return_answer method was indented wrongly inside the initialiser (__init__). Also, in your return_answer, when you want to access class members like the fields bill, amount_of_diners and percent you need to do it with self because they are members of your class and not local variables to your method.
Related
I would like to reduce the amount of similar queries. Here are my models:
class Skill(models.Model):
name = models.TextField()
class Employee(models.Model):
firstname = models.TextField()
skills = models.ManyToManyField(Skill, through='SkillStatus')
def skills_percentage(self):
completed = 0
total = 0
for skill in self.skills.all().prefetch_related("skillstatus_set__employee"):
for item in skill.skillstatus_set.all():
if item.employee.firstname == self.firstname:
total += 1
if item.status:
completed += 1
try:
percentage = round((completed / total * 100), 2)
except ZeroDivisionError:
percentage = 0.0
return f"{percentage} %"
class SkillStatus(models.Model):
employee = models.ForeignKey(Employee, on_delete=models.CASCADE)
skill = models.ForeignKey(Skill, on_delete=models.CASCADE)
status = models.BooleanField(default=False)
My main problen is related to method skills_percentage, I make too many queries while calculating mentioned value. I have already improved situation a little bit with prefetch_related, but there are still extra queries in Django Debug Toolbar. What else can be done here?
I have tried to play with different combinations of select_related and prefetch_related. I thought about other options to calculate skills_percentage but they also required to many queries...
Thanks in advance.
You can try like this:
from django.db.models import Count, When, Case, Cast, FloatField
employees = Employee.objects.annotate(
total=Count('skills',distinct=True),
completed = Count('skills', filter=Q(skillstatus__status=True),distinct=True)
).annotate(
percentage= Case(
When(total=0, then=0.0),
default=(Cast(
F('completed')*100 / F('total'),
output_field=FloatField()
)
)
)
)
# usage
for employee in employees:
print(employee.percentage)
# or
employees.values('firstname', 'percentage')
Here I am counting skills twice, once without any condition for total and second time with a filter condition. Then I am annotating the division of completed/total value with the original queryset by casting it as FloatField.
You can use aggregation functions provided by Django's ORM. This can help to reduce the number of queries required to calculate the completed and total skills counts for an employee.
Using F() expressions and annotate() allows us to perform the calculation in a single query, without the need for a separate loop over the related Skill objects.
from django.db.models import Count, F
class Employee(models.Model):
...
def skills_percentage(self):
counts = self.skills.annotate(
completed_count=Count('skillstatus', filter=Q(skillstatus__status=True, skillstatus__employee=self)),
total_count=Count('skillstatus', filter=Q(skillstatus__employee=self)),
).aggregate(
completed_count_sum=Sum('completed_count'),
total_count_sum=Sum('total_count'),
)
completed = counts['completed_count_sum'] or 0
total = counts['total_count_sum'] or 0
try:
percentage = round((completed / total * 100), 2)
except ZeroDivisionError:
percentage = 0.0
return f"{percentage} %"
I have read this article that explains how to set the level of a floor without moving it. The article refers to the Building Coder where the BuiltInParameter.LEVEL_PARAM is used. However this method no longer works due to updates in the API. I am able to find the new ForgeTypeId of the parameter, but I am told that the LevelId is a Read-Only parameter when I try to run my code. How do I change the level of a floor? In the GUI it's easy, how can this be so hard in the API and so easy in the GUI?
Doing this in RevitPythonShell, my code is the following:
typeid = s0.LookupParameter("Level").GetTypeId()
floorid = ElementId(5873761)
with Transaction(revit.doc,"change level") as t:
p = s0.GetParameter(typeid)
t.Start()
p.Set(floorid)
t.Commit()
Grateful for any help!
You shouldnt have to make a new floor - you can change the level of a floor just like any other Parameter:
levels = list(FilteredElementCollector(doc).OfClass(Level))
newLevelName = 'Level 2'
newLevel = [i for i in levels if i.Name == newLevelName][0]
floor = s0 # your selected floor here
levelParam = floor.LookupParameter('Level')
t = Transaction(doc, 'Changing Floor Level to '+newLevelName)
t.Start()
try:
levelParam.Set(newLevel.Id)
print 'changed level of floor to',level.Name
except Exception as e:
print '!!!',e
t.Commit()
Interestingly, the UserModifiable value of the levelParam is False - turns out users can still modify it though!
I am very new to programming and im trying to call a method from one class to another class. I keep getting the error, "name 'quad' is not defined.
class problem(object):
def ask(problem):
problem = input("What type of problem are you trying to solve?
Quadratic, ").capitalize() #add more
if problem == "Quadratic":
quad.equation()
class quadratic(object):
def equation():
a = float(input('Enter a: '))
b = float(input('Enter b: '))
c = float(input('Enter c: '))
e = problem()
e.ask()
quad = quadratic()
The code isn't completely done as I am doing things one at a time, so anything helps.
I guess my question is, is there an easier way to call the method to the other class? Or am I just doing something wrong?
EDIT:
I have since fixed my code. Thanks for helping.
Here I created a module.
class Employee:
def __init__(self):
self.name = input("Enter your name: ")
self.account_number = int(input("Enter your account number: "))
def withdraw(self): # it receives values from for
if withdraw1 > current_balance:
print ("You have entered a wrong number: ")
else:
print ("The current balance is: ", current_balance - withdraw1)
import TASK2 # I am importing the module I created
c = TASK2.Employee()
def for(self):
c.withdraw1 = int(input("enter number: "))
c.current_balance = int(input("Enter the current balance: "))
d = method(c.withdraw) # here I am trying to pass the values to withdraw
print (d)
The problem I get is that although it asks for the values instead of giving me an answer it gives me None.
Here's my take on your code.
# TASK2.py
class Employee:
def __init__(self):
self.name = input("Enter your name: ")
self.account_number = int(input("Enter your account number: "))
# make sure you initialise your member variables!
self.withdraw_val = 0 # withdraw1 is ambiguous, so I use withdraw_val instead
self.current_balance = 0
# receives values from for ### no it doesn't, right now, it GIVES values TO your "for" function
def withdraw(self):
if self.withdraw_val > self.current_balance: # remember to use "self." to
# access members within the class
print ("You have entered a wrong number: ")
else:
# again, remember "self."
print ("The current balance is: ", self.current_balance - self.withdraw_val)
# TASK2sub.py
import TASK2
c = TASK2.Employee()
def for_employee(employee): # (1) don't use "self" outside a class
# it's contextually unconventional
# (2) "for" is a keyword in Python, don't use it for naming
# variables/functions, it'll mess things up
employee.withdraw_val = int(input("Enter value to withdraw: "))
employee.current_balance = int(input("Enter the current balance: "))
return employee.withdraw_val # not entirely sure what you want to return
# but you should definitely return something
# if you're going to assign it to some variable
d = for_employee(c.withdraw()) # "for_employee" function needs a return statement
# ".withdraw()" method should also require a return statement
print(d)
Note: I'll be referring to your original for function as for_employee from now on. Also note that I'm still hazy about what you're trying to accomplish and that there is most probably a more suitable name for it.
Since your original for_employee function didn't return anything, it returns None by default. (This explains the output you saw.)
I think you're misunderstanding how functions work in general. For example,
d = for_employee(c.withdraw())
print(d)
Your comment for the .withdraw() method is inaccurate.
"it receives values from for"
More accurately, c.withdraw() will first be computed, then whatever it returns is passed into the for_employee function as a parameter. Instead of "receiving values from", the withdraw method "gives values to" the for_employee function.
Something more reasonable would be
c.withdraw() # on a line by itself, since it doesn't return anything
d = for_employee(c) # pass the entire object, since you'll be using self.withdraw_val and whatnot
print(d)
Another issue is with conventional naming. This is what I get from the IDLE (with Python 3.7) when defining a function named for
>>> def for(a): return a
SyntaxError: invalid syntax
Again, for is a keyword in Python, don't use it for naming your variables, functions, or classes.
With self, it's less severe (but I could see that it's confusing you). self is more of a convention used in class methods. But for_employee isn't a class method. So conventionally speaking, the parameter shouldn't be named self.
(I find the code spaghetti-ish, it might benefit if you refactor the code by moving the for_employee method into the class itself. Then it would completely make sense to use self.)
I've been googling for 30 minutes but I can't find a solution to my problem.
Please help!
import math
amount = input("Enter amount of medicine left: ")
dose = input("Enter dose per day: ")
def convertString(str):
try:
returnValue = int(str)
except ValueError:
returnValue = float(str)
return returnValue
def count_days(amount, dose):
last_days = amount / dose
return last_days
print("Your medicine will run out in ",last_days," days.")
I get this error:
NameError: name 'last_days' is not defined
Why isn't this working?
The variable last_days is local within the count_days function. That means that as soon as the function ends, the variable is no longer available. That is however not a problem as the function actually returns that value, making it available for others.
So when you do my_var = count_days(amount, dose), then the function will execute, and return its last_days result which is then stored in my_var.
last_days = count_days(amount, dose)
print("Your medicine will run out in ", last_days, " days.")
last_days is defined just within count_days. Initialise the variable on top of the file to change the visibility