I have a program which holds many functions and I have no idea how to link them together :/
When I'm working on the python's shell everything is working fine because everything is in one definition but when I divide them into different definitions it doesn't work.
I have a different Metals each one of them has a weight and value and a name, etc. I want to make a class for those metals and then sort them in a descending order based on there valuePerBar once and valuePerWeight once
those are my metals :
Gold, 1 bar, 5 weight per bar, 750 value per bar
Silver, 1 bar, 1 weight per bar, 400 value per bar
Rhodium, 1 bar, 4 weight per bar, 500 value per bar
Platinum, 1 bar, 6 weight per bar, 1000 value per bar
and this is the my class:
class Metal(rit_object):
"""
Represents a single metal type, composed of:
:slot name (str): The name of the metal
:slot totalBars (int): The total number of bars
:slot weightPerBar (int): The weight of a single bar
:slot valuePerBar (int): The value of a single bar
:slot valuePerWeight (float): The value per weight of the metal
:slot barsTaken (int): The number of bars added to the satchel
"""
__slots__ = ( 'name' , 'totalBars' , 'weightPerBar', 'valuePerBar', 'valuePerWeight', 'barsTaken' )
_types = ( str , int , int, int, float, int )
Note: rit_object is a private class which makes my work easier and all it does is to put types for each parameter ! ex: name --> str and totalBars --> int ..etc
the way I am doing the sorting in the shell is :
class Metal(rit_object):
"""
Represents a single metal type, composed of:
:slot name (str): The name of the metal
:slot totalBars (int): The total number of bars
:slot weightPerBar (int): The weight of a single bar
:slot valuePerBar (int): The value of a single bar
:slot valuePerWeight (float): The value per weight of the metal
:slot barsTaken (int): The number of bars added to the satchel
"""
__slots__ = ( 'name' , 'totalBars' , 'weightPerBar', 'valuePerBar', 'valuePerWeight', 'barsTaken' )
_types = ( str , int , int, int, float, int )
platinum = Metal("platinum", 1, 6, 1000, 166.666667, 0 )
gold = Metal("gold", 1, 5, 750, 150.0, 0 )
rhodium = Metal("rhodium", 1, 4, 500, 125.0, 0 )
silver = Metal("silver", 1, 1, 4, 400.0, 0 )
Metals = [
Metal("platinum", 1, 6, 1000, 166.666667, 0 ),
Metal("gold", 1, 5, 750, 150.0, 0 ) ,
Metal("rhodium", 1, 4, 500, 125.0, 0 ),
Metal("silver", 1, 1, 4, 400.0, 0 )
]
def getKey(Metal):
return name.valuePerBar
sorted(customlist, key=getKey, reverse=True)
and everything works perfect, now I want to make a program with multiple definition to make it well-organiezed but my problems is I DO NOT KNOW HOW TO LINK THE FUNCTIONS !!
this is my program:
"""
Author: Sean Strout (sps#cs.rit.edu)
Author: <<< YOUR NAME HERE >>>
This class represents the types of metal bars that Greedo can
store in his satchel. Each type of bar is a separate Metal
object. This module also has routines that work with metals,
e.g. creation, reading from a file, and sorting based on
various criteria.
Language: Python 3
"""
from rit_object import * # rit_object class
class Metal(rit_object):
"""
Represents a single metal type, composed of:
:slot name (str): The name of the metal
:slot totalBars (int): The total number of bars
:slot weightPerBar (int): The weight of a single bar
:slot valuePerBar (int): The value of a single bar
:slot valuePerWeight (float): The value per weight of the metal
:slot barsTaken (int): The number of bars added to the satchel
"""
__slots__ = ( 'name' , 'totalBars' , 'weightPerBar', 'valuePerBar', 'valuePerWeight', 'barsTaken' )
_types = ( str , int , int, int, float, int )
def createMetal(name, totalBars, weightPerBar, valuePerBar):
"""
Create and return a new Metal object.
:param name (str): The name of the metal
:param totalBars (int): The total number of bars
:param weightPerBar (int): The weight of a single bar
:param valuePerBar (int): The value of a single bar
:return: A newly initialized Metal object
:rtype: Metal
"""
platinum = Metal("platinum", 1, 6, 1000, 166.666667, 0 )
gold = Metal("gold", 1, 5, 750, 150.0, 0 )
rhodium = Metal("rhodium", 1, 4, 500, 125.0, 0 )
silver = Metal("silver", 1, 1, 4, 400.0, 0 )
def readMetals(Metals):
"""
Read the metals from a file whose format is:
metalName totalBars weightPerBar valuePerBar
:param fileName (str): The name of the file
:return: A list of Metal objects
:rtype: list
"""
Metals = [
Metal("platinum", 1, 6, 1000, 166.666667, 0 ),
Metal("gold", 1, 5, 750, 150.0, 0 ) ,
Metal("rhodium", 1, 4, 500, 125.0, 0 ),
Metal("silver", 1, 1, 4, 400.0, 0 )
]
print (name.valuePerBar)
def getKey(Metal):
return name.valuePerBar
def sortMetalsByValuePerBar():
"""
Sort the metals by value per bar using insertion sort. The list of
metals is modified in place to be ordered by value per bar.
:param metals (list of Metal): The list of metals
:return: None
:rtype: NoneType
"""
return sorted(Metals, key=getKey, reverse=True)
def getKey2(Metal):
return name.weightPerBar
def sortMetalsByValuePerWeight(metals):
"""
Sort the metals by value per weight using insertion sort. The list of
metals is modified in place to be ordered by value per weight.
:param metals (list of Metal): The list of metals
:return: None
:rtype: NoneType"""
return sorted(Metals, key=getKey, reverse=True)
def printMetals(metals):
"""
Display the metals to standard output.
:param metals (list of Metal): The list of metals
:return: None
:rtype: NoneType
"""
if Q == a:
getKey(Metal)
sortMetalsByValuePerBar()
else:
getKey2(Metal)
sortMetalsByValuePerWeight(metals)
Q = input ("Enter 'a' for descending order or 'b' for ascending order")
Metal(rit_object)
createMetal(name, totalBars, weightPerBar, valuePerBar)
readMetals(Metals)
printMetals(metals)
Related
Here's the given for the problem I'm trying to solve:
*python Write a function that given a mapping from material to boiling points, and a boiling point temperature, will return :
the material corresponding to the boiling, if it is within 5% difference
'UNKNOWN' otherwise
Function signature should look like : boiling_material(boiling_map, boiling_point)
An example boiling_map : Given input :
' Butane' : _0.5,
'copper' : 1187,
'Gold' : 2660,
'Mercury' : 357,
'Methane' : _161.7,
'Nonane': 150.8,
'Silver' : 2197,
'water': 100}*
A sample run of the program with the above input:
Enter boiling point > 359
Closest material : Mercury
Enter boiling point > 2000
Closest material : Unknown
I attempted to solve it and it's returning the right output when the inputs are positive numbers but it's only returning 'unknown' when the inputs are negative.
Example:
Enter boiling point > -0.475
Expected Closest material : Butane
actual output: Unknown
This is my current code.
boiling_map=dict()
boiling_map['Butane']=-0.5
boiling_map['Copper']=1187
boiling_map['Gold']=2660
boiling_map['Mercury']=357
boiling_map['Methane']=-161.7
boiling_map['Nonane']=150.8
boiling_map['water']=100
def boiling_material(boiling_map,boiling_point):
closest='unknown'
for material in boiling_map:
if abs(boiling_map[material]) >=abs(boiling_point-(boiling_point*5//100)) and abs(boiling_map[material]-boiling_point)<=abs(boiling_point+(boiling_point*5//100)) :
closest=material
return closest
print(boiling_material(boiling_map,359))
print(boiling_material(boiling_map,-0.475))
print(boiling_material(boiling_map,0))
You could do something like this. The below is using Python 3.10.4, anything under 3.10 won't support using the match keyword. Note that for some test temperatures you get known results on some temperature scales, but not on others. Note that the results here are either "unknown" or a list of candidates.
Added a material and some test temperatures to make things interesting.
from enum import Enum
import sys;
class TemperatureScale(Enum):
CELCIUS = 1
KELVIN = 2
FAHRENHEIT = 3
# boiling points are in kelvin
materials = { "Butane": 272.65
, "Copper": 1460.15
, "Gold": 2933.15
, "Mercury": 630.15
, "Methane": 111.45
, "Nonane": 423.95
, "Water": 373.15
, "Propyl Alcohol": 370.65
}
def kelvin_to_scale(temp, scale):
match scale:
case TemperatureScale.KELVIN:
return temp
case TemperatureScale.CELCIUS:
return temp - 273.15
case TemperatureScale.FAHRENHEIT:
return 1.8 * (temp - 273.15) + 32
case _:
raise Exception('unknown scale')
def scale_to_kelvin(temp, scale):
match scale:
case TemperatureScale.KELVIN:
return temp
case TemperatureScale.CELCIUS:
return temp + 273.15
case TemperatureScale.FAHRENHEIT:
return (temp*5//9)+459.67
case _:
raise Exception('unknown scale')
def test_material(boiling_point, scale = TemperatureScale.KELVIN):
print(f'Test temperature: {boiling_point} {scale}')
candidates = []
for material in materials:
bp = kelvin_to_scale(materials[material], scale)
margin = abs(boiling_point)*5//100
print(f'Testing {material} using {bp} +/- {margin}:\t{bp - margin} <= {boiling_point} < {bp + margin}')
if (bp - margin <= boiling_point) and (bp + margin > boiling_point):
candidates += [ material ]
return candidates
# test values are in celcius
test_values = [ 2801, 359, -0.475, 0, 95 ]
for test_value in test_values:
kelvin_value = scale_to_kelvin(test_value, TemperatureScale.CELCIUS)
for scale in [ TemperatureScale.CELCIUS, TemperatureScale.KELVIN, TemperatureScale.FAHRENHEIT ]:
print(test_material(kelvin_to_scale(kelvin_value, scale), scale) or 'unknown')
Here is the code I have so far. But in line 104 I have one of my walkers, Mi-Ma, walking south two times compared to the other directions each time it is called, but when I coded this I misread the description of the project. Instead of always walking south two times it should instead walk twice as often as any other directions. What can I do to make her walk twice as often and not just two times each times south is called?
import turtle as ts
from math import sqrt
from random import choices, seed
from statistics import mean, stdev
from sys import argv
import tempfile
import subprocess
def save_to_image(dest='random_walk.png'):
'''Saves the turtle canvas to dest. Do not modify this function.'''
with tempfile.NamedTemporaryFile(prefix='random_walk',
suffix='.eps') as tmp:
ts.getcanvas().postscript(file=tmp.name)
subprocess.run(['gs',
'-dSAFER',
'-o',
dest,
'-r200',
'-dEPSCrop',
'-sDEVICE=png16m',
tmp.name],
stdout=subprocess.DEVNULL)
def get_args() -> tuple:
walk_lengths = list(map(int, argv[1].split(',')))
n = int(argv[2])
walker_name = argv[3]
return (walk_lengths, n, walker_name)
class RandomWalker:
'''
Class to represent the random walker.
'''
directions = dict(
North=[0, +1],
South=[0, -1],
East=[+1, 0],
West=[-1, 0],
)
def __init__(self, name, N, S, E, W, color, shape) -> None:
'''
Initialisation function
Parameters:
name (str) : name of random walker
N,S,E,W (int) : weights for moving in the specified direction
color (str) : color for plot representation
shape (str) : shape for plot representation
'''
self.name = name
self.N = N
self.S = S
self.E = E
self.W = W
self.color = color
self.shape = shape
def take_walk(self, walk_length, n) -> list:
'''
Takes a random walk
Parameters:
walk_length (int): Length of random walk
Returns:
List of final destinations
'''
walker_weight = (self.N, self.S, self.E, self.W)
directions = list(RandomWalker.directions.keys())
final_postions = []
for _ in range(n):
position = [0, 0]
for step in range(walk_length):
random_direction = choices(
directions, weights=walker_weight, k=1)[0]
delta_direction = RandomWalker.directions[random_direction]
position[0] += delta_direction[0]
position[1] += delta_direction[1]
final_postions.append(position)
final_distances = list(map(lambda pos: sqrt(
pos[0]**2 + pos[1]**2), final_postions))
Mean = mean(final_distances)
CV = stdev(final_distances)/Mean
Max = max(final_distances)
Min = min(final_distances)
print(f'{self.name} random walk of {walk_length} steps\nMean = {Mean:.1f} CV = {CV:.1f}\nMax = {Max:.1f} Min = {Min:.1f}')
return final_postions
#staticmethod
def get_walkers(walker_name: str) -> list:
'''
Returns the instances of the RandomWalkers
Parameters:
walker_name: mentions the random_walker; 'all' for all walkers
Returns:
A list containing instance of random walkers.
'''
walkers = {
'Pa': RandomWalker('Pa', 1, 1, 1, 1, 'black', 'circle'),
'Mi-Ma': RandomWalker('Mi-Ma', 1, 2, 1, 1, 'green', 'square'),
'Reg': RandomWalker('Reg', 0, 0, 1, 1, 'red', 'triangle'),
}
if walker_name in walkers:
return [(walkers[walker_name])]
elif walker_name == 'all':
return list(walkers.values())
else:
raise Exception(f'Inavlid walker_name, "{walker_name}"')
def simulate(walk_lengths: list, n: int, walker_name: str) -> list:
'''
Simulating random walks
Parameters:
walk_lengths (list) : length of random walks to simulate
n (int) : number of random walks to simulate
walker_name (str) : mentions the random_walker; 'all' for all walkers
Returns:
Data for plotting (list) of form [shape, color, final_postions, walk_length]
'''
walkers = RandomWalker.get_walkers(walker_name)
data_for_plotting = []
for walker in walkers:
for walk_length in walk_lengths:
final_postion = walker.take_walk(walk_length, n)
data_for_plotting.append(
[walker.shape, walker.color, final_postion, walk_length])
return data_for_plotting
def plot(data_for_plotting: list = []) -> None:
'''
Function to plot
Parameters:
data_for_plotting (list) of form [shape, color, final_postions, walk_length]
'''
ts.screensize(300, 400)
turtle = ts.Turtle()
turtle.speed(0)
turtle.shapesize(0.5, 0.5)
turtle.up()
for shape, color, positions, walk_length in data_for_plotting:
if walk_length > 100:
continue
turtle.shape(shape)
turtle.color(color)
for position in positions:
x, y = position
scale = 5
turtle.goto(x*scale, y*scale)
turtle.stamp()
save_to_image()
def main():
plotting_data = simulate(*get_args())
plot(plotting_data)
if __name__ == "__main__":
main()
Here is the part of the project description that asks for this step. It is in the last paragraph.
"Farmer John has an old grandparent (Pa) that likes to wander off randomly when working in the barn. Pa starts from the barn and every second takes one step in a random direction North, South, East or West. What is Pa’s expected distance away from the barn after 1000 steps? If Pa takes many steps, will Pa be likely to move ever further from the origin, or be more likely to wander back to the origin over and over, and end up not far from where he started? Let’s write a simulation to find out.
This particular barn is in the center of a large grassy field. One day Pa starts to wander off, and notices that the grass has been mysteriously cut (by John) to resemble graph paper. Notice that after one step Pa is always exactly one unit away from the start. Let’s assume that Pa wanders eastward from the initial location on the first step. How far away might Pa be from the initial location after the second step? John sees that with a probability of 0.25 Pa will be 0 units away, with a probability of 0.25 Pa will be 2 units away, and with a probability of 0.5 Pa will be √2 units away. So, on average Pa will be further away after two steps than after one step. What about the third step? If the second step is to the north or south, the third step will bring the farmer closer to origin half the time and further half the time. If the second step is to the west (back to the start), the third step will be away from the origin. If the second step is to the east, the third step will be closer to the origin a quarter of the time, and further away three quarters of the time.
It seems like the more steps Pa takes, the greater the expected distance from the origin. We could continue this exhaustive enumeration of possibilities and perhaps develop a pretty good intuition about how this distance grows with respect to the number of steps. However, it is getting pretty tedious, so it seems like a better idea to write a program to do it for us.
However, there are a couple more twists to the situation. Pa’s wife Mi-Ma, another grandparent of John’s, also likes to wander away randomly, but riding an old mule. The mule goes South twice as often as any other direction. Lastly, John’s favorite hog Reg has an odd habit of wandering off too, but only randomly going east or west at each step, never north or south. People think he’s a sun-follower, but nobody’s really sure. John figures your Python program ought to model these two as well, while you’re at it."
Using the random.choices function (as you do), this can be achieved using the weights parameter:
from random import choices
random_direction = choices(population=['N', 'E', 'S', 'W'],
weights=[1, 1, 2, 1],
k=1)[0]
You could make a single call of choices out of the for loop BTW, since this is a choice with replacement. Something like random_direction=choices(..., k=n).
Alternatively, you could use random.choice without s repeating the south direction (inside the for loop here since this only picks one direction at a time):
from random import choice
random_direction = choice(seq=['N', 'E', 'S', 'S', 'W'])
I am looking to return the delivery charge in pence (p) for each order. We have a flat £50 (5000p) fee plus £20 (2000p) for each 100lb (additional weight ignored below 100lbs).
An ordered item has 4 components: name, quantity, unit price (in pence), unit weight (in pounds)
ORDER_SAMPLE_1 = {("lamp", 2, 2399, 2), ("chair", 4, 3199, 10), ("table", 1, 5599, 85)}
ORDER_SAMPLE_2 = {("sofa", 1, 18399, 140), ("bookshelf", 2, 4799, 40)}
def delivery_charges(order):
E.g., delivery_charges({("desk", 1, 11999, 160)}) is 7000 (pence)
E.g., delivery_charges({("desk", 2, 11999, 160)}) is 11000 (pence)
E.g., delivery_charges({("lamp", 1, 2399, 2)}) is 5000 (pence)
E.g., delivery_charges({("lamp", 50, 2399, 2)}) is 7000 (pence)
Is a for loop or elif the best way to approach?
I am assuming that 5000p is the base fee for the whole order and not for each item in it. So here is one way you can do this,
You can iterate over all the items in an order.
For each item calculate its total weight then calculate the extra_charges by dividing the total weight by 100 and then multiplying it with 2000.
Add this to the total charges. Return the total charges after the loop finishes iterating.
def delivery_charges(order):
total_charges = 5000
for item in order:
total_item_weight = item[1] * item[3]
extra_charges = (total_item_weight // 100) * (2000)
total_charges += extra_charges
return total_charges
I would like to print the contents of a tuple with a print statement like this:
print("First value: %i, Next four values: %i %i %i %i, Next three values: %i %i %i\n" % (myTuple[0], myTuple[1], myTuple[2], myTuple[3], myTuple[4], myTuple[5], myTuple[6], myTuple[7]));
Now as you can see in the example above the elements in a tuple are going to be output in order, but the format is such that you can't easily use a loop. Is there a way to just specify the whole tuple/list/set (or a given range) in print statement without specifying each element separately, something like this?
print("First value: %i, Next four values: %i %i %i %i, Next three values: %i %i %i\n" % (myTuple[:7));
You can use string.format:
a = (1,2,3,4)
print("first {}, second {}, thrid {}, forth {}".format(*a))
#first 1, second 2, thrid 3, forth 4
If using str.format(), which you probably should be, you can use the iterable-unpacking operator *:
myTuple = tuple(range(10))
print("There are 7 values {} {} {} {} {} {} {}".format(*myTuple[:7]))
# There are 7 values 0 1 2 3 4 5 6
If using old-style format strings, you can just give my_tuple[:7] as the argument, since it expects a tuple or other iterable after the %:
print("There are 7 values %i %i %i %i %i %i %i" % myTuple[:7])
# There are 7 values 0 1 2 3 4 5 6
Here is a one way you could iterate, group, and print through your tuple. This would allow you to print sequences of numbers beyond 1, 4, then 3.
from collections.abc import MutableSequence
def group(values, limits):
'''
Group sequence of values into bins of size limit
Parameters
----------
values : Sequence
limits : List or tuple of limits
Sizes of each group
Notes
-----
First group is of size 1
Returns
-------
Tuple of grouped values
'''
def _rm_select(limit):
'''
Slice and delete value from sequence
'''
yield values[:limit]
del values[:limit]
def _group(values, limits):
'''
See `group`
'''
# Yield 0th value with slice fo 1
yield from _rm_select(1)
for limit in limits:
yield from _rm_select(limit)
# Check if sequence is mutable
# . If not, convert to `list`, in order for `del` to function
if not isinstance(values, MutableSequence):
values = list(values)
return tuple(_group(values, limits))
def printer(groups, descriptions):
'''
Print each group with a prefixing description.
'''
def _stringify(groups, descriptions):
'''
See `printer`
'''
for description, group in zip(descriptions, groups):
group = ' '.join(str(i) for i in group)
yield f'{description} {group}'
string = tuple(_stringify(groups, descriptions))
print(*string, sep=', ', end='\n\n')
values = (1, 2, 3, 4, 5, 6, 7, 8)
descriptions = ('First value', 'Next four values', 'Next three values')
groups = group(values, limits=(4, 3))
printer(groups, descriptions)
A format string is a good tool if you do not mind an extra pair of parentheses:
myTuple = ((1,2),(3,4),(5,6),(7,8),(9,10))
print(f"First: {myTuple[0]}, next: {myTuple[1:]}")
# First: (1, 2), next: ((3, 4), (5, 6), (7, 8), (9, 10))
This insertion sort mis sorting all elements but the last. It's very weird bc I have an identical function that sorts ALL elements by a different attribute. I tried copying, pasting, and altering the working func, but that seemed futile.
for i in range(1, len(metals)):
index = i
while index != 0 and metals[index].weightPerBar > metals[index - 1].weightPerBar:
metals[index], metals[index - 1] = metals[index - 1], metals[index]
index -= 1
Thanks
Heres the rest of module:
class Metal(struct):
"""
Represents a single metal type, composed of:
:slot name (str): The name of the metal
:slot totalBars (int): The total number of bars
:slot weightPerBar (int): The weight of a single bar
:slot valuePerBar (int): The value of a single bar
:slot valuePerWeight (float): The value per weight of the metal
:slot barsTaken (int): The number of bars added to the satchel
"""
_slots = ((str, "name"), (int, "totalBars"), (int, "weightPerBar"), (int, "valuePerBar"), (float, "valuePerWeight"), (int, "barsTaken"))
pass
def createMetal(name, totalBars, weightPerBar, valuePerBar):
"""
Create and return a new Metal object.
:param name (str): The name of the metal
:param totalBars (int): The total number of bars
:param weightPerBar (int): The weight of a single bar
:param valuePerBar (int): The value of a single bar
:return: A newly initialized Metal object
:rtype: Metal
"""
new_metal = Metal(name, totalBars, weightPerBar, valuePerBar)
return new_metal
pass
def readMetals(fileName):
"""
Read the metals from a file whose format is:
metalName totalBars weightPerBar valuePerBar
:param fileName (str): The name of the file
:return: A list of Metal objects
:rtype: list
"""
metal_list = []
file = open(fileName)
for line in file:
line = line.split()
weight_per_bar = float(line[3])/float(line[2]) # creating derived value
new_metal = Metal(line[0], int(line[1]), int(line[2]), int(line[3]), weight_per_bar, 0)
metal_list += [new_metal]
return metal_list
pass
def sortMetalsByValuePerBar(metals):
"""
Sort the metals by value per bar using insertion sort. The list of
metals is modified in place to be ordered by value per bar.
:param metals (list of Metal): The list of metals
:return: None
:rtype: NoneType
"""
for i in range(1, len(metals)):
index = i
while index != 0 and metals[index].valuePerBar > metals[index - 1].valuePerBar:
metals[index], metals[index - 1] = metals[index - 1], metals[index]
index -= 1
pass
def sortMetalsByValuePerWeight(metals):
"""
Sort the metals by value per weight using insertion sort. The list of
metals is modified in place to be ordered by value per weight.
:param metals (list of Metal): The list of metals
:return: None
:rtype: NoneType
"""
for i in range(1, len(metals)):
index = i
while index != 0 and metals[index].weightPerBar > metals[index - 1].weightPerBar:
metals[index], metals[index - 1] = metals[index - 1], metals[index]
index -= 1
pass
It should work if .weightPerBar are all of the same type and are numeric (not strings or other objects). If weight is a string, it could have the situation where "2", "6", "4", "10" sorts as "6", "4", "2", "10". Instead of 10, 6, 4, 2 as desired.
Your code works fine on my machine, but why are you implementing a sorting algorithm on your own? You can just use:
metals.sort(key=lambda metal: metal.weightPerBar, reverse=True)